1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-12-23 02:58:00 +01:00

Apply Manager pattern to InvitationsMonitor

Don't remove listeners on disconnect (SMACK-571).
This commit is contained in:
Florian Schmaus 2014-10-05 11:58:42 +02:00
parent a0fe337bcd
commit f7517ab6cc

View file

@ -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);
}
}
}
} }
} }