mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-11-22 14:22:05 +01:00
Reworked (send|recv)Listeners, ListenerWrapper
instead of rely on ListenerWrapper checking the PacketFilter *and* invoking the PacketListener we now use two for-each loops, where the first filters the PacketListeners that should get invoked and use the second for-each loop to actually invoke the PacketListener. Before, the code was not thread safe if a PacketListener would remove itself from the (send|recv)Listeners. Also make packet(Listener|Filter) in ListenerWrapper final.
This commit is contained in:
parent
03686fbaaf
commit
15d59299a2
2 changed files with 61 additions and 54 deletions
|
@ -21,11 +21,12 @@ import java.io.Reader;
|
|||
import java.io.Writer;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
|
@ -107,14 +108,14 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
/**
|
||||
* List of PacketListeners that will be notified when a new packet was received.
|
||||
*/
|
||||
protected final Map<PacketListener, ListenerWrapper> recvListeners =
|
||||
new ConcurrentHashMap<PacketListener, ListenerWrapper>();
|
||||
private final Map<PacketListener, ListenerWrapper> recvListeners =
|
||||
new HashMap<PacketListener, ListenerWrapper>();
|
||||
|
||||
/**
|
||||
* List of PacketListeners that will be notified when a new packet was sent.
|
||||
*/
|
||||
protected final Map<PacketListener, ListenerWrapper> sendListeners =
|
||||
new ConcurrentHashMap<PacketListener, ListenerWrapper>();
|
||||
private final Map<PacketListener, ListenerWrapper> sendListeners =
|
||||
new HashMap<PacketListener, ListenerWrapper>();
|
||||
|
||||
/**
|
||||
* List of PacketInterceptors that will be notified when a new packet is about to be
|
||||
|
@ -672,12 +673,16 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
throw new NullPointerException("Packet listener is null.");
|
||||
}
|
||||
ListenerWrapper wrapper = new ListenerWrapper(packetListener, packetFilter);
|
||||
recvListeners.put(packetListener, wrapper);
|
||||
synchronized (recvListeners) {
|
||||
recvListeners.put(packetListener, wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removePacketListener(PacketListener packetListener) {
|
||||
return recvListeners.remove(packetListener) != null;
|
||||
synchronized (recvListeners) {
|
||||
return recvListeners.remove(packetListener) != null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -686,34 +691,35 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
throw new NullPointerException("Packet listener is null.");
|
||||
}
|
||||
ListenerWrapper wrapper = new ListenerWrapper(packetListener, packetFilter);
|
||||
sendListeners.put(packetListener, wrapper);
|
||||
synchronized (sendListeners) {
|
||||
sendListeners.put(packetListener, wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePacketSendingListener(PacketListener packetListener) {
|
||||
sendListeners.remove(packetListener);
|
||||
synchronized (sendListeners) {
|
||||
sendListeners.remove(packetListener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a map of all packet listeners for sending packets of this connection.
|
||||
*
|
||||
* @return a map of all packet listeners for sent packets.
|
||||
*/
|
||||
protected Map<PacketListener, ListenerWrapper> getPacketSendingListeners() {
|
||||
return sendListeners;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process all packet listeners for sending packets.
|
||||
*
|
||||
* @param packet the packet to process.
|
||||
*/
|
||||
private void firePacketSendingListeners(Packet packet) {
|
||||
// Notify the listeners of the new sent packet
|
||||
for (ListenerWrapper listenerWrapper : sendListeners.values()) {
|
||||
List<PacketListener> listenersToNotify = new LinkedList<PacketListener>();
|
||||
synchronized (sendListeners) {
|
||||
for (ListenerWrapper listenerWrapper : sendListeners.values()) {
|
||||
if (listenerWrapper.filterMatches(packet)) {
|
||||
listenersToNotify.add(listenerWrapper.getListener());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (PacketListener listener : listenersToNotify) {
|
||||
try {
|
||||
listenerWrapper.notifyListener(packet);
|
||||
listener.processPacket(packet);
|
||||
}
|
||||
catch (NotConnectedException e) {
|
||||
LOGGER.log(Level.WARNING, "Got not connected exception, aborting");
|
||||
|
@ -819,28 +825,41 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
executorService.submit(new ListenerNotification(packet));
|
||||
}
|
||||
|
||||
protected void notifiyReceivedListeners(Packet packet) {
|
||||
List<PacketListener> listenersToNotify = new LinkedList<PacketListener>();
|
||||
synchronized (recvListeners) {
|
||||
for (ListenerWrapper listenerWrapper : recvListeners.values()) {
|
||||
if (listenerWrapper.filterMatches(packet)) {
|
||||
listenersToNotify.add(listenerWrapper.getListener());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (PacketListener listener : listenersToNotify) {
|
||||
try {
|
||||
listener.processPacket(packet);
|
||||
} catch(NotConnectedException e) {
|
||||
LOGGER.log(Level.WARNING, "Got not connected exception, aborting", e);
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
LOGGER.log(Level.SEVERE, "Exception in packet listener", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A runnable to notify all listeners of a packet.
|
||||
*/
|
||||
private class ListenerNotification implements Runnable {
|
||||
|
||||
private Packet packet;
|
||||
private final Packet packet;
|
||||
|
||||
public ListenerNotification(Packet packet) {
|
||||
this.packet = packet;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
for (ListenerWrapper listenerWrapper : recvListeners.values()) {
|
||||
try {
|
||||
listenerWrapper.notifyListener(packet);
|
||||
} catch(NotConnectedException e) {
|
||||
LOGGER.log(Level.WARNING, "Got not connected exception, aborting", e);
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
LOGGER.log(Level.SEVERE, "Exception in packet listener", e);
|
||||
}
|
||||
}
|
||||
notifiyReceivedListeners(packet);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -901,8 +920,8 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
*/
|
||||
protected static class ListenerWrapper {
|
||||
|
||||
private PacketListener packetListener;
|
||||
private PacketFilter packetFilter;
|
||||
private final PacketListener packetListener;
|
||||
private final PacketFilter packetFilter;
|
||||
|
||||
/**
|
||||
* Create a class which associates a packet filter with a listener.
|
||||
|
@ -915,16 +934,12 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
this.packetFilter = packetFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify and process the packet listener if the filter matches the packet.
|
||||
*
|
||||
* @param packet the packet which was sent or received.
|
||||
* @throws NotConnectedException
|
||||
*/
|
||||
public void notifyListener(Packet packet) throws NotConnectedException {
|
||||
if (packetFilter == null || packetFilter.accept(packet)) {
|
||||
packetListener.processPacket(packet);
|
||||
}
|
||||
public boolean filterMatches(Packet packet) {
|
||||
return packetFilter == null || packetFilter.accept(packet);
|
||||
}
|
||||
|
||||
public PacketListener getListener() {
|
||||
return packetListener;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@ import java.util.concurrent.BlockingQueue;
|
|||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.packet.Element;
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
import org.jivesoftware.smack.packet.PlainStreamElement;
|
||||
|
@ -251,13 +250,6 @@ public class DummyConnection extends AbstractXMPPConnection {
|
|||
}
|
||||
|
||||
// Deliver the incoming packet to listeners.
|
||||
for (ListenerWrapper listenerWrapper : recvListeners.values()) {
|
||||
try {
|
||||
listenerWrapper.notifyListener(packet);
|
||||
}
|
||||
catch (NotConnectedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
notifiyReceivedListeners(packet);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue