powered by Jive Software

Calling Presence Gurus

Hi Folks,

I’‘m revisiting the age old chestnut that is presence and the retrieval of it from the chat server. I need your help guys! I’‘m writing a simple chat client, using the smack libraries and the Wildfire chat server. The database I’'m using is MySQL.

I have created 2 test users, using the admin console of Wildfire. The way I am testing it is as follows; With my first test user, I log into a Spark IM client. The presence of the Spark user is “Online”. I then start up the Java chat client that I have written and log in using the other user. The users are all subscribed correctly to each other’'s presence.

The connection is made to the server correctly, and I’'m retrieving the correct roster, with the correct number of users. I then try and retrieve the presence for each user using the roster.getPresence(rosterEntry.getUser()) method, which seems to be the standard approach. However, when I try this, the presence object that is returned from the roster.getPresence() method is, more often than not, NULL. This is incorrect, as the other user is logged in and is online. At other times a presence object containing the correct information is returned. It is very, very inconsistent.

The client I have written implements the packet listener interface, and I have have provided an implementation for the processPacket(Packet p) method. I am listening out for Presence packets. And, after the initial connection and log in have been performed, if I change the presence of the test user that is logged into the Spark IM client, the packet listener in my chat client receives the presence packet without any problems. I can also have chat conversations between my two test users without any problems.

The only problem I am having is getting the initial presence information for each user in the roster when I connect to the chat server. I saw a post from a guy called Awenkus in another thread, and he said that it can sometimes takes a moment for the presence object to be returned from a roster.getPresence() request. Even if null is returned for this, I am not seeing any subsequent packets arriving via my PacketListener.

Can anybody suggest to me a reliable solution for obtaining the presence information for each user in my roster after I perform a log in to the chat server?? And if you can, could you possibly provide a simple code example to illustrate what you mean? (I’‘d understand it a lot better). I’‘ve included the code I’‘ve written to obtain the presence for each user. I’‘m hoping the JonWrights and Awenckus’'s of this world can help me out here!! I would really appreciate any help or suggestions. Thanks guys.

// Code Sample

public void setupChat(){

this.getChatSettings();

this.connectToChatServer();

if(this.connectionEstablished) {

Roster roster = this.connection.getRoster();

if(roster != null){

System.out.println("The roster count is " + roster.getEntryCount());

if (roster.getEntryCount() > 0){

Iterator i=roster.getEntries();

while(i.hasNext()){

RosterEntry rosterEntry = (RosterEntry)i.next();

if(rosterEntry != null){

String rosterUser = rosterEntry.getUser();

System.out.println("Roster Entry User is " + rosterUser);

Presence presence = roster.getPresence(rosterUser);

if(presence != null){

System.out.println("The value of presence is " + presence);

System.out.println("The presence getStatus is " + presence.getStatus());

} else {

System.out.println(“Presence is NULL”);

}

} else {

System.out.println(“Roster Entry is NULL”);

}

}

} else {

System.out.println(“The roster is EMPTY”);

}

} else {

System.out.println(“Roster is NULL”);

}

} else {

System.out.println(“Could not establish a connection with the chat server”);

}

}

tbh you’'ve probably solved your own problem, which is upon logging in, if you try and retrive the presence information straight away you will not receive the correct results.

There are a number of things you could do in order to work around this problem. The 1st is probably the most inelegant but should solve the problem

simply add a

try {

Thread.sleep(1000); // experiment with timings here

}

catch (Exception e) {}

/code

After you login and before you try and retrive your roster. This should give you enough time for the presence of your contacts to be properly received.

Or you could add a RosterListener via Roster#addRosterListener

This listener will then notify you off any presence changes to your contacts so you can update your UI accordingly.

hth

Jon

Hi Jon,

