roster.java
来自「基于Jabber协议的即时消息服务器」· Java 代码 · 共 1,088 行 · 第 1/4 页
JAVA
1,088 行
* <p>Broadcast the presence update to all subscribers of the roter.</p>
* <p/>
* <p>Any presence change typically results in a broadcast to the roster members.</p>
*
* @param packet The presence packet to broadcast
*/
public void broadcastPresence(Presence packet) {
if (routingTable == null) {
routingTable = XMPPServer.getInstance().getRoutingTable();
}
if (routingTable == null) {
return;
}
// Get the privacy list of this user
PrivacyList list = null;
JID from = packet.getFrom();
if (from != null) {
// Try to use the active list of the session. If none was found then try to use
// the default privacy list of the session
ClientSession session = sessionManager.getSession(from);
if (session != null) {
list = session.getActiveList();
list = list == null ? session.getDefaultList() : list;
}
}
if (list == null) {
// No privacy list was found (based on the session) so check if there is a default list
list = PrivacyListManager.getInstance().getDefaultPrivacyList(username);
}
// Broadcast presence to subscribed entities
for (RosterItem item : rosterItems.values()) {
if (item.getSubStatus() == RosterItem.SUB_BOTH
|| item.getSubStatus() == RosterItem.SUB_FROM) {
packet.setTo(item.getJid());
if (list != null && list.shouldBlockPacket(packet)) {
// Outgoing presence notifications are blocked for this contact
continue;
}
JID searchNode = new JID(item.getJid().getNode(), item.getJid().getDomain(), null);
for (ChannelHandler session : routingTable.getRoutes(searchNode)) {
try {
session.process(packet);
}
catch (Exception e) {
// Theoretically only happens if session has been closed.
Log.debug(e);
}
}
}
}
// Broadcast presence to shared contacts whose subscription status is FROM
for (String contact : implicitFrom.keySet()) {
packet.setTo(contact);
if (list != null && list.shouldBlockPacket(packet)) {
// Outgoing presence notifications are blocked for this contact
continue;
}
for (ChannelHandler session : routingTable.getRoutes(new JID(contact))) {
try {
session.process(packet);
}
catch (Exception e) {
// Theoretically only happens if session has been closed.
Log.debug(e);
}
}
}
if (from != null) {
// Broadcast presence to other user's resources
sessionManager.broadcastPresenceToOtherResources(from, packet);
}
}
/**
* Returns the list of users that belong ONLY to a shared group of this user. If the contact
* belongs to the personal roster and a shared group then it wont' be included in the answer.
*
* @param sharedGroups the shared groups of this user.
* @return the list of users that belong ONLY to a shared group of this user.
*/
private Map<JID,List<Group>> getSharedUsers(Collection<Group> sharedGroups) {
// Get the users to process from the shared groups. Users that belong to different groups
// will have one entry in the map associated with all the groups
Map<JID,List<Group>> sharedGroupUsers = new HashMap<JID,List<Group>>();
for (Group group : sharedGroups) {
// Get all the users that should be in this roster
Collection<JID> users = rosterManager.getSharedUsersForRoster(group, this);
// Add the users of the group to the general list of users to process
JID userJID = getUserJID();
for (JID jid : users) {
// Add the user to the answer if the user doesn't belong to the personal roster
// (since we have already added the user to the answer)
boolean isRosterItem = rosterItems.containsKey(jid.toBareJID());
if (!isRosterItem && !userJID.equals(jid)) {
List<Group> groups = sharedGroupUsers.get(jid);
if (groups == null) {
groups = new ArrayList<Group>();
sharedGroupUsers.put(jid, groups);
}
groups.add(group);
}
}
}
return sharedGroupUsers;
}
private void broadcast(org.xmpp.packet.Roster roster) {
JID recipient = server.createJID(username, null);
roster.setTo(recipient);
if (sessionManager == null) {
sessionManager = SessionManager.getInstance();
}
try {
sessionManager.userBroadcast(username, roster);
}
catch (UnauthorizedException e) {
// Do nothing. We should never end here.
}
}
/**
* Broadcasts the RosterItem to all the connected resources of this user. Due to performance
* optimizations and due to some clients errors that are showing items with subscription status
* FROM we added a flag that indicates if a roster items that exists only because of a shared
* group with subscription status FROM will not be sent.
*
* @param item the item to broadcast.
* @param optimize true indicates that items that exists only because of a shared
* group with subscription status FROM will not be sent
*/
public void broadcast(RosterItem item, boolean optimize) {
// Do not broadcast items with status FROM that exist only because of shared groups
if (optimize && item.isOnlyShared() && item.getSubStatus() == RosterItem.SUB_FROM) {
return;
}
// Set the groups to broadcast (include personal and shared groups)
List<String> groups = new ArrayList<String>(item.getGroups());
for (Group sharedGroup : item.getSharedGroups()) {
groups.add(sharedGroup.getProperties().get("sharedRoster.displayName"));
}
org.xmpp.packet.Roster roster = new org.xmpp.packet.Roster();
roster.setType(IQ.Type.set);
roster.addItem(item.getJid(), item.getNickname(),
getAskStatus(item.getAskStatus()),
org.xmpp.packet.Roster.Subscription.valueOf(item.getSubStatus().getName()),
groups);
broadcast(roster);
}
/**
* Sends a presence probe to the probee for each connected resource of this user.
*/
private void probePresence(JID probee) {
for (ClientSession session : sessionManager.getSessions(username)) {
presenceManager.probePresence(session.getAddress(), probee);
}
}
public int getCachedSize() {
// Approximate the size of the object in bytes by calculating the size
// of each field.
int size = 0;
size += CacheSizes.sizeOfObject(); // overhead of object
size += CacheSizes.sizeOfCollection(rosterItems.values()); // roster item cache
size += CacheSizes.sizeOfString(username); // username
return size;
}
/**
* Update the roster since a group user has been added to a shared group. Create a new
* RosterItem if the there doesn't exist an item for the added user. The new RosterItem won't be
* saved to the backend store unless the user explicitly subscribes to the contact's presence,
* renames the contact in his roster or adds the item to a personal group. Otherwise the shared
* group will be added to the shared groups lists. In any case an update broadcast will be sent
* to all the users logged resources.
*
* @param group the shared group where the user was added.
* @param addedUser the contact to update in the roster.
*/
void addSharedUser(Group group, JID addedUser) {
boolean newItem = false;
RosterItem item = null;
try {
// Get the RosterItem for the *local* user to add
item = getRosterItem(addedUser);
// Do nothing if the item already includes the shared group
if (item.getSharedGroups().contains(group)) {
return;
}
newItem = false;
}
catch (UserNotFoundException e) {
try {
// Create a new RosterItem for this new user
String nickname = UserNameManager.getUserName(addedUser);
item =
new RosterItem(addedUser, RosterItem.SUB_BOTH, RosterItem.ASK_NONE,
RosterItem.RECV_NONE, nickname, null);
// Add the new item to the list of items
rosterItems.put(item.getJid().toBareJID(), item);
newItem = true;
}
catch (UserNotFoundException ex) {
Log.error("Group (" + group.getName() + ") includes non-existent username (" +
addedUser +
")");
}
}
// If an item already exists then take note of the old subscription status
RosterItem.SubType prevSubscription = null;
if (!newItem) {
prevSubscription = item.getSubStatus();
}
// Update the subscription of the item **based on the item groups**
Collection<Group> userGroups = GroupManager.getInstance().getGroups(getUserJID());
Collection<Group> sharedGroups = new ArrayList<Group>();
sharedGroups.addAll(item.getSharedGroups());
// Add the new group to the list of groups to check
sharedGroups.add(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(getUsername(), userGroups, addedUser, sharedGroups)) {
item.setSubStatus(RosterItem.SUB_BOTH);
}
// Update the subscription status depending on the group membership of the new
// user and this user
else if (group.isUser(addedUser) && !group.isUser(getUsername())) {
item.setSubStatus(RosterItem.SUB_TO);
}
else if (!group.isUser(addedUser) && group.isUser(getUsername())) {
item.setSubStatus(RosterItem.SUB_FROM);
}
// Add the shared group to the list of shared groups
if (item.getSubStatus() != RosterItem.SUB_FROM) {
item.addSharedGroup(group);
}
else {
item.addInvisibleSharedGroup(group);
}
// If the item already exists then check if the subscription status should be
// changed to BOTH based on the old and new subscription status
if (prevSubscription != null) {
if (prevSubscription == RosterItem.SUB_TO &&
item.getSubStatus() == RosterItem.SUB_FROM) {
item.setSubStatus(RosterItem.SUB_BOTH);
}
else if (prevSubscription == RosterItem.SUB_FROM &&
item.getSubStatus() == RosterItem.SUB_TO) {
item.setSubStatus(RosterItem.SUB_BOTH);
}
}
// Optimization: Check if we do not need to keep the item in memory
if (item.isOnlyShared() && item.getSubStatus() == RosterItem.SUB_FROM) {
// Remove from memory and do nothing else
rosterItems.remove(item.getJid().toBareJID());
// Cache information about shared contacts with subscription status FROM
implicitFrom.put(item.getJid().toBareJID(), item.getInvisibleSharedGroupsNames());
}
else {
// Remove from list of shared contacts with status FROM (if any)
implicitFrom.remove(item.getJid().toBareJID());
// Ensure that the item is an explicit roster item
rosterItems.put(item.getJid().toBareJID(), item);
// Brodcast to all the user resources of the updated roster item
broadcast(item, true);
// Probe the presence of the new group user
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?