mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-11-26 16:22:06 +01:00
Initial version. SMACK-155
git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@4537 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
parent
494cec8f9a
commit
14b50d790a
3 changed files with 446 additions and 0 deletions
|
@ -0,0 +1,135 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision: 2779 $
|
||||
* $Date: 2005-09-05 17:00:45 -0300 (Mon, 05 Sep 2005) $
|
||||
*
|
||||
* Copyright 2003-2006 Jive Software.
|
||||
*
|
||||
* All rights reserved. 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.packet.Packet;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* A variant of the {@link org.jivesoftware.smack.PacketCollector} class
|
||||
* that does not force attachment to an <code>XMPPConnection</code>
|
||||
* on creation and no filter is required. Used to collect message
|
||||
* packets targeted to a group chat room.
|
||||
*
|
||||
* @author Larry Kirschner
|
||||
*/
|
||||
class ConnectionDetachedPacketCollector {
|
||||
/**
|
||||
* Max number of packets that any one collector can hold. After the max is
|
||||
* reached, older packets will be automatically dropped from the queue as
|
||||
* new packets are added.
|
||||
*/
|
||||
private static final int MAX_PACKETS = 65536;
|
||||
|
||||
private LinkedList<Packet> resultQueue;
|
||||
|
||||
/**
|
||||
* Creates a new packet collector. If the packet filter is <tt>null</tt>, then
|
||||
* all packets will match this collector.
|
||||
*/
|
||||
public ConnectionDetachedPacketCollector() {
|
||||
this.resultQueue = new LinkedList<Packet>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls to see if a packet is currently available and returns it, or
|
||||
* immediately returns <tt>null</tt> if no packets are currently in the
|
||||
* result queue.
|
||||
*
|
||||
* @return the next packet result, or <tt>null</tt> if there are no more
|
||||
* results.
|
||||
*/
|
||||
public synchronized Packet pollResult() {
|
||||
if (resultQueue.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return resultQueue.removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next available packet. The method call will block (not return)
|
||||
* until a packet is available.
|
||||
*
|
||||
* @return the next available packet.
|
||||
*/
|
||||
public synchronized Packet nextResult() {
|
||||
// Wait indefinitely until there is a result to return.
|
||||
while (resultQueue.isEmpty()) {
|
||||
try {
|
||||
wait();
|
||||
}
|
||||
catch (InterruptedException ie) {
|
||||
// Ignore.
|
||||
}
|
||||
}
|
||||
return resultQueue.removeLast();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next available packet. The method call will block (not return)
|
||||
* until a packet is available or the <tt>timeout</tt> has elapased. If the
|
||||
* timeout elapses without a result, <tt>null</tt> will be returned.
|
||||
*
|
||||
* @param timeout the amount of time to wait for the next packet (in milleseconds).
|
||||
* @return the next available packet.
|
||||
*/
|
||||
public synchronized Packet nextResult(long timeout) {
|
||||
// Wait up to the specified amount of time for a result.
|
||||
if (resultQueue.isEmpty()) {
|
||||
try {
|
||||
wait(timeout);
|
||||
}
|
||||
catch (InterruptedException ie) {
|
||||
// Ignore.
|
||||
}
|
||||
}
|
||||
// If still no result, return null.
|
||||
if (resultQueue.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return resultQueue.removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a packet to see if it meets the criteria for this packet collector.
|
||||
* If so, the packet is added to the result queue.
|
||||
*
|
||||
* @param packet the packet to process.
|
||||
*/
|
||||
protected synchronized void processPacket(Packet packet) {
|
||||
if (packet == null) {
|
||||
return;
|
||||
}
|
||||
// If the max number of packets has been reached, remove the oldest one.
|
||||
if (resultQueue.size() == MAX_PACKETS) {
|
||||
resultQueue.removeLast();
|
||||
}
|
||||
// Add the new packet.
|
||||
resultQueue.addFirst(packet);
|
||||
// Notify waiting threads a result is available.
|
||||
notifyAll();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision: 2779 $
|
||||
* $Date: 2005-09-05 17:00:45 -0300 (Mon, 05 Sep 2005) $
|
||||
*
|
||||
* Copyright 2003-2006 Jive Software.
|
||||
*
|
||||
* All rights reserved. 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.PacketListener;
|
||||
import org.jivesoftware.smack.filter.MessageTypeFilter;
|
||||
import org.jivesoftware.smack.filter.PacketExtensionFilter;
|
||||
import org.jivesoftware.smack.filter.PacketFilter;
|
||||
import org.jivesoftware.smack.filter.PacketTypeFilter;
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
import org.jivesoftware.smack.packet.Presence;
|
||||
|
||||
/**
|
||||
* The single <code>PacketListener</code> used by each {@link MultiplexedMultiUserChat}
|
||||
* for all basic processing of presence, and message packets targeted to that chat.
|
||||
*
|
||||
* @author Larry Kirschner
|
||||
*/
|
||||
class PacketMultiplexListener implements PacketListener {
|
||||
|
||||
private static final PacketFilter MESSAGE_FILTER =
|
||||
new MessageTypeFilter(Message.Type.GROUP_CHAT);
|
||||
private static final PacketFilter PRESENCE_FILTER = new PacketTypeFilter(Presence.class);
|
||||
private static final PacketFilter SUBJECT_FILTER = new PacketFilter() {
|
||||
public boolean accept(Packet packet) {
|
||||
Message msg = (Message) packet;
|
||||
return msg.getSubject() != null;
|
||||
}
|
||||
};
|
||||
private static final PacketFilter DECLINES_FILTER =
|
||||
new PacketExtensionFilter("x",
|
||||
"http://jabber.org/protocol/muc#user");
|
||||
|
||||
private ConnectionDetachedPacketCollector messageCollector;
|
||||
private PacketListener presenceListener;
|
||||
private PacketListener subjectListener;
|
||||
private PacketListener declinesListener;
|
||||
|
||||
public PacketMultiplexListener(
|
||||
ConnectionDetachedPacketCollector messageCollector,
|
||||
PacketListener presenceListener,
|
||||
PacketListener subjectListener, PacketListener declinesListener) {
|
||||
if (messageCollector == null) {
|
||||
throw new IllegalArgumentException("MessageCollector is null");
|
||||
}
|
||||
if (presenceListener == null) {
|
||||
throw new IllegalArgumentException("Presence listener is null");
|
||||
}
|
||||
if (subjectListener == null) {
|
||||
throw new IllegalArgumentException("Subject listener is null");
|
||||
}
|
||||
if (declinesListener == null) {
|
||||
throw new IllegalArgumentException("Declines listener is null");
|
||||
}
|
||||
this.messageCollector = messageCollector;
|
||||
this.presenceListener = presenceListener;
|
||||
this.subjectListener = subjectListener;
|
||||
this.declinesListener = declinesListener;
|
||||
}
|
||||
|
||||
public void processPacket(Packet p) {
|
||||
if (PRESENCE_FILTER.accept(p)) {
|
||||
presenceListener.processPacket(p);
|
||||
}
|
||||
else if (MESSAGE_FILTER.accept(p)) {
|
||||
messageCollector.processPacket(p);
|
||||
|
||||
if (SUBJECT_FILTER.accept(p)) {
|
||||
subjectListener.processPacket(p);
|
||||
}
|
||||
}
|
||||
else if (DECLINES_FILTER.accept(p)) {
|
||||
declinesListener.processPacket(p);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
215
source/org/jivesoftware/smackx/muc/RoomListenerMultiplexor.java
Normal file
215
source/org/jivesoftware/smackx/muc/RoomListenerMultiplexor.java
Normal file
|
@ -0,0 +1,215 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision: 2779 $
|
||||
* $Date: 2005-09-05 17:00:45 -0300 (Mon, 05 Sep 2005) $
|
||||
*
|
||||
* Copyright 2003-2006 Jive Software.
|
||||
*
|
||||
* All rights reserved. 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.ConnectionListener;
|
||||
import org.jivesoftware.smack.PacketListener;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.filter.PacketFilter;
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* A <code>RoomListenerMultiplexor</code> multiplexes incoming packets on
|
||||
* an <code>XMPPConnection</code> using a single listener/filter pair.
|
||||
* A single <code>RoomListenerMultiplexor</code> is created for each
|
||||
* {@link org.jivesoftware.smack.XMPPConnection} that has joined MUC rooms
|
||||
* within its session.
|
||||
*
|
||||
* @author Larry Kirschner
|
||||
*/
|
||||
class RoomListenerMultiplexor implements ConnectionListener {
|
||||
|
||||
// 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<XMPPConnection, WeakReference<RoomListenerMultiplexor>> monitors =
|
||||
new WeakHashMap<XMPPConnection, WeakReference<RoomListenerMultiplexor>>();
|
||||
|
||||
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)) {
|
||||
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<RoomListenerMultiplexor>(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("Connection 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);
|
||||
}
|
||||
|
||||
public void connectionClosed() {
|
||||
cancel();
|
||||
}
|
||||
|
||||
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 <code>XMPPConnection</code>-level <code>PacketFilter</code> used by a {@link RoomListenerMultiplexor}
|
||||
* for all muc chat rooms on an <code>XMPPConnection</code>.
|
||||
* Each time a muc chat room is added to/removed from an
|
||||
* <code>XMPPConnection</code> the address for that chat room
|
||||
* is added to/removed from that <code>XMPPConnection</code>'s
|
||||
* <code>RoomMultiplexFilter</code>.
|
||||
*/
|
||||
private static class RoomMultiplexFilter implements PacketFilter {
|
||||
|
||||
private Map<String, String> roomAddressTable = new ConcurrentHashMap<String, String>();
|
||||
|
||||
public boolean accept(Packet p) {
|
||||
String from = p.getFrom();
|
||||
if (from == null) {
|
||||
return false;
|
||||
}
|
||||
return roomAddressTable.containsKey(StringUtils.parseBareAddress(from).toLowerCase());
|
||||
}
|
||||
|
||||
public void addRoom(String address) {
|
||||
if (address == null) {
|
||||
return;
|
||||
}
|
||||
roomAddressTable.put(address.toLowerCase(), address);
|
||||
}
|
||||
|
||||
public void removeRoom(String address) {
|
||||
if (address == null) {
|
||||
return;
|
||||
}
|
||||
roomAddressTable.remove(address.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The single <code>XMPPConnection</code>-level <code>PacketListener</code>
|
||||
* used by a {@link RoomListenerMultiplexor}
|
||||
* for all muc chat rooms on an <code>XMPPConnection</code>.
|
||||
* Each time a muc chat room is added to/removed from an
|
||||
* <code>XMPPConnection</code> the address and listener for that chat room
|
||||
* are added to/removed from that <code>XMPPConnection</code>'s
|
||||
* <code>RoomMultiplexListener</code>.
|
||||
*
|
||||
* @author Larry Kirschner
|
||||
*/
|
||||
private static class RoomMultiplexListener implements PacketListener {
|
||||
|
||||
private Map<String, PacketMultiplexListener> roomListenersByAddress =
|
||||
new ConcurrentHashMap<String, PacketMultiplexListener>();
|
||||
|
||||
public void processPacket(Packet p) {
|
||||
String from = p.getFrom();
|
||||
if (from == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
PacketMultiplexListener listener =
|
||||
roomListenersByAddress.get(StringUtils.parseBareAddress(from).toLowerCase());
|
||||
|
||||
if (listener != null) {
|
||||
listener.processPacket(p);
|
||||
}
|
||||
}
|
||||
|
||||
public void addRoom(String address, PacketMultiplexListener listener) {
|
||||
if (address == null) {
|
||||
return;
|
||||
}
|
||||
roomListenersByAddress.put(address.toLowerCase(), listener);
|
||||
}
|
||||
|
||||
public void removeRoom(String address) {
|
||||
if (address == null) {
|
||||
return;
|
||||
}
|
||||
roomListenersByAddress.remove(address.toLowerCase());
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue