/** * * Copyright 2003-2006 Jive Software. * * 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.AbstractConnectionListener; import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.filter.PacketFilter; import org.jivesoftware.smack.packet.Packet; import org.jxmpp.util.XmppStringUtils; import java.lang.ref.WeakReference; import java.util.Locale; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; /** * A RoomListenerMultiplexor multiplexes incoming packets on * a XMPPConnection using a single listener/filter pair. * A single RoomListenerMultiplexor is created for each * {@link org.jivesoftware.smack.XMPPConnection} that has joined MUC rooms * within its session. * * @author Larry Kirschner */ class RoomListenerMultiplexor extends AbstractConnectionListener { // 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> monitors = new WeakHashMap>(); private XMPPConnection 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(XMPPConnection 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(rm)); } // Return the InvitationsMonitor that monitors the connection return monitors.get(conn).get(); } } /** * All access should be through * the static method {@link #getRoomMultiplexor(XMPPConnection)}. */ private RoomListenerMultiplexor(XMPPConnection connection, RoomMultiplexFilter filter, RoomMultiplexListener listener) { if (connection == null) { throw new IllegalArgumentException("XMPPConnection 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); } @Override public void connectionClosed() { cancel(); } @Override public void connectionClosedOnError(Exception e) { cancel(); } /** * 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 XMPPConnection-level PacketFilter used by a {@link RoomListenerMultiplexor} * for all muc chat rooms on an XMPPConnection. * Each time a muc chat room is added to/removed from an * XMPPConnection the address for that chat room * is added to/removed from that XMPPConnection's * RoomMultiplexFilter. */ private static class RoomMultiplexFilter implements PacketFilter { private Map roomAddressTable = new ConcurrentHashMap(); public boolean accept(Packet p) { String from = p.getFrom(); if (from == null) { return false; } return roomAddressTable.containsKey(XmppStringUtils.parseBareAddress(from).toLowerCase(Locale.US)); } public void addRoom(String address) { if (address == null) { return; } roomAddressTable.put(address.toLowerCase(Locale.US), address); } public void removeRoom(String address) { if (address == null) { return; } roomAddressTable.remove(address.toLowerCase(Locale.US)); } } /** * The single XMPPConnection-level PacketListener * used by a {@link RoomListenerMultiplexor} * for all muc chat rooms on an XMPPConnection. * Each time a muc chat room is added to/removed from an * XMPPConnection the address and listener for that chat room * are added to/removed from that XMPPConnection's * RoomMultiplexListener. * * @author Larry Kirschner */ private static class RoomMultiplexListener implements PacketListener { private Map roomListenersByAddress = new ConcurrentHashMap(); public void processPacket(Packet p) throws NotConnectedException { String from = p.getFrom(); if (from == null) { return; } PacketMultiplexListener listener = roomListenersByAddress.get(XmppStringUtils.parseBareAddress(from).toLowerCase(Locale.US)); if (listener != null) { listener.processPacket(p); } } public void addRoom(String address, PacketMultiplexListener listener) { if (address == null) { return; } roomListenersByAddress.put(address.toLowerCase(Locale.US), listener); } public void removeRoom(String address) { if (address == null) { return; } roomListenersByAddress.remove(address.toLowerCase(Locale.US)); } } }