📄 presenceupdatehandler.java
字号:
/**
* $RCSfile: PresenceUpdateHandler.java,v $
* $Revision: 3125 $
* $Date: 2005-11-30 15:14:14 -0300 (Wed, 30 Nov 2005) $
*
* Copyright (C) 2004 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.
*/
package org.jivesoftware.wildfire.handler;
import org.jivesoftware.util.ConcurrentHashSet;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.wildfire.*;
import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.jivesoftware.wildfire.container.BasicModule;
import org.jivesoftware.wildfire.roster.Roster;
import org.jivesoftware.wildfire.roster.RosterItem;
import org.jivesoftware.wildfire.roster.RosterManager;
import org.jivesoftware.wildfire.user.UserManager;
import org.jivesoftware.wildfire.user.UserNotFoundException;
import org.xmpp.packet.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* Implements the presence protocol. Clients use this protocol to
* update presence and roster information.
* <p/>
* The handler must properly detect the presence type, update the user's roster,
* and inform presence subscribers of the session's updated presence
* status. Presence serves many purposes in Jabber so this handler will
* likely be the most complex of all handlers in the server.
* <p/>
* There are four basic types of presence updates:
* <ul>
* <li>Simple presence updates - addressed to the server (or to address), these updates
* are properly addressed by the server, and multicast to
* interested subscribers on the user's roster. An empty, missing,
* or "unavailable" type attribute indicates a simple update (there
* is no "available" type although it should be accepted by the server.
* <li>Directed presence updates - addressed to particular jabber entities,
* these presence updates are properly addressed and directly delivered
* to the entity without broadcast to roster subscribers. Any update type
* is possible except those reserved for subscription requests.
* <li>Subscription requests - these updates request presence subscription
* status changes. Such requests always affect the roster. The server must:
* <ul>
* <li>update the roster with the proper subscriber info
* <li>push the roster changes to the user
* <li>forward the update to the correct parties.
* </ul>
* The valid types include "subscribe", "subscribed", "unsubscribed",
* and "unsubscribe".
* <li>XMPPServer probes - Provides a mechanism for servers to query the presence
* status of users on another server. This allows users to immediately
* know the presence status of users when they come online rather than way
* for a presence update broadcast from the other server or tracking them
* as they are received. Requires S2S capabilities.
* </ul>
*
* @author Iain Shigeoka
*/
public class PresenceUpdateHandler extends BasicModule implements ChannelHandler {
private Map<String, WeakHashMap<ChannelHandler, Set<String>>> directedPresences;
private RosterManager rosterManager;
private XMPPServer localServer;
private PresenceManager presenceManager;
private PacketDeliverer deliverer;
private OfflineMessageStore messageStore;
private SessionManager sessionManager;
private UserManager userManager;
public PresenceUpdateHandler() {
super("Presence update handler");
directedPresences = new ConcurrentHashMap<String, WeakHashMap<ChannelHandler, Set<String>>>();
}
public void process(Packet xmppPacket) throws UnauthorizedException, PacketException {
Presence presence = (Presence)xmppPacket;
try {
ClientSession session = sessionManager.getSession(presence.getFrom());
Presence.Type type = presence.getType();
// Available
if (type == null) {
if (session != null && session.getStatus() == Session.STATUS_CLOSED) {
Log.warn("Rejected available presence: " + presence + " - " + session);
return;
}
broadcastUpdate(presence.createCopy());
if (session != null) {
session.setPresence(presence);
if (!session.isInitialized()) {
initSession(session);
session.setInitialized(true);
}
}
// Notify the presence manager that the user is now available. The manager may
// remove the last presence status sent by the user when he went offline.
presenceManager.userAvailable(presence);
}
else if (Presence.Type.unavailable == type) {
broadcastUpdate(presence.createCopy());
broadcastUnavailableForDirectedPresences(presence);
if (session == null) {
// Recovery logic. Check if a session can be found in the routing table.
session = (ClientSession) XMPPServer.getInstance().getRoutingTable()
.getRoute(presence.getFrom());
}
if (session != null) {
session.setPresence(presence);
}
// Notify the presence manager that the user is now unavailable. The manager may
// save the last presence status sent by the user and keep track when the user
// went offline.
presenceManager.userUnavailable(presence);
}
else {
presence = presence.createCopy();
if (session != null) {
presence.setFrom(new JID(null, session.getServerName(), null));
presence.setTo(session.getAddress());
}
else {
JID sender = presence.getFrom();
presence.setFrom(presence.getTo());
presence.setTo(sender);
}
presence.setError(PacketError.Condition.bad_request);
deliverer.deliver(presence);
}
}
catch (Exception e) {
Log.error(LocaleUtils.getLocalizedString("admin.error") + ". Triggered by packet: " +
xmppPacket, e);
}
}
/**
* Handle presence updates that affect roster subscriptions.
*
* @param presence The presence presence to handle
*/
public void process(Presence presence) throws PacketException {
try {
process((Packet)presence);
}
catch (UnauthorizedException e) {
try {
Session session = sessionManager.getSession(presence.getFrom());
presence = presence.createCopy();
if (session != null) {
presence.setFrom(new JID(null, session.getServerName(), null));
presence.setTo(session.getAddress());
}
else {
JID sender = presence.getFrom();
presence.setFrom(presence.getTo());
presence.setTo(sender);
}
presence.setError(PacketError.Condition.not_authorized);
deliverer.deliver(presence);
}
catch (Exception err) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), err);
}
}
}
/**
* A session that has transitioned to available status must be initialized.
* This includes:
* <ul>
* <li>Sending all offline presence subscription requests</li>
* <li>Sending offline messages</li>
* </ul>
*
* @param session The session being updated
* @throws UserNotFoundException If the user being updated does not exist
*/
private void initSession(ClientSession session) throws UserNotFoundException {
// Only user sessions need to be authenticated
if (userManager.isRegisteredUser(session.getAddress().getNode())) {
String username = session.getAddress().getNode();
// Send pending subscription requests to user if roster service is enabled
if (RosterManager.isRosterServiceEnabled()) {
Roster roster = rosterManager.getRoster(username);
for (RosterItem item : roster.getRosterItems()) {
if (item.getRecvStatus() == RosterItem.RECV_SUBSCRIBE) {
session.process(createSubscribePresence(item.getJid(), true));
}
else if (item.getRecvStatus() == RosterItem.RECV_UNSUBSCRIBE) {
session.process(createSubscribePresence(item.getJid(), false));
}
if (item.getSubStatus() == RosterItem.SUB_TO
|| item.getSubStatus() == RosterItem.SUB_BOTH) {
presenceManager.probePresence(session.getAddress(), item.getJid());
}
}
}
if (session.canFloodOfflineMessages()) {
// deliver offline messages if any
Collection<OfflineMessage> messages = messageStore.getMessages(username, true);
for (Message message : messages) {
session.process(message);
}
}
}
}
public Presence createSubscribePresence(JID senderAddress, boolean isSubscribe) {
Presence presence = new Presence();
presence.setFrom(senderAddress);
if (isSubscribe) {
presence.setType(Presence.Type.subscribe);
}
else {
presence.setType(Presence.Type.unsubscribe);
}
return presence;
}
/**
* Broadcast the given update to all subscribers. We need to:
* <ul>
* <li>Query the roster table for subscribers</li>
* <li>Iterate through the list and send the update to each subscriber</li>
* </ul>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -