1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-06-27 13:54:50 +02:00
Smack/extensions/src/main/java/org/jivesoftware/smackx/muc/RoomListenerMultiplexor.java
Florian Schmaus fcc8414a92 "not connected" is now a checked Exception thrown by sendPacket()
There is a unsolveable race condition between the connection state and
sendPacket(), i.e. the connection could go down, right after the
method calling sendPacket is called, but before sendPacket() is
invoked. Before this change, sendPacket() has thrown an unchecked
IllegalStateException, which could be ignored by the Smack user, who
would also not notice the race condition. We have decided to throw a
checked Exception in this case now, to make the Smack user aware of
this situation.

SMACK-426
2014-03-19 15:56:41 +01:00

216 lines
7.7 KiB
Java

/**
*
* Copyright 2003-2006 Jive Software.
*
* 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;
import org.jivesoftware.smack.AbstractConnectionListener;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.util.StringUtils;
import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
/**
* A <code>RoomListenerMultiplexor</code> multiplexes incoming packets on
* a <code>XMPPConnection</code> using a single listener/filter pair.
* A single <code>RoomListenerMultiplexor</code> is created for each
* {@link org.jivesoftware.smack.XMPPConnection} that has joined MUC rooms
* within its session.
*
* @author Larry Kirschner
*/
class RoomListenerMultiplexor extends AbstractConnectionListener {
// We use a WeakHashMap so that the GC can collect the monitor when the
// connection is no longer referenced by any object.
private static final Map<XMPPConnection, WeakReference<RoomListenerMultiplexor>> monitors =
new WeakHashMap<XMPPConnection, WeakReference<RoomListenerMultiplexor>>();
private XMPPConnection connection;
private RoomMultiplexFilter filter;
private RoomMultiplexListener listener;
/**
* Returns a new or existing RoomListenerMultiplexor for a given connection.
*
* @param conn the connection to monitor for room invitations.
* @return a new or existing RoomListenerMultiplexor for a given connection.
*/
public static RoomListenerMultiplexor getRoomMultiplexor(XMPPConnection conn) {
synchronized (monitors) {
if (!monitors.containsKey(conn) || monitors.get(conn).get() == null) {
RoomListenerMultiplexor rm = new RoomListenerMultiplexor(conn, new RoomMultiplexFilter(),
new RoomMultiplexListener());
rm.init();
// We need to use a WeakReference because the monitor references the
// connection and this could prevent the GC from collecting the monitor
// when no other object references the monitor
monitors.put(conn, new WeakReference<RoomListenerMultiplexor>(rm));
}
// Return the InvitationsMonitor that monitors the connection
return monitors.get(conn).get();
}
}
/**
* All access should be through
* the static method {@link #getRoomMultiplexor(XMPPConnection)}.
*/
private RoomListenerMultiplexor(XMPPConnection connection, RoomMultiplexFilter filter,
RoomMultiplexListener listener) {
if (connection == null) {
throw new IllegalArgumentException("XMPPConnection is null");
}
if (filter == null) {
throw new IllegalArgumentException("Filter is null");
}
if (listener == null) {
throw new IllegalArgumentException("Listener is null");
}
this.connection = connection;
this.filter = filter;
this.listener = listener;
}
public void addRoom(String address, PacketMultiplexListener roomListener) {
filter.addRoom(address);
listener.addRoom(address, roomListener);
}
@Override
public void connectionClosed() {
cancel();
}
@Override
public void connectionClosedOnError(Exception e) {
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.
*/
public void init() {
connection.addConnectionListener(this);
connection.addPacketListener(listener, filter);
}
public void removeRoom(String address) {
filter.removeRoom(address);
listener.removeRoom(address);
}
/**
* Cancels all the listeners that this InvitationsMonitor has added to the connection.
*/
private void cancel() {
connection.removeConnectionListener(this);
connection.removePacketListener(listener);
}
/**
* The single <code>XMPPConnection</code>-level <code>PacketFilter</code> used by a {@link RoomListenerMultiplexor}
* for all muc chat rooms on an <code>XMPPConnection</code>.
* Each time a muc chat room is added to/removed from an
* <code>XMPPConnection</code> the address for that chat room
* is added to/removed from that <code>XMPPConnection</code>'s
* <code>RoomMultiplexFilter</code>.
*/
private static class RoomMultiplexFilter implements PacketFilter {
private Map<String, String> roomAddressTable = new ConcurrentHashMap<String, String>();
public boolean accept(Packet p) {
String from = p.getFrom();
if (from == null) {
return false;
}
return roomAddressTable.containsKey(StringUtils.parseBareAddress(from).toLowerCase());
}
public void addRoom(String address) {
if (address == null) {
return;
}
roomAddressTable.put(address.toLowerCase(), address);
}
public void removeRoom(String address) {
if (address == null) {
return;
}
roomAddressTable.remove(address.toLowerCase());
}
}
/**
* The single <code>XMPPConnection</code>-level <code>PacketListener</code>
* used by a {@link RoomListenerMultiplexor}
* for all muc chat rooms on an <code>XMPPConnection</code>.
* Each time a muc chat room is added to/removed from an
* <code>XMPPConnection</code> the address and listener for that chat room
* are added to/removed from that <code>XMPPConnection</code>'s
* <code>RoomMultiplexListener</code>.
*
* @author Larry Kirschner
*/
private static class RoomMultiplexListener implements PacketListener {
private Map<String, PacketMultiplexListener> roomListenersByAddress =
new ConcurrentHashMap<String, PacketMultiplexListener>();
public void processPacket(Packet p) throws NotConnectedException {
String from = p.getFrom();
if (from == null) {
return;
}
PacketMultiplexListener listener =
roomListenersByAddress.get(StringUtils.parseBareAddress(from).toLowerCase());
if (listener != null) {
listener.processPacket(p);
}
}
public void addRoom(String address, PacketMultiplexListener listener) {
if (address == null) {
return;
}
roomListenersByAddress.put(address.toLowerCase(), listener);
}
public void removeRoom(String address) {
if (address == null) {
return;
}
roomListenersByAddress.remove(address.toLowerCase());
}
}
}