XMPPError 409

I am using the smack library along with the Jive Messenger and very much appreciate both offerings. I am having trouble moving ahead on my slowly-evolving application because of a basic lack of knowledge about the ‘‘state machine’’ involved in using Multi User Chat.

My test cycle, at the moment, only involves:

a. Make the connection.

b. Login.

c. Establish the muc.

d. Add a packet listener.

e. Join the muc.

f. Send a message to the muc.

g. Leave the muc.

h. Close the connection.

[All of the above, by the way, is carried out on a connection with your very

nice DEBUG_ENABLED, and the code is wrapped in try-catch blocks that

are giving me fine stack traces where applicable.]

Now I have several different, but related, questions about what is going on.

I will try to keep things simple and clear, however, by limiting one request for

help to one issue and work through the set constructively.

Here’'s basic question #1. The first time through this suite of calls, I get output

that seems reasonable. No exceptions are thrown. The second time, I can get

through steps a-d, but attempting to re-join throws the 409, Conflict, error.

I try to use the administrative interface to Jive Messenger to learn as much as I can,

about the ‘‘state machine’’, but I can’'t learn anything other than: The second session

connection is open, there is 1 member in the chat room.

It’'s pretty clear that my ‘‘leave()’’ did not occur. So, I stop and restart Jive Messenger,

knowing no other way to purge the ‘‘state machine’’. Then I try my loop a third time,

and, yes, it reproduces the output from the first loop.

What’'s the correct way to leave, shutdown, obliterate, or whatever a session in a

chat room so that the member can later re-enter with the same Nickname?

Hey Terry,

I bet this is a timing issue between g) and h). Current Smack implementation will not send the packet to the server if you close the connection immediately. Try adding a small delay between g and h and let me know how it goes. For the next Smack release we are considering adding a flush of the queued packets before closing the connection. Anyway, if you wait a couple of seconds after h) the server should remove the user from the room since the connection was closed.

Regards,

– Gato

Gato,

Thank you for the reply. While waiting for some help, I looked at a couple of things,

and added a 2-second delay just where you suggested, but it did not fix the

problem. I will retry with a somewhat longer interval when I get my database

reconstructed! [One of the other things I began to investigate is the persistence of

‘‘delayed’’ messages to a chat room, so I appreciated reading your response to

related questions a couple of months ago, but I probably created some integrity

constraint error when I deleted some INSERT statements from the database

startup scripts.]

Since I have the system ‘‘hosed’’ at the moment, I cannot faithfully recreate the facts,

but I am pretty certain that after I looked at your source code and discovered that

‘‘leave()’’ is implemented as a request to the server to declare that member as

‘‘unavailable’’, I looked at the debug output and, indeed, saw that that packet was

sent. After the 2-second delay, I think there was another ‘‘unavailable’’ packet

associated with the connection.close(). So, I can see that your sense is

correct concerning where the race condition is trapped. I think the state on the

Jive Messenger server would show my session ended, but still show a count of 1/30

‘‘in the room’’.

Terry,

Ok, I think that you may have hit a concurrency issue we recently fixed in Messenger. Try using the latest nightly build and let me know if you still have this problem. If you do then I would appreciate if you can provide me a sample code that reproduces the problem.

Thanks,

– Gato

Ok, I’'ve restored the database and re-run with a 5 second delay between

muc.leave(); and conn.close();. I don’'t attempt to re-connect re-join for a minute or

two after that. In the time inbetween I use the Jive Messenger Admin Console

and confirm that there are not open sessions, but the server still believes at least

one member is in the chat room – I sure wish the Admin Console would let me

drill down another level to determine who that member is, but since no one else has

access to this test bed, the odds are pretty good that it is the member who joined

and left.

Here is the debug output, if that will help us:

The second packet comes 5 seconds after the first

one; that’'s why it looks that conn.close(); must send a slightly different form of

‘‘unavailability notice’’ to the server than muc.leave();.

I guess I could test ‘‘not closing the connection’’, but I hope that isn’'t the correct solution because that would be ‘‘awkward’’, at best, in the way the planned application should operate.

Thank you for your continued help.

Terry,

Are you using the latest nightly build? If you do can you send me your test case so I can use it to analyze the problem?

Thanks,

– Gato

  1. No, my previous test were against Jive Messenger 2.1.3 – April 22, 2005.

  2. So, I just took the May 6th nightly and hopefully configured it correctly for a valid test case: a.) I copied the jive-messenger.xml from the ‘‘production’’ install and the ‘‘embedded-db’’ directory. I couldn’'t discover how to launch the Admin Console in order to ‘‘snoop around’’ to make sure that I had valid server state, so I at least cranked up an Exodus client connection and it appeared to show that the ‘‘production’’ environment was still in tact running the latest nightly executable.

  3. Sadly, no change in the result. Here is my application audit trail output, it pretty well tells the story:

Selection Event: SelectionEvent{Tree {} time=108492524 data=null item=TreeItem {Raven’'s Nest} detail=0 x=0 y=0 width=0 height=0 stateMask=0 doit=true} :: WID_MEN_02_01

Number of occupants: 0

Room Subject: Fright

Successful muc.

About to join.

I’'m a joiner…true

Sorry, Ed, he’'s gone.

This room is not anonymous.

De-muced…

Selection Event: SelectionEvent{Tree {} time=108567541 data=null item=TreeItem {Raven’'s Nest} detail=0 x=0 y=0 width=0 height=0 stateMask=0 doit=true} :: WID_MEN_02_01

Number of occupants: 1

Room Subject: Fright

Successful muc.

About to join.

(409)

at org.jivesoftware.smackx.muc.MultiUserChat.join(MultiUserChat.java:438)

at com.sss.xmpp.XMPPControlView.join(XMPPControlView.java:115)

at com.sss.ui.MenuControlView.widgetSelected(MenuControlView.java:200)

at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:89)

at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:82)

at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:842)

at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:2894)

at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:2527)

at com.sss.SSSClient.(SSSClient.java:131)

at com.sss.SSSClient.main(SSSClient.java:164)

And here’'s the chunk of code that produces that result

public XMPPControlView (CTabItem tabItem)
     {
          this.tabItem = tabItem;
          tabFolder = tabItem.getParent();
          display = tabFolder.getDisplay();
          shell = tabFolder.getShell();           rb = ResourceBundle.getBundle (System.getProperty ("com.sss.app.rb"));
          appBackground = (RGB) rb.getObject ("APP_BACKGROUND_RGB");
          imageManager = (ImageManager) display.getData (ImageManager.ID);           configure();
          
          try {
               XMPPConnection.DEBUG_ENABLED = true;
               conn = new XMPPConnection ("ag", 5222);          
               conn.login ("tcorbet", "1corbet");                RoomInfo info = MultiUserChat.getRoomInfo (conn, "Ravens@conference.ag");
               P.rintln ("Number of occupants: " + info.getOccupantsCount());
               P.rintln ("Room Subject: " + info.getSubject());
               
               muc = new MultiUserChat (conn, "Ravens@conference.ag");
               P.rintln ("Successful muc.");
          } catch (XMPPException ex) {
               ex.printStackTrace();
          }
     } // End of Constructor for XMPPControlView.      private void configure()
     {
          roomPanel = new Composite (tabFolder, SWT.NONE);
          roomPanel.setData (EnumHelper.EID, "Room Panel");
          roomPanel.setData ("XMPP", this);
          FormData formLOData = new FormData();
          formLOData.top = new FormAttachment (0, 0);
          formLOData.left = new FormAttachment (0, 0);
          formLOData.bottom = new FormAttachment (100, 0);
          formLOData.right = new FormAttachment (100, 0);
          roomPanel.setLayoutData (formLOData);
          roomPanel.setLayout (new FormLayout());
          
          tabItem.setControl (roomPanel);
     } // End of configure().
     
     public void join (AppUser user)
     {
          try {
               muc.addMessageListener (new PacketListener()
               {
                    public void processPacket (Packet packet)
                    {
                         P.rintln (packet.toXML());
                    } // End of processPacket().
               }); // End of Anonymous MessageListener Class.
               
               muc.addParticipantListener (new PacketListener()
               {
                    public void processPacket (Packet packet)
                    {
                         P.rintln (packet.toXML());
                    } // End of processPacket().
               }); // End of Anonymous ParticipantListener Class.
               
               P.rintln ("About to join.");
               DiscussionHistory history = new DiscussionHistory();
               history.setMaxStanzas (50);
               muc.join ("Terry", null, history,
                    SmackConfiguration.getPacketReplyTimeout());
               P.rintln ("I''m a joiner..." + muc.isJoined());
               
               muc.sendMessage ("Here''s Johnny.");
               P.rintln ("Sorry, Ed, he''s gone.");
          // } catch (XMPPException ex) {
          } catch (Exception ex) {
               ex.printStackTrace();
          }
     } // End of join();
     
     public void leave()
     {
          try {
               muc.leave();
               try { Thread.sleep (5000); } catch (Exception ex) {;}
               conn.close();
               P.rintln ("De-muced.....");
          } catch (Exception ex) {
               ex.printStackTrace();
          }
     } // End of leave(). } // End of XMPPControlView Class.

Ok, so now that I have spent many hours poking around in the code and learning how to get a build with some additional logging, here’'s why my leave() fails:

In MUCUserImpl.process (Presence packet) there is a check to determine whether or not the occupant who has just declared himself ‘‘unavailable’’ is a member of a Group. If he is, you’'ll never hit server.removeUser().

I have no idea why the semantics of an individual’‘s action should depend upon group membership, but that’'s the way the code is currently written…

Hey Terry,

Thanks for the detailed report. The problem was triggered because your room name contains upper case letters. (JM-290) We modified some code in the server so even if you use upper case letters the zombies won’‘t stay forever in the room. Try using tomorrow’'s nightly build and let me know how it goes.

Thanks,

– Gato

I was unaware of any case sensitivity to a room name.

I have dropped the one with mixed case and created a new one with

all lower case. Even without the new build, this finally fixes the leave/join

problem.

Thank you.