many thanks for taking the time to repond, it’‘s much appreciated. That suggestion you made does indeed work in my test environment. (It gives the server a chance to catch it’‘s breath!) However, I’‘m kinda reluctant to use it because if I specify an arbitrary delay time, that delay time may work in the vast majority of cases. But if there is a heavy load on the server at a particular time, or some other problem, and it needs longer than the time you specified to get all the information together, then we’‘re back to the same problem. And, as you say, it is a bit inelegant! (I’'m a sucker for style!)

I’‘d like to explore the possibility of the RosterListener. I’'m just looking at the API for that particular interface. Will any of the methods in that interface be invoked when I do my initial request for the roster after the login is performed? Remember, my problem is getting the initial presence information for all my roster contacts after the login. Can the RosterListener be used for that purpose? And if so, would it be possible to demonstrate using a simple code snippet?

Many thanks.

The RosterListner is just like a PacketListener, except it specificallty listens out for changes in your Roster.

So you would have a class that implements RosterListener

class MyRosterListener implements RosterListener {

void entriesAdded(Collection addresses)

// Called when roster entries are added.

void entriesDeleted(Collection addresses)

// Called when a roster entries are removed.

void entriesUpdated(Collection addresses)

// Called when a roster entries are updated.

void presenceChanged(String XMPPAddress)

// Called when the presence of a roster entry is changed.

// So here might be a good place to update your Roster UI

}

/code

And then you would add your Listener to your Roster

Roster roster = conn.getRoster();

roster.addRosterListener(new MyRosterListener());

/code

Now when your roster is updated you have full control on what to do with it.

hth

Jon

Hi Jon,

thanks for that RosterListener code sample. I still don’‘t think that addresses the problem of how to reliably retrieve a proper and complete roster object that contains an accurate presence state for all the contacts in the roster, right after a logon has been performed. From looking at the API for RosterListener, I think it will only listen for changes to the roster, but only after you’'ve done the initial retrieval of it. Which is great if you want to handle those type of changes during normal usage of the chat client, but not much use if I want to get a reliable initial state of my roster.

If I implement that in my client, when I retrieve the roster after the login I’‘m still going to get incorrect NULL presences for some of my roster contacts, regardless of whether I’'ve added a RosterListener. The RosterListener will only alert me to changes to the roster state, after the client is up and running.

Or is my understanding of RosterListener flawed?

Well the RosterLister wont stop you obtaining null values from your Roster when you retrieve it upon login. This is to do with the speed of the packets being sent across networks so tbh there is not a lot you can do.

The Rosterlistener will allow you to change the state of your contacts though, so upon login you might be getting null from one of your contacts but after a few seconds you’‘ll receive a update from the RosterListener so you’'ll be able to then show them as being online.

If you think about how other chat apps like MSN, Spark, Pandion all work, they have a loading screen. One of the benefits of this is to allow the Roster to be fully updated( as much as can be) before it is then displayed to the user

It appears that you have a timing issue. When logging on the roster and presence information must be passed to Smack from Wildfire. Before this occurs, as you have found, the roster is basically in an unknown state. In this regard it is better for you to use listeners to know when a RosterEntry has been created and thus be able to ascertain a users presence.

Thanks,

Alex

Hi folks,

thanks for all your replies so far. I had to leave this particular problem on the backburner for a couple of days to deal with something else, but I’‘m back on the case again. Hence the reason I haven’'t posted a response.

I’‘ve implemented the RosterListener approach and it does the job perfectly, so many thanks for that suggestion. I’'ve now got a robust approach to getting reliable initial state after I logon to the chat server.

Thanks guys!

Are you sure this approach is robust?

Basically, deducting from this thread, my experiments and the documentation, I have to do the following:

1 create an XMPPConnection object, supplying the connection information (server, port, etc)

2 log in by using XMPPConnection.login(String username, String password) on that connection

3 get the Roster by using XMPPConnection.getRoster()

4 add some object as a roster listener

What happens when some presence packet is received between step 2 and 4? I wouldn’'t get the notifications about those changes, since they were received in the past.

I can’‘t move 2 after 4, since getRoster returns null when I’‘m not logged in yet. I can’‘t read the roster between 3 and 4, since it’‘s in an unknown state there (and I still could receive presence packets between 3.5 and 4). When I’‘d move that to 5, I’'d get some presence information twice.

You are quite right, there is a bit of a race condition there. I had to go back to see how I had handled this situation in the IM client I wrote, the solution I had chosen wasn’‘t particulary elegant though, it worked. I basically had one thread that handled updating roster entrys in my gui according to their state in the XMPP Roster. This thread would wait on a blocking queue to “see” new contacts that needed to be updated. So, when i first initially got the roster I would add the listener, and then populate any contact that were already in there. It didn’'t matter if I told anyone to update twice as a contact could only sit once on the queue, waiting for the UI to update it.

Then in the UI update thread I would pull the entry from the roster, if null was returned they were assumed to be offline, and update the GUI accordingly.

Thanks,

Alex

That all would be a non-issue if I could subscribe to presence changes before authenticating

You can!

// snippet

XMPPConnection connection = new XMPPConnection(host, port);

connection.addPacketListener(packetListener, presenceFilter);

connection.login(username, password);

/code

Hopefully that helps,

Alex

It’'s funny that you say that Alex, because during my testing of the RosterListener code I put in recently, I came across the exact same problem and used practically the same solution that you did, ie. implement a queue mechanism to keep track of presence updates and then perform the presence updates when I was sure everything was in synch and ready. I too thought that it was a bit messy, but it seemed like the most logical thing to do at the time.

And hey, it works, so can’'t really argue with that!

And hey, it works, so can’'t really argue with that!

Well, the problem with race conditions is that you can never be sure that there isn’‘t one just by trying around. There certainly is a case where that solution fails, it’'s just very uncommon.

I outlined this particular case here in the comments: http://adiumx.com/soc/2006/05/30/first-problems/

Hmmm… I don’'t see how that certain case would cause my solution to fail. For the record here is the comment:

“btw, that workaround will fail if a contact is online first, then I grab the contact list, then he goes offline, and then I set the state in my local list.”

For every presence update you are adding that contact to the queue as needing to be updated in the GUI. When they are checked, when the thread handling the updates reaches that particular contact, the contact will be seen as offline. In effect, my queue is a flag that a contact needs to be updated in the GUI, that flag is updated every time a presence change is recieved. If you want more insight to how this system works, you can check the client I wrote online, http://mindim.dev.java.net/

Hope that helps,

Alex

The underlying issue is that the main thread reading the roster’'s state is a different thread that the one receiving the presence packets. In this scenario, you can never be sure about the correct ordering of the events, since you essentially receive them at the same time (what was first? getting the roster, or receiving that presence update?)

One solution to this problem would be to move the roster handling to that packet receiving thread, but I simply can’‘t access that one (it’‘s created internally in Smack, I checked the source), except maybe as a response to another event, but I shouldn’‘t block that thread for a long time (right now, it doesn’'t do more than filtering and passing the event to the main thread).

Bypassing Smack’'s roster handling seems to be the only way to do this properly and elegantly right now apparently.

Here is the basic way I had handled this situation:

On startup I have a thread that handles transfering the current roster state to the GUI

Also, on startup I add a PacketListener to listen for presence changes.

The first thing I do on this thread is populate the initial roster by calling XMPPConnection#getRoster()

I retrieve all contacts from it on that thread, populate there current presences accordingly

In the mean time my PacketListener is populating the queue with any contacts that need to be updated.

After the initial roster population in the GUI my thread switches over to sitting on the queue looking for contacts that need to be updated. I start retriving them.

Say the first contact is alex@jivesoftware.com… I query my roster to get his current state, I then update my GUI accordingly

I then continue to do this for all contacts in the queue and as the program continues this thread’'s only job is to update the contacts in the GUI.

The ordering of events doesn’'t matter as you are merely saying with the presence listener, Something has Happened to this Contact, Update them in the GUI

Hope that helps,

Alex