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