diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/AutoJoinFailedCallback.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/AutoJoinFailedCallback.java new file mode 100644 index 000000000..f9bf7a6b1 --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/AutoJoinFailedCallback.java @@ -0,0 +1,29 @@ +/** + * + * Copyright 2016 Florian Schmaus + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.muc; + +public interface AutoJoinFailedCallback { + + /** + * Invoked if the automatic rejoin rooms on reconnect failed. + * + * @param muc the MultiUserChat which failed first. + * @param e the exception causing the failure. + */ + void autoJoinFailed(MultiUserChat muc, Exception e); + +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatManager.java index a183570cd..33b4a7ebe 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatManager.java @@ -26,8 +26,10 @@ import java.util.Map; import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.CopyOnWriteArraySet; +import java.util.logging.Level; import java.util.logging.Logger; +import org.jivesoftware.smack.AbstractConnectionListener; import org.jivesoftware.smack.ConnectionCreationListener; import org.jivesoftware.smack.Manager; import org.jivesoftware.smack.StanzaListener; @@ -44,6 +46,7 @@ import org.jivesoftware.smack.filter.NotFilter; import org.jivesoftware.smack.filter.StanzaTypeFilter; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Stanza; +import org.jivesoftware.smack.util.Async; import org.jivesoftware.smackx.disco.AbstractNodeInformationProvider; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.disco.packet.DiscoverInfo; @@ -55,8 +58,24 @@ import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.EntityFullJid; import org.jxmpp.jid.DomainBareJid; import org.jxmpp.jid.Jid; +import org.jxmpp.jid.parts.Resourcepart; import org.jxmpp.jid.EntityJid; +/** + * A manager for Multi-User Chat rooms. + *

+ * Use {@link #getMultiUserChat(EntityBareJid)} to retrieve an object representing a Multi-User Chat room. + *

+ *

+ * Automatic rejoin: The manager supports automatic rejoin of MultiUserChat rooms once the connection got + * re-established. This mechanism is disabled by default. To enable it, use {@link #setAutoJoinOnReconnect(boolean)}. + * You can set a {@link AutoJoinFailedCallback} via {@link #setAutoJoinFailedCallback(AutoJoinFailedCallback)} to get + * notified if this mechanism failed for some reason. Note that as soon as rejoining for a single room failed, no + * further attempts will be made for the other rooms. + *

+ * + * @see XEP-0045: Multi-User Chat + */ public final class MultiUserChatManager extends Manager { private final static String DISCO_NODE = MUCInitialPresence.NAMESPACE + "#rooms"; @@ -122,6 +141,10 @@ public final class MultiUserChatManager extends Manager { */ private final Map> multiUserChats = new HashMap<>(); + private boolean autoJoinOnReconnect; + + private AutoJoinFailedCallback autoJoinFailedCallback; + private MultiUserChatManager(XMPPConnection connection) { super(connection); // Listens for all messages that include a MUCUser extension and fire the invitation @@ -152,6 +175,55 @@ public final class MultiUserChatManager extends Manager { } }; connection.addAsyncStanzaListener(invitationPacketListener, INVITATION_FILTER); + + connection.addConnectionListener(new AbstractConnectionListener() { + @Override + public void authenticated(XMPPConnection connection, boolean resumed) { + if (resumed) return; + if (!autoJoinOnReconnect) return; + + final Set mucs = getJoinedRooms(); + if (mucs.isEmpty()) return; + + Async.go(new Runnable() { + @Override + public void run() { + final AutoJoinFailedCallback failedCallback = autoJoinFailedCallback; + for (EntityBareJid mucJid : mucs) { + MultiUserChat muc = getMultiUserChat(mucJid); + + if (!muc.isJoined()) return; + + Resourcepart nickname = muc.getNickname(); + if (nickname == null) return; + + try { + muc.leave(); + } catch (NotConnectedException | InterruptedException e) { + if (failedCallback != null) { + failedCallback.autoJoinFailed(muc, e); + } else { + LOGGER.log(Level.WARNING, "Could not leave room", e); + } + return; + } + try { + muc.join(nickname); + } catch (NotAMucServiceException | NoResponseException | XMPPErrorException + | NotConnectedException | InterruptedException e) { + if (failedCallback != null) { + failedCallback.autoJoinFailed(muc, e); + } else { + LOGGER.log(Level.WARNING, "Could not leave room", e); + } + return; + } + } + } + + }); + } + }); } /** @@ -354,6 +426,29 @@ public final class MultiUserChatManager extends Manager { invitationsListeners.remove(listener); } + /** + * If automatic join on reconnect is enabled, then the manager will try to auto join MUC rooms after the connection + * got re-established. + * + * @param autoJoin true to enable, false to disable. + */ + public void setAutoJoinOnReconnect(boolean autoJoin) { + autoJoinOnReconnect = autoJoin; + } + + /** + * Set a callback invoked by this manager when automatic join on reconnect failed. If failedCallback is not + * null,then automatic rejoin get also enabled. + * + * @param failedCallback the callback. + */ + public void setAutoJoinFailedCallback(AutoJoinFailedCallback failedCallback) { + autoJoinFailedCallback = failedCallback; + if (failedCallback != null) { + setAutoJoinOnReconnect(true); + } + } + void addJoinedRoom(EntityBareJid room) { joinedRooms.add(room); }