📄 roster.java
字号:
/**
* $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 + -