mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-11-26 00:02:06 +01:00
Apply Manager pattern to InvitationsMonitor
Don't remove listeners on disconnect (SMACK-571).
This commit is contained in:
parent
a0fe337bcd
commit
f7517ab6cc
1 changed files with 49 additions and 110 deletions
|
@ -32,10 +32,10 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.jivesoftware.smack.AbstractConnectionListener;
|
|
||||||
import org.jivesoftware.smack.Chat;
|
import org.jivesoftware.smack.Chat;
|
||||||
import org.jivesoftware.smack.ChatManager;
|
import org.jivesoftware.smack.ChatManager;
|
||||||
import org.jivesoftware.smack.ConnectionCreationListener;
|
import org.jivesoftware.smack.ConnectionCreationListener;
|
||||||
|
import org.jivesoftware.smack.Manager;
|
||||||
import org.jivesoftware.smack.MessageListener;
|
import org.jivesoftware.smack.MessageListener;
|
||||||
import org.jivesoftware.smack.PacketCollector;
|
import org.jivesoftware.smack.PacketCollector;
|
||||||
import org.jivesoftware.smack.PacketInterceptor;
|
import org.jivesoftware.smack.PacketInterceptor;
|
||||||
|
@ -2344,23 +2344,16 @@ public class MultiUserChat {
|
||||||
*
|
*
|
||||||
* @author Gaston Dombiak
|
* @author Gaston Dombiak
|
||||||
*/
|
*/
|
||||||
private static class InvitationsMonitor extends AbstractConnectionListener {
|
private static class InvitationsMonitor extends Manager {
|
||||||
// We use a WeakHashMap so that the GC can collect the monitor when the
|
|
||||||
// connection is no longer referenced by any object.
|
private static Map<XMPPConnection, InvitationsMonitor> INSTANCES = new WeakHashMap<XMPPConnection, InvitationsMonitor>();
|
||||||
// Note that when the InvitationsMonitor is used, i.e. when there are InvitationListeners, it will add a
|
|
||||||
// PacketListener to the XMPPConnection and therefore a strong reference from the XMPPConnection to the
|
private static final PacketFilter invitationFilter = new PacketExtensionFilter(new MUCUser());
|
||||||
// InvitationsMonior will exists, preventing it from beeing gc'ed. After the last InvitationListener is gone,
|
|
||||||
// the PacketListener will get removed (cancel()) allowing the garbage collection of the InvitationsMonitor
|
|
||||||
// instance.
|
|
||||||
private final static Map<XMPPConnection, WeakReference<InvitationsMonitor>> monitors =
|
|
||||||
new WeakHashMap<XMPPConnection, WeakReference<InvitationsMonitor>>();
|
|
||||||
|
|
||||||
// We don't use a synchronized List here because it would break the semantic of (add|remove)InvitationListener
|
// We don't use a synchronized List here because it would break the semantic of (add|remove)InvitationListener
|
||||||
private final List<InvitationListener> invitationsListeners =
|
private final List<InvitationListener> invitationsListeners =
|
||||||
new ArrayList<InvitationListener>();
|
new ArrayList<InvitationListener>();
|
||||||
private XMPPConnection connection;
|
private final PacketListener invitationPacketListener;
|
||||||
private PacketFilter invitationFilter;
|
|
||||||
private PacketListener invitationPacketListener;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new or existing InvitationsMonitor for a given connection.
|
* Returns a new or existing InvitationsMonitor for a given connection.
|
||||||
|
@ -2368,19 +2361,13 @@ public class MultiUserChat {
|
||||||
* @param conn the connection to monitor for room invitations.
|
* @param conn the connection to monitor for room invitations.
|
||||||
* @return a new or existing InvitationsMonitor for a given connection.
|
* @return a new or existing InvitationsMonitor for a given connection.
|
||||||
*/
|
*/
|
||||||
public static InvitationsMonitor getInvitationsMonitor(XMPPConnection conn) {
|
public static synchronized InvitationsMonitor getInvitationsMonitor(XMPPConnection conn) {
|
||||||
synchronized (monitors) {
|
InvitationsMonitor invitationsMonitor = INSTANCES.get(conn);
|
||||||
if (!monitors.containsKey(conn) || monitors.get(conn).get() == null) {
|
if (invitationsMonitor == null) {
|
||||||
// We need to use a WeakReference because the monitor references the
|
invitationsMonitor = new InvitationsMonitor(conn);
|
||||||
// connection and this could prevent the GC from collecting the monitor
|
INSTANCES.put(conn, invitationsMonitor);
|
||||||
// when no other object references the monitor
|
|
||||||
InvitationsMonitor ivm = new InvitationsMonitor(conn);
|
|
||||||
monitors.put(conn, new WeakReference<InvitationsMonitor>(ivm));
|
|
||||||
return ivm;
|
|
||||||
}
|
|
||||||
// Return the InvitationsMonitor that monitors the connection
|
|
||||||
return monitors.get(conn).get();
|
|
||||||
}
|
}
|
||||||
|
return invitationsMonitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2390,85 +2377,9 @@ public class MultiUserChat {
|
||||||
* @param connection the connection to monitor for possible room invitations
|
* @param connection the connection to monitor for possible room invitations
|
||||||
*/
|
*/
|
||||||
private InvitationsMonitor(XMPPConnection connection) {
|
private InvitationsMonitor(XMPPConnection connection) {
|
||||||
this.connection = connection;
|
super(connection);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a listener to invitation notifications. The listener will be fired anytime
|
|
||||||
* an invitation is received.<p>
|
|
||||||
*
|
|
||||||
* If this is the first monitor's listener then the monitor will be initialized in
|
|
||||||
* order to start listening to room invitations.
|
|
||||||
*
|
|
||||||
* @param listener an invitation listener.
|
|
||||||
*/
|
|
||||||
public void addInvitationListener(InvitationListener listener) {
|
|
||||||
synchronized (invitationsListeners) {
|
|
||||||
// If this is the first monitor's listener then initialize the listeners
|
|
||||||
// on the connection to detect room invitations
|
|
||||||
if (invitationsListeners.size() == 0) {
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
if (!invitationsListeners.contains(listener)) {
|
|
||||||
invitationsListeners.add(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes a listener to invitation notifications. The listener will be fired anytime
|
|
||||||
* an invitation is received.<p>
|
|
||||||
*
|
|
||||||
* If there are no more listeners to notifiy for room invitations then the monitor will
|
|
||||||
* be stopped. As soon as a new listener is added to the monitor, the monitor will resume
|
|
||||||
* monitoring the connection for new room invitations.
|
|
||||||
*
|
|
||||||
* @param listener an invitation listener.
|
|
||||||
*/
|
|
||||||
public void removeInvitationListener(InvitationListener listener) {
|
|
||||||
synchronized (invitationsListeners) {
|
|
||||||
if (invitationsListeners.contains(listener)) {
|
|
||||||
invitationsListeners.remove(listener);
|
|
||||||
}
|
|
||||||
// If there are no more listeners to notifiy for room invitations
|
|
||||||
// then proceed to cancel/release this monitor
|
|
||||||
if (invitationsListeners.size() == 0) {
|
|
||||||
cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fires invitation listeners.
|
|
||||||
*/
|
|
||||||
private void fireInvitationListeners(String room, String inviter, String reason, String password,
|
|
||||||
Message message) {
|
|
||||||
InvitationListener[] listeners;
|
|
||||||
synchronized (invitationsListeners) {
|
|
||||||
listeners = new InvitationListener[invitationsListeners.size()];
|
|
||||||
invitationsListeners.toArray(listeners);
|
|
||||||
}
|
|
||||||
for (InvitationListener listener : listeners) {
|
|
||||||
listener.invitationReceived(connection, room, inviter, reason, password, message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void connectionClosed() {
|
|
||||||
cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the listeners to detect received room invitations and to detect when the
|
|
||||||
* connection gets closed. As soon as a room invitation is received the invitations
|
|
||||||
* listeners will be fired. When the connection gets closed the monitor will remove
|
|
||||||
* his listeners on the connection.
|
|
||||||
*/
|
|
||||||
private void init() {
|
|
||||||
// Listens for all messages that include a MUCUser extension and fire the invitation
|
// Listens for all messages that include a MUCUser extension and fire the invitation
|
||||||
// listeners if the message includes an invitation.
|
// listeners if the message includes an invitation.
|
||||||
invitationFilter =
|
|
||||||
new PacketExtensionFilter("x", "http://jabber.org/protocol/muc#user");
|
|
||||||
invitationPacketListener = new PacketListener() {
|
invitationPacketListener = new PacketListener() {
|
||||||
public void processPacket(Packet packet) {
|
public void processPacket(Packet packet) {
|
||||||
// Get the MUCUser extension
|
// Get the MUCUser extension
|
||||||
|
@ -2483,18 +2394,46 @@ public class MultiUserChat {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
connection.addPacketListener(invitationPacketListener, invitationFilter);
|
connection.addPacketListener(invitationPacketListener, invitationFilter);
|
||||||
// Add a listener to detect when the connection gets closed in order to
|
|
||||||
// cancel/release this monitor
|
|
||||||
connection.addConnectionListener(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancels all the listeners that this InvitationsMonitor has added to the connection.
|
* Adds a listener to invitation notifications. The listener will be fired anytime
|
||||||
|
* an invitation is received.
|
||||||
|
*
|
||||||
|
* @param listener an invitation listener.
|
||||||
*/
|
*/
|
||||||
private void cancel() {
|
public void addInvitationListener(InvitationListener listener) {
|
||||||
connection.removePacketListener(invitationPacketListener);
|
synchronized (invitationsListeners) {
|
||||||
connection.removeConnectionListener(this);
|
if (!invitationsListeners.contains(listener)) {
|
||||||
|
invitationsListeners.add(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a listener to invitation notifications. The listener will be fired anytime
|
||||||
|
* an invitation is received.
|
||||||
|
*
|
||||||
|
* @param listener an invitation listener.
|
||||||
|
*/
|
||||||
|
public void removeInvitationListener(InvitationListener listener) {
|
||||||
|
synchronized (invitationsListeners) {
|
||||||
|
if (invitationsListeners.contains(listener)) {
|
||||||
|
invitationsListeners.remove(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires invitation listeners.
|
||||||
|
*/
|
||||||
|
private void fireInvitationListeners(String room, String inviter, String reason, String password,
|
||||||
|
Message message) {
|
||||||
|
synchronized (invitationsListeners) {
|
||||||
|
for (InvitationListener listener : invitationsListeners) {
|
||||||
|
listener.invitationReceived(connection(), room, inviter, reason, password, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue