diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java index ee9edd84c..aceb4d9ff 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java @@ -662,7 +662,12 @@ public class MultiUserChat { // If we've already joined the room, leave it before joining under a new // nickname. if (joined) { - leave(); + try { + leaveSync(); + } + catch (XMPPErrorException | NoResponseException | MucNotJoinedException e) { + LOGGER.log(Level.WARNING, "Could not leave MUC prior joining, assuming we are not joined", e); + } } enter(mucEnterConfiguration); } @@ -703,6 +708,50 @@ public class MultiUserChat { connection.sendStanza(leavePresence); } + /** + * Leave the chat room. + * + * @return the leave presence as reflected by the MUC. + * @throws NotConnectedException + * @throws InterruptedException + * @throws XMPPErrorException + * @throws NoResponseException + * @throws MucNotJoinedException + */ + public synchronized Presence leaveSync() + throws NotConnectedException, InterruptedException, NoResponseException, XMPPErrorException, MucNotJoinedException { + // Note that this method is intentionally not guarded by + // "if (!joined) return" because it should be always be possible to leave the room in case the instance's + // state does not reflect the actual state. + + // Reset occupant information first so that we are assume that we left the room even if sendStanza() would + // throw. + userHasLeft(); + + final EntityFullJid myRoomJid = this.myRoomJid; + if (myRoomJid == null) { + throw new MucNotJoinedException(this); + } + + // We leave a room by sending a presence packet where the "to" + // field is in the form "roomName@service/nickname" + Presence leavePresence = new Presence(Presence.Type.unavailable); + leavePresence.setTo(myRoomJid); + + StanzaFilter reflectedLeavePresenceFilter = new AndFilter( + StanzaTypeFilter.PRESENCE, + new StanzaIdFilter(leavePresence), + new OrFilter( + new AndFilter(FromMatchesFilter.createFull(myRoomJid), PresenceTypeFilter.UNAVAILABLE, MUCUserStatusCodeFilter.STATUS_110_PRESENCE_TO_SELF), + new AndFilter(fromRoomFilter, PresenceTypeFilter.ERROR) + ) + ); + + Presence reflectedLeavePresence = connection.createStanzaCollectorAndSend(reflectedLeavePresenceFilter, leavePresence).nextResultOrThrow(); + + return reflectedLeavePresence; + } + /** * Get a {@link MucConfigFormManager} to configure this room. *

diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatIntegrationTest.java index d4898bf16..d57f4467e 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatIntegrationTest.java @@ -16,6 +16,9 @@ */ package org.jivesoftware.smackx.muc; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + import java.util.List; import org.jivesoftware.smack.MessageListener; @@ -23,8 +26,12 @@ import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smackx.muc.MultiUserChat.MucCreateConfigFormHandle; +import org.jivesoftware.smackx.muc.MultiUserChatException.MucNotJoinedException; +import org.jivesoftware.smackx.muc.MultiUserChatException.NotAMucServiceException; +import org.jivesoftware.smackx.muc.packet.MUCUser; import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTest; @@ -36,6 +43,7 @@ import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.jid.parts.Localpart; import org.jxmpp.jid.parts.Resourcepart; +import org.jxmpp.stringprep.XmppStringprepException; public class MultiUserChatIntegrationTest extends AbstractSmackIntegrationTest { @@ -61,6 +69,24 @@ public class MultiUserChatIntegrationTest extends AbstractSmackIntegrationTest { } } + @SmackIntegrationTest + public void mucJoinLeaveTest() throws XmppStringprepException, NotAMucServiceException, NoResponseException, + XMPPErrorException, NotConnectedException, InterruptedException, MucNotJoinedException { + EntityBareJid mucAddress = JidCreate.entityBareFrom(Localpart.from("smack-inttest-join-leave-" + randomString), + mucService.getDomain()); + + MultiUserChat muc = mucManagerOne.getMultiUserChat(mucAddress); + + muc.join(Resourcepart.from("nick-one")); + + Presence reflectedLeavePresence = muc.leaveSync(); + + MUCUser mucUser = MUCUser.from(reflectedLeavePresence); + assertNotNull(mucUser); + + assertTrue(mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110)); + } + @SmackIntegrationTest public void mucTest() throws Exception { EntityBareJid mucAddress = JidCreate.entityBareFrom(Localpart.from("smack-inttest-" + randomString), mucService.getDomain());