/** * $RCSfile$ * $Revision: 2779 $ * $Date: 2005-09-05 17:00:45 -0300 (Mon, 05 Sep 2005) $ * * Copyright 2003-2006 Jive Software. * * All rights reserved. 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.ConnectionListener; import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.Connection; 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>Connection</code> using a single listener/filter pair. * A single <code>RoomListenerMultiplexor</code> is created for each * {@link org.jivesoftware.smack.Connection} that has joined MUC rooms * within its session. * * @author Larry Kirschner */ class RoomListenerMultiplexor implements ConnectionListener { // 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<Connection, WeakReference<RoomListenerMultiplexor>> monitors = new WeakHashMap<Connection, WeakReference<RoomListenerMultiplexor>>(); private Connection 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(Connection 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(Connection)}. */ private RoomListenerMultiplexor(Connection connection, RoomMultiplexFilter filter, RoomMultiplexListener listener) { if (connection == null) { throw new IllegalArgumentException("Connection 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); } public void connectionClosed() { cancel(); } public void connectionClosedOnError(Exception e) { cancel(); } public void reconnectingIn(int seconds) { // ignore } public void reconnectionSuccessful() { // ignore } public void reconnectionFailed(Exception e) { // ignore } /** * 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>Connection</code>-level <code>PacketFilter</code> used by a {@link RoomListenerMultiplexor} * for all muc chat rooms on an <code>Connection</code>. * Each time a muc chat room is added to/removed from an * <code>Connection</code> the address for that chat room * is added to/removed from that <code>Connection</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>Connection</code>-level <code>PacketListener</code> * used by a {@link RoomListenerMultiplexor} * for all muc chat rooms on an <code>Connection</code>. * Each time a muc chat room is added to/removed from an * <code>Connection</code> the address and listener for that chat room * are added to/removed from that <code>Connection</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) { 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()); } } }