⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 roster.java

📁 openfire 服务器源码下载
💻 JAVA
📖 第 1 页 / 共 4 页
字号:
/**
 * $RCSfile$
 * $Revision: $
 * $Date: $
 *
 * Copyright (C) 2008 Jive Software. All rights reserved.
 *
 * This software is published under the terms of the GNU Public License (GPL),
 * a copy of which is included in this distribution, or a commercial license
 * agreement with Jive.
 */

package org.jivesoftware.openfire.roster;

import org.jivesoftware.database.JiveID;
import org.jivesoftware.openfire.*;
import org.jivesoftware.openfire.group.Group;
import org.jivesoftware.openfire.group.GroupManager;
import org.jivesoftware.openfire.privacy.PrivacyList;
import org.jivesoftware.openfire.privacy.PrivacyListManager;
import org.jivesoftware.openfire.session.ClientSession;
import org.jivesoftware.openfire.user.UserAlreadyExistsException;
import org.jivesoftware.openfire.user.UserNameManager;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.JiveConstants;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.cache.CacheSizes;
import org.jivesoftware.util.cache.Cacheable;
import org.jivesoftware.util.cache.ExternalizableUtil;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.Presence;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <p>A roster is a list of users that the user wishes to know if they are online.</p>
 * <p>Rosters are similar to buddy groups in popular IM clients. The Roster class is
 * a representation of the roster data.<p/>
 *
 * <p>Updates to this roster is effectively a change to the user's roster. To reflect this,
 * the changes to this class will automatically update the persistently stored roster, as well as
 * send out update announcements to all logged in user sessions.</p>
 *
 * @author Gaston Dombiak
 */
@JiveID(JiveConstants.ROSTER)
public class Roster implements Cacheable, Externalizable {

    /**
     * Roster item cache - table: key jabberid string; value roster item.
     */
    protected ConcurrentHashMap<String, RosterItem> rosterItems = new ConcurrentHashMap<String, RosterItem>();
    /**
     * Contacts with subscription FROM that only exist due to shared groups
     * key: jabberid string; value: groups why the implicit roster item exists (aka invisibleSharedGroups).
     */
    protected ConcurrentHashMap<String, Set<String>> implicitFrom = new ConcurrentHashMap<String, Set<String>>();

    private RosterItemProvider rosterItemProvider;
    private String username;
    private SessionManager sessionManager;
    private XMPPServer server = XMPPServer.getInstance();
    private RoutingTable routingTable;
    private PresenceManager presenceManager;
    /**
     * Note: Used only for shared groups logic.
     */
    private RosterManager rosterManager;


    /**
     * Constructor added for Externalizable. Do not use this constructor.
     */
    public Roster() {
    }

    /**
     * Create a roster for the given user, pulling the existing roster items
     * out of the backend storage provider. The roster will also include items that
     * belong to the user's shared groups.<p>
     *
     * RosterItems that ONLY belong to shared groups won't be persistent unless the user
     * explicitly subscribes to the contact's presence, renames the contact in his roster or adds
     * the item to a personal group.<p>
     *
     * This constructor is not public and instead you should use
     * {@link org.jivesoftware.openfire.roster.RosterManager#getRoster(String)}.
     *
     * @param username The username of the user that owns this roster
     */
    Roster(String username) {
        presenceManager = XMPPServer.getInstance().getPresenceManager();
        rosterManager = XMPPServer.getInstance().getRosterManager();
        sessionManager = SessionManager.getInstance();
        routingTable = XMPPServer.getInstance().getRoutingTable();
        this.username = username;

        // Get the shared groups of this user
        Collection<Group> sharedGroups = rosterManager.getSharedGroups(username);
        Collection<Group> userGroups = GroupManager.getInstance().getGroups(getUserJID());

        // Add RosterItems that belong to the personal roster
        rosterItemProvider =  RosterItemProvider.getInstance();
        Iterator items = rosterItemProvider.getItems(username);
        while (items.hasNext()) {
            RosterItem item = (RosterItem)items.next();
            // Check if the item (i.e. contact) belongs to a shared group of the user. Add the
            // shared group (if any) to this item
            for (Group group : sharedGroups) {
                if (group.isUser(item.getJid())) {
                    // TODO Group name conflicts are not being considered (do we need this?)
                    item.addSharedGroup(group);
                    item.setSubStatus(RosterItem.SUB_BOTH);
                }
            }
            rosterItems.put(item.getJid().toBareJID(), item);
        }
        // Add RosterItems that belong only to shared groups
        Map<JID,List<Group>> sharedUsers = getSharedUsers(sharedGroups);
        for (JID jid : sharedUsers.keySet()) {
            try {
                Collection<Group> itemGroups = new ArrayList<Group>();
                String nickname = "";
                RosterItem item = new RosterItem(jid, RosterItem.SUB_TO, RosterItem.ASK_NONE,
                        RosterItem.RECV_NONE, nickname , null);
                // Add the shared groups to the new roster item
                for (Group group : sharedUsers.get(jid)) {
                    if (group.isUser(jid)) {
                        item.addSharedGroup(group);
                        itemGroups.add(group);
                    }
                    else {
                        item.addInvisibleSharedGroup(group);
                    }
                }
                // Set subscription type to BOTH if the roster user belongs to a shared group
                // that is mutually visible with a shared group of the new roster item
                if (rosterManager.hasMutualVisibility(username, userGroups, jid, itemGroups)) {
                    item.setSubStatus(RosterItem.SUB_BOTH);
                }
                else {
                    // Set subscription type to FROM if the contact does not belong to any of
                    // the associated shared groups
                    boolean belongsToGroup = false;
                    for (Group group : sharedUsers.get(jid)) {
                        if (group.isUser(jid)) {
                            belongsToGroup = true;
                        }
                    }
                    if (!belongsToGroup) {
                        item.setSubStatus(RosterItem.SUB_FROM);
                    }
                }
                // Set nickname and store in memory only if subscription type is not FROM.
                // Roster items with subscription type FROM that exist only because of shared
                // groups will be recreated on demand in #getRosterItem(JID) and #isRosterItem()
                // but will never be stored in memory nor in the database. This is an important
                // optimization to reduce objects in memory and avoid loading users in memory
                // to get their nicknames that will never be shown
                if (item.getSubStatus() != RosterItem.SUB_FROM) {
                    item.setNickname(UserNameManager.getUserName(jid));
                    rosterItems.put(item.getJid().toBareJID(), item);
                }
                else {
                    // Cache information about shared contacts with subscription status FROM
                    implicitFrom
                            .put(item.getJid().toBareJID(), item.getInvisibleSharedGroupsNames());
                }
            }
            catch (UserNotFoundException e) {
                Log.error("Groups (" + sharedUsers.get(jid) + ") include non-existent username (" +
                        jid.getNode() +
                        ")");
            }
        }
        // Fire event indicating that a roster has just been loaded
        RosterEventDispatcher.rosterLoaded(this);
    }

