Friday, September 23, 2005

Writing a XMPP chat client in Java (Google Talk)

I think I just start off to the topic, firstly u need a 3rd-party library "smack.jar"+"smackx.jar" which extracted from "smack-2.0.0.zip". Get the file from here !

I'm using "javax.swing" classes to build the UI, below is a brief design layout:
+-----------------------------------------------+
| Login : __________ | <---- login name (e.g.: yourmail.google.com)
| Password: ________ | Login| | <---- password (e.g.: google mail password)
+-----------------------------------------------+
| +------------------------------+ | Send | |
| | | |
| | | |
| +------------------------------+ | quit | |
| _____________________________ V | <--- friend's list
| _______________________________ | <---- chat message here
+-----------------------------------------------+

Now u get what u needed, put the libraries in a the classpath so that your Java compiler can find it.
Here's the code and I'll explains line by line in comments (codes are meant to be small to save up space, simply cut and paste to a textpad for the aid of reading):
____________________________________________________________________________

ChatClient.java
____________________________________________________________________________

package com.avatar.chat;

// import swing classes
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

// import smack classes
import org.jivesoftware.smack.*;
import org.jivesoftware.smack.packet.*;
import org.jivesoftware.smack.filter.*;


// packet listener will required you to implement a method
// called
"processPacket(Packet packet)" which lets you
// go lower level to handle the in/out packets better

public class ChatClient extends JFrame implements ActionListener, PacketListener{
private JButton btnLogin, btnSend, btnQuit;
private JTextArea taChatArea;
private JTextField tfChatMessage, tfLoginName;
private JComboBox cbOpponent;
private JPasswordField pfLoginPassword;

// GoogleTalkConnection is one of the class by Smack
private GoogleTalkConnection con = null;

// GroupChat will allowed you to listen to more than 1 opponent
private GroupChat chat = null;
private String login_name = "";
private String login_password = "";
private String opponent = "";
private boolean isLogin = false;

// require class -- PacketFilter and PacketCollector, explain below
private PacketFilter filter = null;
private PacketCollector myCollector = null;

// Message is a class to hold all data which extracted from the packet,
// e.g. sender, receiver, timestamp, message ... etc

private Message msg = null;

public ChatClient(){
super("Chat Client");

setSize(400,300);
setResizable(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout(0,0));
initComponents();

setVisible(true);
}

public void initComponents(){
btnLogin = new JButton("Login");
btnLogin.addActionListener(this);
btnSend = new JButton("Send");
btnSend.setMnemonic(KeyEvent.VK_ENTER);
btnSend.addActionListener(this);
btnQuit = new JButton("Quit");
btnQuit.addActionListener(this);
taChatArea = new JTextArea(10,40);
taChatArea.setEditable(false);
JScrollPane chatScroller = new JScrollPane(taChatArea);
chatScroller.setPreferredSize(new Dimension(300, 200));
chatScroller.setMinimumSize(new Dimension(300, 200));
chatScroller.setAlignmentX(Component.LEFT_ALIGNMENT);
tfChatMessage = new JTextField(40);
tfLoginName = new JTextField(10);
cbOpponent = new JComboBox();
cbOpponent.setEditable(true);
pfLoginPassword = new JPasswordField(10);

JPanel pnlButton = new JPanel(new GridLayout(2,1,0,0));
JPanel pnlLogin = new JPanel(new GridLayout(3,2,0,0));
JPanel pnlMessage = new JPanel(new GridLayout(2,1,0,0));

pnlLogin.add(new JLabel("User Name:"));
pnlLogin.add(tfLoginName);
pnlLogin.add(new JLabel("Password:"));
pnlLogin.add(pfLoginPassword);
pnlLogin.add(new JLabel("press here to login ->"));
pnlLogin.add(btnLogin);
pnlButton.add(btnSend);
pnlButton.add(btnQuit);
pnlMessage.add(cbOpponent);
pnlMessage.add(tfChatMessage);

setChat(false);

add(pnlLogin, BorderLayout.NORTH);
add(pnlButton, BorderLayout.EAST);
add(chatScroller, BorderLayout.CENTER);
add(pnlMessage, BorderLayout.SOUTH);
}

// disable sending meassage when variable 'mark' is false (offline), and vice-versa
public void setChat(boolean mark){
tfChatMessage.setEnabled(mark);
cbOpponent.setEnabled(mark);
btnSend.setEnabled(mark);
}

// clean up jobs appoint exit
public void logout(){
if(con!=null){
con.close();
}
cbOpponent.removeAllItems();
}

// when user logged in, set the flag to true (online)
public boolean login(String login_name,String login_password){

// get textfield's username
this.login_name = login_name;

// get passwordfield's password
this.login_password = login_password;
boolean flag = false;

try{
con = new GoogleTalkConnection();
con.login(login_name, login_password);

// filter the XML packet into a PacketCollector (much like a queue),
// so that you can get them back later

filter = new AndFilter(
new PacketTypeFilter(Message.class),
new FromContainsFilter(opponent));
myCollector = con.createPacketCollector(filter);

// initialise your chatgroup
chat = con.createGroupChat(login_name+"'s Chat");

// initialise your message
msg = new Message();

// get all user from your friends list
Roster roster = con.getRoster();
for (Iterator i=roster.getEntries(); i.hasNext(); ) {
RosterEntry re = (RosterEntry)i.next();
cbOpponent.addItem(re.getUser());

// Register the listener.
}

con.addPacketListener(this, filter);

setChat(true);
flag = true;
}catch(Exception ex){
ex.printStackTrace();

// any thing during login, disable user to send message
setChat(false);
flag = false;
}finally{
return flag;
}
}

public void windowClosing(WindowEvent we){
logout();
}


// implementing processPacket for PacketListener interface
public void processPacket(Packet packet) {
// Put the incoming message on the chat history or chat board.
Message msg = (Message)packet;
taChatArea.append(msg.getFrom()+
": "+msg.getBody()+"\n");
}

public void actionPerformed(ActionEvent e){
if(e.getSource()==btnLogin){
String test = btnLogin.getLabel();
if(test.equals("Login")){
isLogin = login(tfLoginName.getText().toString().trim(),
pfLoginPassword.getText().toString().trim());
btnLogin.setLabel("Logout");
}else if(test.equals("Logout")){
logout();
setChat(false);
isLogin = false;
btnLogin.setLabel("Login");
}
}else if(e.getSource()==btnSend){
opponent = cbOpponent.getSelectedItem().toString();
String content = tfChatMessage.getText().toString();
if(isLogin){
try{
msg.setTo(opponent);
msg.setBody(content);
chat.sendMessage(msg);

// append your sent message to chat board
taChatArea.append(login_name+": "+tfChatMessage.getText()+"\n");
}catch(Exception ex){
ex.printStackTrace();
}
}
}
else if(e.getSource()==btnQuit){
logout();
System.exit(0);
}
}

public static void main(String[] args){
ChatClient c = new ChatClient();
}
}

