📄 presenceupdatehandler.java
字号:
+ localServer.getServerInfo().getXMPPDomain()
+ " by unknown user: " + update.getFrom());
}
}
/**
* Notification method sent to this handler when a user has sent a directed
* presence to an entity. If the sender of the presence is local (to this server)
* and the target entity does not belong to the user's roster then update the
* registry of sent directed presences by the user.
*
* @param update the directed Presence sent by the user to an entity.
* @param handlerJID the JID of the handler that will receive/handle/process the sent packet.
* @param jid the receipient specified in the packet to handle.
*/
public void directedPresenceSent(Presence update, JID handlerJID, String jid) {
if (update.getFrom() == null) {
return;
}
if (localServer.isLocal(update.getFrom())) {
boolean keepTrack = false;
String name = update.getFrom().getNode();
if (name != null && !"".equals(name)) {
// Keep track of all directed presences if roster service is disabled
if (!RosterManager.isRosterServiceEnabled()) {
keepTrack = true;
}
else {
try {
Roster roster = rosterManager.getRoster(name);
// If the directed presence was sent to an entity that is not in the user's
// roster, keep a registry of this so that when the user goes offline we
// will be able to send the unavailable presence to the entity
RosterItem rosterItem = null;
try {
rosterItem = roster.getRosterItem(update.getTo());
}
catch (UserNotFoundException e) {
// Ignore
}
if (rosterItem == null ||
RosterItem.SUB_NONE == rosterItem.getSubStatus() ||
RosterItem.SUB_TO == rosterItem.getSubStatus()) {
keepTrack = true;
}
}
catch (UserNotFoundException e) {
Log.warn("Presence being sent from unknown user " + name, e);
}
catch (PacketException e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
}
}
}
else if (update.getFrom().getResource() != null){
// Keep always track of anonymous users directed presences
keepTrack = true;
}
if (keepTrack) {
String sender = update.getFrom().toString();
Lock lock = CacheFactory.getLock(sender, directedPresencesCache);
try {
lock.lock();
Collection<DirectedPresence> directedPresences = directedPresencesCache.get(sender);
if (Presence.Type.unavailable.equals(update.getType())) {
if (directedPresences != null) {
// It's a directed unavailable presence
if (routingTable.hasClientRoute(handlerJID)) {
// Client sessions will receive only presences to the same JID (the
// address of the session) so remove the handler from the map
for (DirectedPresence directedPresence : directedPresences) {
if (directedPresence.getHandler().equals(handlerJID)) {
directedPresences.remove(directedPresence);
break;
}
}
}
else {
// A service may receive presences for many JIDs so in this case we
// just need to remove the jid that has received a directed
// unavailable presence
for (DirectedPresence directedPresence : directedPresences) {
if (directedPresence.getHandler().equals(handlerJID)) {
directedPresence.removeReceiver(jid);
if (directedPresence.isEmpty()) {
directedPresences.remove(directedPresence);
}
break;
}
}
}
if (directedPresences.isEmpty()) {
// Remove the user from the registry since the list of directed
// presences is empty
directedPresencesCache.remove(sender);
localDirectedPresences.remove(sender);
}
else {
directedPresencesCache.put(sender, directedPresences);
localDirectedPresences.put(sender, directedPresences);
}
}
}
else {
if (directedPresences == null) {
// We are using a set to avoid duplicate jids in case the user
// sends several directed presences to the same handler. The Map also
// ensures that if the user sends several presences to the same handler
// we will have only one entry in the Map
directedPresences = new ConcurrentLinkedQueue<DirectedPresence>();
}
// Add the handler to the list of handler that processed the directed
// presence sent by the user. This handler will be used to send
// the unavailable presence when the user goes offline
DirectedPresence affectedDirectedPresence = null;
for (DirectedPresence directedPresence : directedPresences) {
if (directedPresence.getHandler().equals(handlerJID)) {
affectedDirectedPresence = directedPresence;
break;
}
}
if (affectedDirectedPresence == null) {
affectedDirectedPresence = new DirectedPresence(handlerJID);
directedPresences.add(affectedDirectedPresence);
}
affectedDirectedPresence.addReceiver(jid);
directedPresencesCache.put(sender, directedPresences);
localDirectedPresences.put(sender, directedPresences);
}
} finally {
lock.unlock();
}
}
}
}
/**
* Sends an unavailable presence to the entities that received a directed (available) presence
* by the user that is now going offline.
*
* @param update the unavailable presence sent by the user.
*/
private void broadcastUnavailableForDirectedPresences(Presence update) {
JID from = update.getFrom();
if (from == null) {
return;
}
if (localServer.isLocal(from)) {
// Remove the registry of directed presences of this user
Collection<DirectedPresence> directedPresences = null;
Lock lock = CacheFactory.getLock(from.toString(), directedPresencesCache);
try {
lock.lock();
directedPresences = directedPresencesCache.remove(from.toString());
} finally {
lock.unlock();
}
if (directedPresences != null) {
// Iterate over all the entities that the user sent a directed presence
for (DirectedPresence directedPresence : directedPresences) {
for (String receiver : directedPresence.getReceivers()) {
Presence presence = update.createCopy();
presence.setTo(receiver);
localServer.getPresenceRouter().route(presence);
}
}
localDirectedPresences.remove(from.toString());
}
}
}
public boolean hasDirectPresence(JID ownerJID, JID recipientJID) {
if (recipientJID == null) {
return false;
}
Collection<DirectedPresence> directedPresences = directedPresencesCache.get(ownerJID.toString());
if (directedPresences != null) {
String recipient = recipientJID.toBareJID();
for (DirectedPresence directedPresence : directedPresences) {
for (String receiver : directedPresence.getReceivers()) {
if (receiver.contains(recipient)) {
return true;
}
}
}
}
return false;
}
/**
* Removes directed presences sent to entities that are no longer available.
*/
public void removedExpiredPresences() {
Map<String, Collection<DirectedPresence>> copy =
new HashMap<String, Collection<DirectedPresence>>(localDirectedPresences);
for (Map.Entry<String, Collection<DirectedPresence>> entry : copy.entrySet()) {
for (DirectedPresence directedPresence : entry.getValue()) {
if (!routingTable.hasClientRoute(directedPresence.getHandler()) &&
!routingTable.hasComponentRoute(directedPresence.getHandler())) {
Collection<DirectedPresence> presences = localDirectedPresences.get(entry.getKey());
presences.remove(directedPresence);
if (presences.isEmpty()) {
localDirectedPresences.remove(entry.getKey());
}
}
}
}
}
public void initialize(XMPPServer server) {
super.initialize(server);
localServer = server;
rosterManager = server.getRosterManager();
presenceManager = server.getPresenceManager();
deliverer = server.getPacketDeliverer();
messageStore = server.getOfflineMessageStore();
sessionManager = server.getSessionManager();
userManager = server.getUserManager();
routingTable = server.getRoutingTable();
directedPresencesCache = CacheFactory.createCache(PRESENCE_CACHE_NAME);
// TODO Add as route listener (to remove direct presences info for removed routes). Mainly for c2s sessions which is uncommon.
// Listen to cluster events
ClusterManager.addListener(this);
}
public void joinedCluster() {
// Populate directedPresencesCache with local content since when not in a cluster
// we could still send directed presences to entities that when connected to a cluster
// they will be replicated. An example would be MUC rooms.
for (Map.Entry<String, Collection<DirectedPresence>> entry : localDirectedPresences.entrySet()) {
if (entry.getValue().isEmpty()) {
Log.warn("PresenceUpdateHandler - Skipping empty directed presences when joining cluster for sender: " +
entry.getKey());
continue;
}
// TODO perhaps we should not lock for every entry. Instead, lock it
// once (using a LOCK_ALL global key), and handle iterations in
// one go. We should first make sure that this doesn't lead to
// deadlocks though! The tryLock() mechanism could be used to first
// try one approach, but fall back on the other approach.
Lock lock = CacheFactory.getLock(entry.getKey(), directedPresencesCache);
try {
lock.lock();
directedPresencesCache.put(entry.getKey(), entry.getValue());
} finally {
lock.unlock();
}
}
}
public void joinedCluster(byte[] nodeID) {
// Do nothing
}
public void leftCluster() {
if (!XMPPServer.getInstance().isShuttingDown()) {
// Populate directedPresencesCache with local content
for (Map.Entry<String, Collection<DirectedPresence>> entry : localDirectedPresences.entrySet()) {
if (entry.getValue().isEmpty()) {
Log.warn(
"PresenceUpdateHandler - Skipping empty directed presences when leaving cluster for sender: " +
entry.getKey());
continue;
}
// TODO perhaps we should not lock for every entry. Instead, lock it
// once (using a LOCK_ALL global key), and handle iterations in
// one go. We should first make sure that this doesn't lead to
// deadlocks though! The tryLock() mechanism could be used to first
// try one approach, but fall back on the other approach.
Lock lock = CacheFactory.getLock(entry.getKey(), directedPresencesCache);
try {
lock.lock();
directedPresencesCache.put(entry.getKey(), entry.getValue());
} finally {
lock.unlock();
}
}
}
}
public void leftCluster(byte[] nodeID) {
// Do nothing
}
public void markedAsSeniorClusterMember() {
// Do nothing
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -