blob: 6f8bf05a76a3293efc04d97046cac852220dc946 [file] [log] [blame]
Shuyi Chend7955ce2013-05-22 14:51:55 -07001/**
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
21package org.jivesoftware.smackx.muc;
22
23import org.jivesoftware.smack.ConnectionListener;
24import org.jivesoftware.smack.PacketListener;
25import org.jivesoftware.smack.Connection;
26import org.jivesoftware.smack.filter.PacketFilter;
27import org.jivesoftware.smack.packet.Packet;
28import org.jivesoftware.smack.util.StringUtils;
29
30import java.lang.ref.WeakReference;
31import java.util.Map;
32import java.util.WeakHashMap;
33import 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 */
44class 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}