Home | History | Annotate | Download | only in muc
      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