diff --git a/source/org/jivesoftware/smack/Chat.java b/source/org/jivesoftware/smack/Chat.java index c29832dd9..57e1633e4 100644 --- a/source/org/jivesoftware/smack/Chat.java +++ b/source/org/jivesoftware/smack/Chat.java @@ -24,15 +24,16 @@ import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.filter.*; +import java.util.*; +import java.lang.ref.WeakReference; + /** - * A chat is a series of messages sent between two users. Each chat can have - * a unique thread ID, which is used to track which messages are part of a particular - * conversation.

- * - * In some situations, it is better to have all messages from the other user delivered - * to a Chat rather than just the messages that have a particular thread ID. To - * enable this behavior, call {@link #setFilteredOnThreadID(boolean)} with - * false as the parameter. + * A chat is a series of messages sent between two users. Each chat has a unique + * thread ID, which is used to track which messages are part of a particular + * conversation. Some messages are sent without a thread ID, and some clients + * don't send thread IDs at all. Therefore, if a message without a thread ID + * arrives it is routed to the most recently created Chat with the message + * sender. * * @see XMPPConnection#createChat(String) * @author Matt Tucker @@ -43,13 +44,7 @@ public class Chat { * A prefix helps to make sure that ID's are unique across mutliple instances. */ private static String prefix = StringUtils.randomString(5); - - /** - * True if only messages that have a matching threadID will be delivered to a Chat. When - * false, any message from the other participant will be delivered to a Chat. - */ - private static boolean filteredOnThreadID = true; - + /** * Keeps track of the current increment, which is appended to the prefix to * forum a unique ID. @@ -71,6 +66,7 @@ public class Chat { private String participant; private PacketFilter messageFilter; private PacketCollector messageCollector; + private Set listeners = new HashSet(); /** * Creates a new chat with the specified user. @@ -81,10 +77,6 @@ public class Chat { public Chat(XMPPConnection connection, String participant) { // Automatically assign the next chat ID. this(connection, participant, nextID()); - // If not filtering on thread ID, force the thread ID for this Chat to be null. - if (!filteredOnThreadID) { - this.threadID = null; - } } /** @@ -99,42 +91,17 @@ public class Chat { this.participant = participant; this.threadID = threadID; - if (filteredOnThreadID) { - // Filter the messages whose thread equals Chat's id - messageFilter = new ThreadFilter(threadID); - } - else { - // Filter the messages of type "chat" and sender equals Chat's participant - messageFilter = - new OrFilter( - new AndFilter( - new MessageTypeFilter(Message.Type.CHAT), - new FromContainsFilter(participant)), - new ThreadFilter(threadID)); - } + // Register with the map of chats so that messages with no thread ID + // set will be delivered to this Chat. + connection.chats.put(StringUtils.parseBareAddress(participant), + new WeakReference(this)); + + // Filter the messages whose thread equals Chat's id + messageFilter = new ThreadFilter(threadID); + messageCollector = connection.createPacketCollector(messageFilter); } - /** - * Returns true if only messages that have a matching threadID will be delivered to Chat - * instances. When false, any message from the other participant will be delivered to Chat instances. - * - * @return true if messages delivered to Chat instances are filtered on thread ID. - */ - public static boolean isFilteredOnThreadID() { - return filteredOnThreadID; - } - - /** - * Sets whether only messages that have a matching threadID will be delivered to Chat instances. - * When false, any message from the other participant will be delivered to a Chat instances. - * - * @param value true if messages delivered to Chat instances are filtered on thread ID. - */ - public static void setFilteredOnThreadID(boolean value) { - filteredOnThreadID = value; - } - /** * Returns the thread id associated with this chat, which corresponds to the * thread field of XMPP messages. This method may return null @@ -252,6 +219,41 @@ public class Chat { */ public void addMessageListener(PacketListener listener) { connection.addPacketListener(listener, messageFilter); + // Keep track of the listener so that we can manually deliver extra + // messages to it later if needed. + synchronized (listeners) { + listeners.add(new WeakReference(listener)); + } + } + + /** + * Delivers a message directly to this chat, which will add the message + * to the collector and deliver it to all listeners registered with the + * Chat. This is used by the XMPPConnection class to deliver messages + * without a thread ID. + * + * @param message the message. + */ + void deliver(Message message) { + // Because the collector and listeners are expecting a thread ID with + // a specific value, set the thread ID on the message even though it + // probably never had one. + message.setThread(threadID); + + messageCollector.processPacket(message); + synchronized (listeners) { + for (Iterator i=listeners.iterator(); i.hasNext(); ) { + WeakReference listenerRef = (WeakReference)i.next(); + PacketListener listener; + if ((listener = (PacketListener)listenerRef.get()) != null) { + listener.processPacket(message); + } + // If the reference was cleared, remove it from the set. + else { + i.remove(); + } + } + } } public void finalize() throws Throwable { @@ -261,6 +263,8 @@ public class Chat { messageCollector.cancel(); } } - catch (Exception e) {} + catch (Exception e) { + // Ignore. + } } } \ No newline at end of file diff --git a/source/org/jivesoftware/smack/PacketCollector.java b/source/org/jivesoftware/smack/PacketCollector.java index 75fba96cf..b54cc82dc 100644 --- a/source/org/jivesoftware/smack/PacketCollector.java +++ b/source/org/jivesoftware/smack/PacketCollector.java @@ -76,10 +76,7 @@ public class PacketCollector { */ public void cancel() { // If the packet collector has already been cancelled, do nothing. - if (cancelled) { - return; - } - else { + if (!cancelled) { cancelled = true; // Remove object from collectors list by setting the value in the // list at the correct index to null. The collector thread will @@ -130,7 +127,9 @@ public class PacketCollector { try { wait(); } - catch (InterruptedException ie) { } + catch (InterruptedException ie) { + // Ignore. + } } return (Packet)resultQueue.removeLast(); } @@ -149,7 +148,9 @@ public class PacketCollector { try { wait(timeout); } - catch (InterruptedException ie) { } + catch (InterruptedException ie) { + // Ignore. + } } // If still no result, return null. if (resultQueue.isEmpty()) { diff --git a/source/org/jivesoftware/smack/PacketReader.java b/source/org/jivesoftware/smack/PacketReader.java index 157602c47..148ed77ae 100644 --- a/source/org/jivesoftware/smack/PacketReader.java +++ b/source/org/jivesoftware/smack/PacketReader.java @@ -165,7 +165,9 @@ class PacketReader { } } } - catch (InterruptedException ie) { } + catch (InterruptedException ie) { + // Ignore. + } if (connectionID == null) { throw new XMPPException("Connection failed. No response from server."); } @@ -229,7 +231,6 @@ class PacketReader { * Process listeners. */ private void processListeners() { - boolean processedPacket = false; while (!done) { synchronized (listeners) { if (listeners.size() > 0) { @@ -240,7 +241,7 @@ class PacketReader { } } } - processedPacket = false; + boolean processedPacket = false; int size = listeners.size(); for (int i=0; i"); writer.flush(); - } catch (IOException e) { + } + catch (IOException e) { packetReader.notifyConnectionError(e); } } diff --git a/test/org/jivesoftware/smack/PresencePriorityTest.java b/test/org/jivesoftware/smack/PresencePriorityTest.java index 88ccd721d..01772aedf 100644 --- a/test/org/jivesoftware/smack/PresencePriorityTest.java +++ b/test/org/jivesoftware/smack/PresencePriorityTest.java @@ -31,8 +31,6 @@ public class PresencePriorityTest extends SmackTestCase { * Connection(1) has logged from two different places with different presence priorities. */ public void testMessageToHighestPriority() { - boolean wasFiltering = Chat.isFilteredOnThreadID(); - Chat.setFilteredOnThreadID(false); XMPPConnection conn = null; try { // User_1 will log in again using another resource @@ -118,8 +116,6 @@ public class PresencePriorityTest extends SmackTestCase { fail(e.getMessage()); } finally { - // Restore the previous filtering value so we don't affect other test cases - Chat.setFilteredOnThreadID(wasFiltering); if (conn != null) { conn.close(); }