[vox-tech] Chat program in 100 lines of code!

Brian Lavender brian at brie.com
Wed Oct 20 09:12:52 PDT 2010


I read a PERL column  that Randal Schwartz wrote a few years back
(maybe 10 years back) about using a gpg signed email and the body with an
image or some content that could be used to update a website. You would
send the message and a procmail recipe would intercept the message and
pipe it through a PERL script.

So, what if we could just write programs like this? Ones that could send
messages back and forth? Well, I recently discovered Java Message Service
(JMS) and it looks very cool. I was looking through the O'Reilly Java
Message service book and I was able to write a chat program with just
a few lines of code and the ActiveMQ message broker.

The following code is from Chapter 2 of the "Java Message Service, 2nd
Ed" by Mark Richards et al, 2009, ISBN-13 978-0-596-52204-9. Some of the
description in the book didn't make sense, so here is my implementation.

First, make sure you have Java installed. I use OpenJDK.

Download ActiveMQ
http://activemq.apache.org/download.html

Remember the directory when you unpacked the activeMQ.
$ export ACTIVEMQ_HOME=<wherever you put it>

Add the following to your classpath. The following also adds the current
working directory to your CLASSPATH, so if you don't have it, you will
probably need it to run the java Chat program below.

$ export CLASSPATH=ACTIVEMQ_HOME/activemq-all-5.4.1.jar:.:$CLASSPATH

Edit the config file and add the following configuration to the
conf/activemq.xml configuration file right after the </destinationPolicy>
end directive.

$ cat conf/activemq.xml

<destinations>
	<topic name="topic1" physicalName="jms.topic1" />
</destinations>

Start your ActiveMQ messaging broker. I assume it is a broker. I believe
it will create a default security policy, so you might have to follow
a few directiions.

$ cd $ACTIVEMQ_HOME
$ bin/activemq start

Change to some other directory such as a source folder.

$ cd ~/src

Create the file jndi.properties. Content is as follows:
$ cat jndi.properties

java.naming.factory.initial = org.apache.activemq.jndi.ActiveMQInitialContextFactory
java.naming.provider.url = tcp://localhost:61616
java.naming.security.principal=system
java.naming.security.credentials=manager
connectionFactoryNames = TopicCF
topic.topic1 = jms.topic1

Make a ch02 subdirectory.
$ mkdir ch02

Content is the following for the java Chat program:
$ cat Chat.java

package ch02;

import java.io.*;
import javax.jms.*;
import javax.naming.*;

public class Chat implements javax.jms.MessageListener{
    private TopicSession pubSession;
    private TopicPublisher publisher;
    private TopicConnection connection;
    private String username;

    /* Constructor used to Initialize Chat */
    public Chat(String topicFactory, String topicName, String username) 
        throws Exception {
    	
    	// Obtain a JNDI connection using the jndi.properties file
        InitialContext ctx = new InitialContext();

        // Look up a JMS connection factory
        TopicConnectionFactory conFactory = 
        	(TopicConnectionFactory)ctx.lookup(topicFactory);

        // Create a JMS connection
        TopicConnection connection = conFactory.createTopicConnection();

        // Create two JMS session objects
        TopicSession pubSession = connection.createTopicSession(
        	false, Session.AUTO_ACKNOWLEDGE);        
        TopicSession subSession = connection.createTopicSession(
        	false, Session.AUTO_ACKNOWLEDGE);

        // Look up a JMS topic
        Topic chatTopic = (Topic)ctx.lookup(topicName);

        // Create a JMS publisher and subscriber
        TopicPublisher publisher = 
            pubSession.createPublisher(chatTopic);
        TopicSubscriber subscriber = 
            subSession.createSubscriber(chatTopic, null, true);

        // Set a JMS message listener
        subscriber.setMessageListener(this);

        // Intialize the Chat application variables
        this.connection = connection;
        this.pubSession = pubSession;
        this.publisher = publisher;
        this.username = username;

        // Start the JMS connection; allows messages to be delivered
        connection.start( );
    }

    /* Receive Messages From Topic Subscriber */
    public void onMessage(Message message) {
        try {
            TextMessage textMessage = (TextMessage) message;
            String text = textMessage.getText( );
            System.out.println(text);
        } catch (JMSException jmse){ jmse.printStackTrace( ); }
    }

    /* Create and Send Message Using Publisher */
    protected void writeMessage(String text) throws JMSException {
        TextMessage message = pubSession.createTextMessage( );
        message.setText(username+": "+text);
        publisher.publish(message);
    }

    /* Close the JMS Connection */
    public void close( ) throws JMSException {
        connection.close( );
    }

    /* Run the Chat Client */
    public static void main(String [] args){
        try{
            if (args.length!=3)
                System.out.println("Factory, Topic, or username missing");

            // args[0]=topicFactory; args[1]=topicName; args[2]=username
            Chat chat = new Chat(args[0],args[1],args[2]);

            // Read from command line
            BufferedReader commandLine = new 
              java.io.BufferedReader(new InputStreamReader(System.in));

            // Loop until the word "exit" is typed
            while(true){
                String s = commandLine.readLine( );
                if (s.equalsIgnoreCase("exit")){
                    chat.close( ); // close down connection
                    System.exit(0);// exit program
                } else 
                    chat.writeMessage(s);
            }
        } catch (Exception e){ e.printStackTrace( ); }
    }
}

Compile the program. Change to the directory above to do this as follows:

$ cd ..
$ javac ch02/Chat.java

Now, run your chat client. Make sure that your CLASSPATH is set as above.

$ java ch02.Chat TopicCF topic1 John 

Now, from another terminal, set the same CLASSPATH environment and run the
Chat program with another user such as Mary.

$ java ch02.Chat TopicCF topic1 Mary

When you type and press return, the message should go to all who are connected.
Is this cool or what?

brian
-- 
Brian Lavender
http://www.brie.com/brian/

"Program testing can be used to show the presence of bugs, but never to
show their absence!"

Professor Edsger Dijkstra
1972 Turing award recipient
_______________________________________________
Lug-nuts mailing list
Lug-nuts at saclug.org
http://www.leibmanland.com/cgi-bin/mailman/listinfo/lug-nuts
-- 
Brian Lavender
http://www.brie.com/brian/

"Program testing can be used to show the presence of bugs, but never to
show their absence!"

Professor Edsger Dijkstra
1972 Turing award recipient


More information about the vox-tech mailing list