How to Create a Chat Room in Java

Source

When you start to think about how to create your own personal chat room, probably the first thing that you think of is related to network I/O, sockets, etc. and maybe you begin to feel discouraged. Don't be! There is a great solution that makes implementing an application like this easy: JGroups.

What is JGroups? It is a reliable multicast system written in the Java language. From Wikipedia: "Multicast is the delivery of a message or information to a group of destination computers simultaneously in a single transmission from the source".

In simple words, we can imagine our chat room as a group. Each user represents a node in this group, and every time a message is sent by a node, it is delivered to all the other nodes in the group.

The chat that we are going to create
The chat that we are going to create

Basic Concepts of JGroups

In our chat room we can forget sockets and other low level network elements, but we have to learn some basic concepts about JGroups before continuing.

As already stated, we can see our chat room as a group. To join a group and to send/receive messages, we need channels. Every channel has its own address and it is connected to a group using the group name. So a group can have many channels associated to it (in our case, one channel per user).

Channels can always retrieve a list of the member addresses in the group (basically a user can see who is in the chat room). We can even be notified when a user leaves/joins the chat room.

It's Code Time!

import org.jgroups.JChannel;
import org.jgroups.Message;
import org.jgroups.ReceiverAdapter;
import org.jgroups.View;
import org.jgroups.util.Util;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.LinkedList;
import java.util.List;

public class GUIChat extends ReceiverAdapter {

    final List<String> history = new LinkedList<String>();

    private JChannel channel;
    private String userName = System.getProperty("user");
    private JTextField textField;
    private JTextArea textArea;

    public static void main(String[] args) throws Exception {
        new GUIChat().start();
    }

    public void viewAccepted(View newView) {
        appendText("** in room: " + newView + "\n");
    }

    public void receive(Message msg) {
        String line = msg.getObject().toString();
        synchronized(history) {
            history.add(line);
            appendText(line);
        }
    }

    public void getState(OutputStream output) throws Exception {
        synchronized(history) {
            Util.objectToStream(history, new DataOutputStream(output));
        }
    }

    public void setState(InputStream input) throws Exception {
        List<String> list = (List<String>) Util.objectFromStream(new DataInputStream(input));
        synchronized(history) {
            history.clear();
            history.addAll(list);
        }
        appendText("received " + list.size() + " messages in chat history:\n");
        for(String str: list) appendText(str);
    }

    private void start() throws Exception {
        final JPanel panel = createPanel();
        channel = new JChannel();
        channel.setReceiver(this);
        channel.connect("ChatCluster");
        channel.getState(null, 10000);
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame frame = new JFrame("Simple Chat");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(panel);
                frame.pack();
                frame.setVisible(true);
            }
        });
    }

    private JPanel createPanel() {
        JPanel panel = new JPanel(new GridBagLayout());
        GridBagConstraints c = new GridBagConstraints();
        c.gridwidth = GridBagConstraints.REMAINDER;
        c.fill = GridBagConstraints.HORIZONTAL;
        c.weightx = c.weighty = 1.0;
        textArea = new JTextArea(20, 40);
        textArea.setEditable(false);
        panel.add(new JScrollPane(textArea), c);

        c.weightx = c.weighty = 0.0;
        c.fill = GridBagConstraints.HORIZONTAL;

        JPanel messageContainer = new JPanel(new FlowLayout());
        messageContainer.add(new JLabel("Message:"));
        textField = new JTextField(40);
        textField.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                String line="[" + userName + "] " + textField.getText() + "\n";
                try {
                    channel.send(new Message(null, null, line));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        messageContainer.add(textField);

        JButton button = new JButton("Bye!");
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                channel.close();
                System.exit(0);
            }
        });
        messageContainer.add(button);

        panel.add(messageContainer, c);
        return panel;
    }

    private void appendText(String newline) {
        textArea.append(newline);
        textField.setText("");
        textArea.setCaretPosition(textArea.getDocument().getLength());
    }

}

The code above represents an all-in-one client/server chat room based on JGroups and Swing. The main features are:

  • Keeping a chat history (as a list in memory) to send to any new user connected.
  • Sending and receiving messages to/from other users.
  • Being notified when someone leaves/joins the chat.

First of all, GUIChat class extends org.jgroups.ReceiverAdapter that has most of the code ready for us, and we limit to override only a few methods of it.

To start the chat, we need to define the channel and its group:

channel = new JChannel();
channel.setReceiver(this);
channel.connect("ChatCluster");
channel.getState(null, 10000);

and when we leave the chat the channel must be closed:

channel.close();

To keep the chat history updated, we use the method:

public void receive(Message msg)

This method is used when we receive messages from other users (and ourself) too. Indeed, don't forget that in a multicast system, messages are sent to all members of the group including the sender.

So when a message is received, it is appended on the history-list and displayed in the text area.

To send a message to the other members we use the method:

channel.send(new Message(null, null, line));

The constructor parameters for the Message class are, respectively, destination address, source address, and data to send. Null on the destination address will send the message to all the group's members.

When a new user joins the chat, the already present members send him/her the history with the method:

public void getState(OutputStream output) throws Exception

that 'streams' (marshalling) the history-list object.

The history received is converted (unmarshalling) to history-list object with the method:

public void setState(InputStream input) throws Exception

When a user joins or leaves the chat all the other members are notified via:

public void viewAccepted(View newView)

To have a better comprehension of the API and the functionalities of JGroups I recommend that you read the official documentation here.

Compiling and Running the Application

First, create a folder where you will create the GUIChat.java file with the code above. Then download from here the JGroups jar file (I have used jgroups-3.2.8.Final.jar).

To compile run:

javac -cp jgroups-3.2.8.Final.jar GUIChat.java

and to run the application:

java -cp .:jgroups-3.2.8.Final.jar -Duser=john -Djava.net.preferIPv4Stack=true GUIChat

You can run different instances of the chat on the same machine or on different machines in the same network.


Well, that's it guys, I hope you enjoyed it!

More by this Author

  • Three ways to remove "Proudly powered by WordPress"
    1

    If you want to remove or replace the message "Proudly powered by WordPress" from the bottom of a theme, for example TwentyTen, you can follow 3 procedures. I suggest you to work on a child theme created from...

  • How To Parse Html In Java - Part 2
    0

    In the first article we saw how to parse html in Java using the libraries: Jsoup HtmlCleaner Internal Swing SDK parser This time we will see other useful libraries to parse html like: TagSoup HTML Parser...

  • How To Parse Html In Java
    7

    Examples and information about how to parse html in Java using: Jsoup, HtmlCleaner, Internal Swing SDK parser, TagSoup, HTML Parser, JTidy, NekoHtml, Cobra, HotSAX and Jerico HTML Parser.


Comments

No comments yet.

    Sign in or sign up and post using a HubPages Network account.

    0 of 8192 characters used
    Post Comment

    No HTML is allowed in comments, but URLs will be hyperlinked. Comments are not for promoting your articles or other sites.


    Click to Rate This Article
    working