___________________________________________________________
End of
"ChatClient.java"
___________________________________________________________
Thanks to Smack by JiveSoftware, online messager never got easier to write!

The final output should looks like this, still depends on platform and versioning of JDK.

Other resoures:

[NOTE: Smack is a trademark of Jive Software, Google Talk is a trademark of Google.]

11 comments:

Perfectly Random said...

hi,

This is what i was looking for quite sometime.. thanks! But I couldn't find "GoogleTalkConnection" class in smack jars.. Is it something that u wrote??!? Do I miss something ;)?!?!

Thanks,
-Gopi
email : cgopi24@gmail.com

Raj said...

Thanks for this chat client. I faced the following errors. Not sure why.

cannot find symbol
symbol : class GoogleTalkConnection
location: class com.avatar.chat.ChatClient
private GoogleTalkConnection con = null;

cannot find symbol
symbol : class GroupChat
location: class com.avatar.chat.ChatClient
private GroupChat chat = null;

annot find symbol
symbol : class GoogleTalkConnection
location: class com.avatar.chat.ChatClient
con = new GoogleTalkConnection();

java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
4 errors

Tool completed with exit code 1

aishubaskar said...

hi i tried ur client prog...
am gettin the foll errors cud u pls help me out
thx in advance

ChatClient.java:103: cannot find symbol
symbol : method close()
location: class org.jivesoftware.smack.GoogleTalkConnection
con.close();
^
ChatClient.java:134: incompatible types
found : java.util.Collection < org.jivesoftware.smack.RosterEntry >
required: java.util.Iterator
for (Iterator i=roster.getEntries(); i.hasNext(); ) {
^
ChatClient.java:186: cannot find symbol
symbol : variable chat
location: class com.avatar.chat.ChatClient
chat.sendMessage(msg);
^
Note: ChatClient.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
3 errors

MS Vivek Chaitanya said...

here is the code for GoogleTalkConnection.java

import org.jivesoftware.smack.*;

public class GoogleTalkConnection extends XMPPConnection {

public GoogleTalkConnection() throws XMPPException {
super("talk.google.com");
}
}

I am facing different issue. I am not able to connect to server.

I am trying from Office so there could be some restrictions. And I don't have internet at Home. Trying to solve this issue.

aishubaskar said...

i ve solved tht problem....its working for me... thanx for the client program ..the problem was with the roster entry code...

Roster myContacts = conn2.getRoster();
Collection<
RosterEntry
> entries = myContacts.getEntries();
for (RosterEntry entry : entries)
{
//System.out.println(entry.getUser() + myContacts.getPresence(entry.getUser()));
cbOpponent.addItem(entry.getUser());
}

and if u r not able to connect to the server put this following code in the try block and chk it out ...

ConnectionConfiguration config = new ConnectionConfiguration("talk.google.com", 5222, "gmail.com");
XMPPConnection conn2 = new XMPPConnection(config);
conn2.connect();

conn2.login(login_name, login_password);

Unknown said...

I got the code to work,

you might want to change groupChat to Chat for a first test or retrieve groupChat from elsewhere.

Added maybe create an array to list the contact names tant you can refer to when you send message, the send message needs the destination's JID

Saminda said...

Any idea hw to implement the group chat?? I mean hw do u accept an invitation from our gtalk client?

Unknown said...

Hello,

I am using smack to connect to Gtalk and retrieve all the Roster Entries. I just need to know who are online/offline, i.e the user's Presence Status.

I am using the following code to retrieve Presence:

Iterator < RosterEntry > iter = con.getRoster().getEntries().iterator();
while (iter.hasNext()) {
re = iter.next();
System.out.println(re.getUser() + " ==>"
+ roster.getPresence(re.getUser()));
}

When I do this I am always getting the Presence as "Unavailable", though i see a lot of contacts online.

Have i missed something? Please help.

Thanks,
Pradeep

Unknown said...

Roster roster=conn.getRoster();
Iterator i=roster.getEntries().iterator();
ArrayList al=new ArrayList();
while(i.hasNext()){
RosterEntry entry=(RosterEntry)i.next();
al.add(entry.getUser());
System.out.println(entry.getUser()+" "+entry.getName() +" "+roster.getPresence(entry.getUser()).isAvailable()) ;

}
===================================
this is how you can know if a user is available or away

Nirmal a.k.a Nim said...

HI , I have been having the same problem too. I am always getting the presence as unaivalable.Soham , i ve tried using your code but still it says all the users are unaivalable

Darshana Sri said...

does anybody know how to get the chat history of a contact ?