1 /** 2 * $RCSfile$ 3 * $Revision: 2779 $ 4 * $Date: 2005-09-05 17:00:45 -0300 (Mon, 05 Sep 2005) $ 5 * 6 * Copyright 2003-2006 Jive Software. 7 * 8 * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 21 package org.jivesoftware.smackx.muc; 22 23 import org.jivesoftware.smack.ConnectionListener; 24 import org.jivesoftware.smack.PacketListener; 25 import org.jivesoftware.smack.Connection; 26 import org.jivesoftware.smack.filter.PacketFilter; 27 import org.jivesoftware.smack.packet.Packet; 28 import org.jivesoftware.smack.util.StringUtils; 29 30 import java.lang.ref.WeakReference; 31 import java.util.Map; 32 import java.util.WeakHashMap; 33 import java.util.concurrent.ConcurrentHashMap; 34 35 /** 36 * A <code>RoomListenerMultiplexor</code> multiplexes incoming packets on 37 * a <code>Connection</code> using a single listener/filter pair. 38 * A single <code>RoomListenerMultiplexor</code> is created for each 39 * {@link org.jivesoftware.smack.Connection} that has joined MUC rooms 40 * within its session. 41 * 42 * @author Larry Kirschner 43 */ 44 class RoomListenerMultiplexor implements ConnectionListener { 45 46 // We use a WeakHashMap so that the GC can collect the monitor when the 47 // connection is no longer referenced by any object. 48 private static final Map<Connection, WeakReference<RoomListenerMultiplexor>> monitors = 49 new WeakHashMap<Connection, WeakReference<RoomListenerMultiplexor>>(); 50 51 private Connection connection; 52 private RoomMultiplexFilter filter; 53 private RoomMultiplexListener listener; 54 55 /** 56 * Returns a new or existing RoomListenerMultiplexor for a given connection. 57 * 58 * @param conn the connection to monitor for room invitations. 59 * @return a new or existing RoomListenerMultiplexor for a given connection. 60 */ 61 public static RoomListenerMultiplexor getRoomMultiplexor(Connection conn) { 62 synchronized (monitors) { 63 if (!monitors.containsKey(conn) || monitors.get(conn).get() == null) { 64 RoomListenerMultiplexor rm = new RoomListenerMultiplexor(conn, new RoomMultiplexFilter(), 65 new RoomMultiplexListener()); 66 67 rm.init(); 68 69 // We need to use a WeakReference because the monitor references the 70 // connection and this could prevent the GC from collecting the monitor 71 // when no other object references the monitor 72 monitors.put(conn, new WeakReference<RoomListenerMultiplexor>(rm)); 73 } 74 // Return the InvitationsMonitor that monitors the connection 75 return monitors.get(conn).get(); 76 } 77 } 78 79 /** 80 * All access should be through 81 * the static method {@link #getRoomMultiplexor(Connection)}. 82 */ 83 private RoomListenerMultiplexor(Connection connection, RoomMultiplexFilter filter, 84 RoomMultiplexListener listener) { 85 if (connection == null) { 86 throw new IllegalArgumentException("Connection is null"); 87 } 88 if (filter == null) { 89 throw new IllegalArgumentException("Filter is null"); 90 } 91 if (listener == null) { 92 throw new IllegalArgumentException("Listener is null"); 93 } 94 this.connection = connection; 95 this.filter = filter; 96 this.listener = listener; 97 } 98 99 public void addRoom(String address, PacketMultiplexListener roomListener) { 100 filter.addRoom(address); 101 listener.addRoom(address, roomListener); 102 } 103 104 public void connectionClosed() { 105 cancel(); 106 } 107 108 public void connectionClosedOnError(Exception e) { 109 cancel(); 110 } 111 112 public void reconnectingIn(int seconds) { 113 // ignore 114 } 115 116 public void reconnectionSuccessful() { 117 // ignore 118 } 119 120 public void reconnectionFailed(Exception e) { 121 // ignore 122 } 123 124 /** 125 * Initializes the listeners to detect received room invitations and to detect when the 126 * connection gets closed. As soon as a room invitation is received the invitations 127 * listeners will be fired. When the connection gets closed the monitor will remove 128 * his listeners on the connection. 129 */ 130 public void init() { 131 connection.addConnectionListener(this); 132 connection.addPacketListener(listener, filter); 133 } 134 135 public void removeRoom(String address) { 136 filter.removeRoom(address); 137 listener.removeRoom(address); 138 } 139 140 /** 141 * Cancels all the listeners that this InvitationsMonitor has added to the connection. 142 */ 143 private void cancel() { 144 connection.removeConnectionListener(this); 145 connection.removePacketListener(listener); 146 } 147 148 /** 149 * The single <code>Connection</code>-level <code>PacketFilter</code> used by a {@link RoomListenerMultiplexor} 150 * for all muc chat rooms on an <code>Connection</code>. 151 * Each time a muc chat room is added to/removed from an 152 * <code>Connection</code> the address for that chat room 153 * is added to/removed from that <code>Connection</code>'s 154 * <code>RoomMultiplexFilter</code>. 155 */ 156 private static class RoomMultiplexFilter implements PacketFilter { 157 158 private Map<String, String> roomAddressTable = new ConcurrentHashMap<String, String>(); 159 160 public boolean accept(Packet p) { 161 String from = p.getFrom(); 162 if (from == null) { 163 return false; 164 } 165 return roomAddressTable.containsKey(StringUtils.parseBareAddress(from).toLowerCase()); 166 } 167 168 public void addRoom(String address) { 169 if (address == null) { 170 return; 171 } 172 roomAddressTable.put(address.toLowerCase(), address); 173 } 174 175 public void removeRoom(String address) { 176 if (address == null) { 177 return; 178 } 179 roomAddressTable.remove(address.toLowerCase()); 180 } 181 } 182 183 /** 184 * The single <code>Connection</code>-level <code>PacketListener</code> 185 * used by a {@link RoomListenerMultiplexor} 186 * for all muc chat rooms on an <code>Connection</code>. 187 * Each time a muc chat room is added to/removed from an 188 * <code>Connection</code> the address and listener for that chat room 189 * are added to/removed from that <code>Connection</code>'s 190 * <code>RoomMultiplexListener</code>. 191 * 192 * @author Larry Kirschner 193 */ 194 private static class RoomMultiplexListener implements PacketListener { 195 196 private Map<String, PacketMultiplexListener> roomListenersByAddress = 197 new ConcurrentHashMap<String, PacketMultiplexListener>(); 198 199 public void processPacket(Packet p) { 200 String from = p.getFrom(); 201 if (from == null) { 202 return; 203 } 204 205 PacketMultiplexListener listener = 206 roomListenersByAddress.get(StringUtils.parseBareAddress(from).toLowerCase()); 207 208 if (listener != null) { 209 listener.processPacket(p); 210 } 211 } 212 213 public void addRoom(String address, PacketMultiplexListener listener) { 214 if (address == null) { 215 return; 216 } 217 roomListenersByAddress.put(address.toLowerCase(), listener); 218 } 219 220 public void removeRoom(String address) { 221 if (address == null) { 222 return; 223 } 224 roomListenersByAddress.remove(address.toLowerCase()); 225 } 226 } 227 } 228