All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jivesoftware.smack.ChatManager Maven / Gradle / Ivy

The newest version!
/**
 *
 * 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.UUID;
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.util.StringUtils;
import org.jivesoftware.smack.util.collections.ReferenceMap;
import org.xmpp.packet.Message;
import org.xmpp.packet.Message.Type;
import org.xmpp.packet.Packet;

/**
 * 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 necessary 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 {
    /*
     * Sets the default behaviour for allowing 'normal' messages to be used in
     * chats. As some clients don't set the message type to chat, the type
     * normal has to be accepted to allow chats with these clients.
     */
    private static boolean defaultIsNormalInclude = true;

    /*
     * Sets the default behaviour for how to match chats when there is NO thread
     * id in the incoming message.
     */
    private static MatchMode defaultMatchMode = MatchMode.BARE_JID;

    /**
     * Defines the different modes under which a match will be attempted with an
     * existing chat when the incoming message does not have a thread id.
     */
    public enum MatchMode {
        /**
         * Will not attempt to match, always creates a new chat.
         */
        NONE,
        /**
         * Will match on the JID in the from field of the message.
         */
        SUPPLIED_JID,
        /**
         * Will attempt to match on the JID in the from field, and then attempt
         * the base JID if no match was found. This is the most lenient
         * matching.
         */
        BARE_JID;
    }

    /*
     * Determines whether incoming messages of type normal can create chats.
     */
    private boolean normalIncluded = defaultIsNormalInclude;

    /*
     * Determines how incoming message with no thread will be matched to
     * existing chats.
     */
    private MatchMode matchMode = defaultMatchMode;

    /**
     * Maps thread ID to chat.
     */
    private Map threadChats = Collections
            .synchronizedMap(new ReferenceMap(ReferenceMap.HARD,
                    ReferenceMap.WEAK));

    /**
     * Maps jids to chats
     */
    private Map jidChats = Collections
            .synchronizedMap(new ReferenceMap(ReferenceMap.HARD,
                    ReferenceMap.WEAK));

    /**
     * Maps base jids to chats
     */
    private Map baseJidChats = Collections
            .synchronizedMap(new ReferenceMap(ReferenceMap.HARD,
                    ReferenceMap.WEAK));

    private Set chatManagerListeners = new CopyOnWriteArraySet();

    private Map interceptors = new WeakHashMap();

    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 == Type.chat)
                        || (normalIncluded ? messageType == Type.normal : false);
            }
        };

        // Add a listener for all message packets so that we can deliver
        // 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().toFullJID());
                } else {
                    chat = getThreadChat(message.getThread());
                }

                if (chat == null) {
                    chat = createChat(message);
                }
                deliverMessage(chat, message);
            }
        }, filter);
    }

    /**
     * Determines whether incoming messages of type normal will be used
     * for creating new chats or matching a message to existing ones.
     * 
     * @return true if normal is allowed, false otherwise.
     */
    public boolean isNormalIncluded() {
        return normalIncluded;
    }

    /**
     * Sets whether to allow incoming messages of type normal to be used
     * for creating new chats or matching a message to an existing one.
     * 
     * @param normalIncluded
     *            true to allow normal, false otherwise.
     */
    public void setNormalIncluded(boolean normalIncluded) {
        this.normalIncluded = normalIncluded;
    }

    /**
     * Gets the current mode for matching messages with NO thread id to
     * existing chats.
     * 
     * @return The current mode.
     */
    public MatchMode getMatchMode() {
        return matchMode;
    }

    /**
     * Sets the mode for matching messages with NO thread id to existing
     * chats.
     * 
     * @param matchMode
     *            The mode to set.
     */
    public void setMatchMode(MatchMode matchMode) {
        this.matchMode = matchMode;
    }

    /**
     * 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) {
        return createChat(userJID, null, 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().toFullJID();

        return createChat(userJID, threadID, false);
    }

    /**
     * Try to get a matching chat for the given user JID, based on the
     * {@link MatchMode}. 
  • NONE - return null
  • SUPPLIED_JID - match the * jid in the from field of the message exactly.
  • BARE_JID - if not match * for from field, try the bare jid. * * @param userJID * jid in the from field of message. * @return Matching chat, or null if no match found. */ private Chat getUserChat(String userJID) { if (matchMode == MatchMode.NONE) { return null; } Chat match = jidChats.get(userJID); if (match == null && (matchMode == MatchMode.BARE_JID)) { 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 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 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); } } /** * Returns a unique id. * * @return the next id. */ private static String nextID() { return UUID.randomUUID().toString(); } public static void setDefaultMatchMode(MatchMode mode) { defaultMatchMode = mode; } public static void setDefaultIsNormalIncluded(boolean allowNormal) { defaultIsNormalInclude = allowNormal; } }




  • © 2015 - 2025 Weber Informatics LLC | Privacy Policy