aboutsummaryrefslogtreecommitdiff
path: root/src/org/jivesoftware/smack/ChatManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/jivesoftware/smack/ChatManager.java')
-rw-r--r--src/org/jivesoftware/smack/ChatManager.java284
1 files changed, 284 insertions, 0 deletions
diff --git a/src/org/jivesoftware/smack/ChatManager.java b/src/org/jivesoftware/smack/ChatManager.java
new file mode 100644
index 0000000..22dc3f9
--- /dev/null
+++ b/src/org/jivesoftware/smack/ChatManager.java
@@ -0,0 +1,284 @@
+/**
+ * $RCSfile$
+ * $Revision: 2407 $
+ * $Date: 2004-11-02 15:37:00 -0800 (Tue, 02 Nov 2004) $
+ *
+ * Copyright 2003-2007 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.smack;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.jivesoftware.smack.filter.AndFilter;
+import org.jivesoftware.smack.filter.FromContainsFilter;
+import org.jivesoftware.smack.filter.PacketFilter;
+import org.jivesoftware.smack.filter.ThreadFilter;
+import org.jivesoftware.smack.packet.Message;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smack.util.collections.ReferenceMap;
+
+import java.util.*;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+/**
+ * The chat manager keeps track of references to all current chats. It will not hold any references
+ * in memory on its own so it is neccesary to keep a reference to the chat object itself. To be
+ * made aware of new chats, register a listener by calling {@link #addChatListener(ChatManagerListener)}.
+ *
+ * @author Alexander Wenckus
+ */
+public class ChatManager {
+
+ /**
+ * Returns the next unique id. Each id made up of a short alphanumeric
+ * prefix along with a unique numeric value.
+ *
+ * @return the next id.
+ */
+ private static synchronized String nextID() {
+ return prefix + Long.toString(id++);
+ }
+
+ /**
+ * A prefix helps to make sure that ID's are unique across mutliple instances.
+ */
+ private static String prefix = StringUtils.randomString(5);
+
+ /**
+ * Keeps track of the current increment, which is appended to the prefix to
+ * forum a unique ID.
+ */
+ private static long id = 0;
+
+ /**
+ * Maps thread ID to chat.
+ */
+ private Map<String, Chat> threadChats = Collections.synchronizedMap(new ReferenceMap<String, Chat>(ReferenceMap.HARD,
+ ReferenceMap.WEAK));
+
+ /**
+ * Maps jids to chats
+ */
+ private Map<String, Chat> jidChats = Collections.synchronizedMap(new ReferenceMap<String, Chat>(ReferenceMap.HARD,
+ ReferenceMap.WEAK));
+
+ /**
+ * Maps base jids to chats
+ */
+ private Map<String, Chat> baseJidChats = Collections.synchronizedMap(new ReferenceMap<String, Chat>(ReferenceMap.HARD,
+ ReferenceMap.WEAK));
+
+ private Set<ChatManagerListener> chatManagerListeners
+ = new CopyOnWriteArraySet<ChatManagerListener>();
+
+ private Map<PacketInterceptor, PacketFilter> interceptors
+ = new WeakHashMap<PacketInterceptor, PacketFilter>();
+
+ private Connection connection;
+
+ ChatManager(Connection connection) {
+ this.connection = connection;
+
+ PacketFilter filter = new PacketFilter() {
+ public boolean accept(Packet packet) {
+ if (!(packet instanceof Message)) {
+ return false;
+ }
+ Message.Type messageType = ((Message) packet).getType();
+ return messageType != Message.Type.groupchat &&
+ messageType != Message.Type.headline;
+ }
+ };
+ // Add a listener for all message packets so that we can deliver errant
+ // messages to the best Chat instance available.
+ connection.addPacketListener(new PacketListener() {
+ public void processPacket(Packet packet) {
+ Message message = (Message) packet;
+ Chat chat;
+ if (message.getThread() == null) {
+ chat = getUserChat(message.getFrom());
+ }
+ else {
+ chat = getThreadChat(message.getThread());
+ if (chat == null) {
+ // Try to locate the chat based on the sender of the message
+ chat = getUserChat(message.getFrom());
+ }
+ }
+
+ if(chat == null) {
+ chat = createChat(message);
+ }
+ deliverMessage(chat, message);
+ }
+ }, filter);
+ }
+
+ /**
+ * Creates a new chat and returns it.
+ *
+ * @param userJID the user this chat is with.
+ * @param listener the listener which will listen for new messages from this chat.
+ * @return the created chat.
+ */
+ public Chat createChat(String userJID, MessageListener listener) {
+ String threadID;
+ do {
+ threadID = nextID();
+ } while (threadChats.get(threadID) != null);
+
+ return createChat(userJID, threadID, listener);
+ }
+
+ /**
+ * Creates a new chat using the specified thread ID, then returns it.
+ *
+ * @param userJID the jid of the user this chat is with
+ * @param thread the thread of the created chat.
+ * @param listener the listener to add to the chat
+ * @return the created chat.
+ */
+ public Chat createChat(String userJID, String thread, MessageListener listener) {
+ if(thread == null) {
+ thread = nextID();
+ }
+ Chat chat = threadChats.get(thread);
+ if(chat != null) {
+ throw new IllegalArgumentException("ThreadID is already used");
+ }
+ chat = createChat(userJID, thread, true);
+ chat.addMessageListener(listener);
+ return chat;
+ }
+
+ private Chat createChat(String userJID, String threadID, boolean createdLocally) {
+ Chat chat = new Chat(this, userJID, threadID);
+ threadChats.put(threadID, chat);
+ jidChats.put(userJID, chat);
+ baseJidChats.put(StringUtils.parseBareAddress(userJID), chat);
+
+ for(ChatManagerListener listener : chatManagerListeners) {
+ listener.chatCreated(chat, createdLocally);
+ }
+
+ return chat;
+ }
+
+ private Chat createChat(Message message) {
+ String threadID = message.getThread();
+ if(threadID == null) {
+ threadID = nextID();
+ }
+ String userJID = message.getFrom();
+
+ return createChat(userJID, threadID, false);
+ }
+
+ /**
+ * Try to get a matching chat for the given user JID. Try the full
+ * JID map first, the try to match on the base JID if no match is
+ * found.
+ *
+ * @param userJID
+ * @return
+ */
+ private Chat getUserChat(String userJID) {
+ Chat match = jidChats.get(userJID);
+
+ if (match == null) {
+ match = baseJidChats.get(StringUtils.parseBareAddress(userJID));
+ }
+ return match;
+ }
+
+ public Chat getThreadChat(String thread) {
+ return threadChats.get(thread);
+ }
+
+ /**
+ * Register a new listener with the ChatManager to recieve events related to chats.
+ *
+ * @param listener the listener.
+ */
+ public void addChatListener(ChatManagerListener listener) {
+ chatManagerListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener, it will no longer be notified of new events related to chats.
+ *
+ * @param listener the listener that is being removed
+ */
+ public void removeChatListener(ChatManagerListener listener) {
+ chatManagerListeners.remove(listener);
+ }
+
+ /**
+ * Returns an unmodifiable collection of all chat listeners currently registered with this
+ * manager.
+ *
+ * @return an unmodifiable collection of all chat listeners currently registered with this
+ * manager.
+ */
+ public Collection<ChatManagerListener> getChatListeners() {
+ return Collections.unmodifiableCollection(chatManagerListeners);
+ }
+
+ private void deliverMessage(Chat chat, Message message) {
+ // Here we will run any interceptors
+ chat.deliver(message);
+ }
+
+ void sendMessage(Chat chat, Message message) {
+ for(Map.Entry<PacketInterceptor, PacketFilter> interceptor : interceptors.entrySet()) {
+ PacketFilter filter = interceptor.getValue();
+ if(filter != null && filter.accept(message)) {
+ interceptor.getKey().interceptPacket(message);
+ }
+ }
+ // Ensure that messages being sent have a proper FROM value
+ if (message.getFrom() == null) {
+ message.setFrom(connection.getUser());
+ }
+ connection.sendPacket(message);
+ }
+
+ PacketCollector createPacketCollector(Chat chat) {
+ return connection.createPacketCollector(new AndFilter(new ThreadFilter(chat.getThreadID()),
+ new FromContainsFilter(chat.getParticipant())));
+ }
+
+ /**
+ * Adds an interceptor which intercepts any messages sent through chats.
+ *
+ * @param packetInterceptor the interceptor.
+ */
+ public void addOutgoingMessageInterceptor(PacketInterceptor packetInterceptor) {
+ addOutgoingMessageInterceptor(packetInterceptor, null);
+ }
+
+ public void addOutgoingMessageInterceptor(PacketInterceptor packetInterceptor, PacketFilter filter) {
+ if (packetInterceptor != null) {
+ interceptors.put(packetInterceptor, filter);
+ }
+ }
+}