3

I am developing a simple ejabberd chat using smack api.
I want to make a single program for both sender and receiver side. The idea is like this:

  • Upon running program asks the user whether you want to chat with someone (i.e. wants to be a sender) or wait for someone to initiate the chat (i.e. want to be a receiver).
  • If you choose sender it will ask for receiver address.
  • Then it will initiate a chat to the receiver.

Code for the same is:

import java.util.*;
import java.io.*;
import java.lang.*;

import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.ChatManager;
import org.jivesoftware.smack.ChatManagerListener;


public class Client implements MessageListener{
    static XMPPConnection connection;

    public void login(String userName, String password) throws XMPPException {
        ConnectionConfiguration config = new ConnectionConfiguration("10.100.99.107",5222,"localhost");
        connection = new XMPPConnection(config);

        connection.connect();
        connection.login(userName, password);
    }

    public Chat createChat(String to) throws XMPPException{
        return connection.getChatManager().createChat(to, this);    
    }  

    public void sendMessage(Chat chat, String message) throws XMPPException {
        chat.sendMessage(message);
    }

    public void disconnect() {
            connection.disconnect();
    }

    public void processMessage(Chat chat, Message message) {
        System.out.println("Here");
        if(message.getType() == Message.Type.chat)
            System.out.println(chat.getParticipant() + " says: " + message.getBody());
    }

    public static void main(String args[]) throws XMPPException, IOException, InterruptedException {
        // declare variables
        final Client client = new Client();
        final BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        // turn on the enhanced debugger
        // XMPPConnection.DEBUG_ENABLED = true;

        // Enter your login information here
        System.out.println("Login information:\nusername: ");
        String login_username = br.readLine();
        System.out.print("password: ");
        String login_pass = br.readLine();

        client.login(login_username, login_pass);

        if(connection.isAuthenticated()==true){
            System.out.println("\n"+login_username+" :Successfully logged in");

            final Chat chat;
            System.out.println("Do you want to talk? (y/n)");
            String choice = br.readLine();
            if( choice.equals("y")){
                //Sender's code
                System.out.println("Whom do you want to talk to? - Type contacts full email address:");
                chat = client.createChat(br.readLine());
                chat.addMessageListener(client);

                System.out.println("Enter your message in the console (Type 'quit' to exit.):\n");

                String msg;
                while( !(msg=br.readLine()).equals("quit")) {
                    client.sendMessage(chat, msg);
                }
            }else if(choice.equals("n")){
                //Reciever's code
                ChatManager chatmanager = connection.getChatManager();
                chatmanager.addChatListener( new ChatManagerListener() {
                    @Override
                    public void chatCreated(Chat chatReceived, boolean createdLocally)
                    {
                        System.out.println("Chat Created");

                        chatReceived.addMessageListener(client);

                        System.out.println("Enter your message in the console (Type 'quit' to exit.):\n");
                        String msg;
                        try {
                            while( !(msg=br.readLine()).equals("quit")) {
                                client.sendMessage(chatReceived, msg);
                            }
                        }
                        catch(Exception e){
                            System.out.println(e.getMessage());
                        }
                    }
                });

                //put receiver on waiting
                while (true) {
                    Thread.sleep(200);
                }
            }else{
                System.out.println("Wrong Input");                
            }
            client.disconnect();
            System.exit(0);
        }
    }
}

Error:

When the sender sends a message, the receiver receives it (I have checked via debugging) but doesn't display it. When I quit the chat on the receiver's side it will print all the messages at once. This is only happening for receiver when sender sends a message. I am not sure where I am blocking the output stream. Problem should be with my understanding of interrupts or the waiting while loop.

Abhishek Gupta
  • 6,465
  • 10
  • 50
  • 82

1 Answers1

0

The problem is the location of your read loop inside the callback function.

If you look at ChatManager, you get one thread per chat. In your receiver code, the chatCreated callback is going to occur on that one thread. You then lock up that thread by sitting there reading from stdin. What you're seeing is that when you quit, the processMessage callback is finally free to fire and you get your messages.

You need to spin off a reader thread in chatCreated to handle all the input. Then you return from the callback immediately. In fact, you should always create a separate thread for blocking input. It's the right way to do it, and even in toy problems like this you'll get tripped up in weird ways, so you might as well just make a habit.

A couple other suggestions:

  • Do not call System.exit(0) to exit. That terminates the JVM and some code may not shut down cleanly. Just return from main
  • Your while(true) { Thread.sleep(200); } loop is ugly. A nicer way would be to have the main thread block on a Semaphore and have the reader thread release the lock when they get a 'quit'. Or call Thread#join() on the reader thread.
Nathaniel Waisbrot
  • 23,261
  • 7
  • 71
  • 99