    /**
     * Returns true if the specified user is a member of the roster, false otherwise.
     *
     * @param user the user object to check.
     * @return true if the specified user is a member of the roster, false otherwise.
     */
    public boolean isRosterItem(JID user) {
        // Optimization: Check if the contact has a FROM subscription due to shared groups
        // (only when not present in the rosterItems collection)
        return rosterItems.containsKey(user.toBareJID()) || getImplicitRosterItem(user) != null;
    }

    /**
     * Returns a collection of users in this roster.<p>
     *
     * Note: Roster items with subscription type FROM that exist only because of shared groups
     * are not going to be returned.
     *
     * @return a collection of users in this roster.
     */
    public Collection<RosterItem> getRosterItems() {
        return Collections.unmodifiableCollection(rosterItems.values());
    }

    /**
     * Returns the roster item that is associated with the specified JID. If no roster item
     * was found then a UserNotFoundException will be thrown.
     *
     * @param user the XMPPAddress for the roster item to retrieve
     * @return The roster item associated with the user XMPPAddress.
     * @throws UserNotFoundException if no roster item was found for the specified JID.
     */
    public RosterItem getRosterItem(JID user) throws UserNotFoundException {
        RosterItem item = rosterItems.get(user.toBareJID());
        if (item == null) {
            // Optimization: Check if the contact has a FROM subscription due to shared groups
            item = getImplicitRosterItem(user);
            if (item == null) {
                throw new UserNotFoundException(user.toBareJID());
            }
        }
        return item;
    }

    /**
     * Returns a roster item if the specified user has a subscription of type FROM to this
     * user and the susbcription only exists due to some shared groups or otherwise
     * <tt>null</tt>. This method assumes that this user does not have a subscription to
     * the contact. In other words, this method will not check if there should be a subscription
     * of type TO ot BOTH.
     *
     * @param user the contact to check if he is subscribed to the presence of this user.
     * @return a roster item if the specified user has a subscription of type FROM to this
     *         user and the susbcription only exists due to some shared groups or otherwise null.
     */
    private RosterItem getImplicitRosterItem(JID user) {
        Set<String> invisibleSharedGroups = implicitFrom.get(user.toBareJID());
        if (invisibleSharedGroups != null) {
            RosterItem rosterItem = new RosterItem(user, RosterItem.SUB_FROM, RosterItem.ASK_NONE,
                    RosterItem.RECV_NONE, "", null);
            rosterItem.setInvisibleSharedGroupsNames(invisibleSharedGroups);
            return rosterItem;
        }
        return null;
    }

    /**
     * Create a new item to the roster. Roster items may not be created that contain the same user
     * address as an existing item.
     *
     * @param user       The item to add to the roster.
     * @param push       True if the new item must be pushed to the user.
     * @param persistent True if the new roster item should be persisted to the DB.
     */
    public RosterItem createRosterItem(JID user, boolean push, boolean persistent)
            throws UserAlreadyExistsException, SharedGroupException {
        return createRosterItem(user, null, null, push, persistent);
    }

    /**
     * Create a new item to the roster. Roster items may not be created that contain the same user
     * address as an existing item.
     *
     * @param user       The item to add to the roster.
     * @param nickname   The nickname for the roster entry (can be null).
     * @param push       True if the new item must be push to the user.
     * @param persistent True if the new roster item should be persisted to the DB.
     * @param groups   The list of groups to assign this roster item to (can be null)
     */
    public RosterItem createRosterItem(JID user, String nickname, List<String> groups, boolean push,
                                       boolean persistent)
            throws UserAlreadyExistsException, SharedGroupException {
        return provideRosterItem(user, nickname, groups, push, persistent);
    }

    /**

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -