Commit 4178af02 authored by Gaston Dombiak's avatar Gaston Dombiak Committed by gato

1) Improved delivery logic when user is connected from many resources. JM-14

2) Sessions with negative presence priorities are not considered when messages were sent to bare JID. JM-1009

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@7960 b35dd754-fafc-0310-a699-88a17e54d16e
parent e2c92253
......@@ -11,18 +11,19 @@
package org.jivesoftware.openfire;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import org.jivesoftware.openfire.container.BasicModule;
import org.jivesoftware.openfire.interceptor.InterceptorManager;
import org.jivesoftware.openfire.interceptor.PacketRejectedException;
import org.jivesoftware.openfire.session.ClientSession;
import org.jivesoftware.openfire.session.Session;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
import org.xmpp.packet.PacketError;
import org.xmpp.packet.Presence;
import java.util.StringTokenizer;
import java.util.*;
/**
* <p>Route message packets throughout the server.</p>
......@@ -93,7 +94,15 @@ public class MessageRouter extends BasicModule {
}
try {
routingTable.getBestRoute(recipientJID).process(packet);
// Check if message was sent to a bare JID of a local user
if (recipientJID != null && recipientJID.getResource() == null &&
serverName.equals(recipientJID.getDomain())) {
routeToBareJID(recipientJID, packet);
}
else {
// Deliver stanza to best route
routingTable.getBestRoute(recipientJID).process(packet);
}
}
catch (Exception e) {
try {
......@@ -129,6 +138,100 @@ public class MessageRouter extends BasicModule {
}
}
/**
* Deliver the message sent to the bare JID of a local user to the best connected resource. If the
* target user is not online then messages will be stored offline according to the offline strategy.
* However, if the user is connected from only one resource then the message will be delivered to
* that resource. In the case that the user is connected from many resources the logic will be the
* following:
* <ol>
* <li>Select resources with highest priority</li>
* <li>Select resources with highest show value (chat, available, away, xa, dnd)</li>
* <li>Select resource with most recent activity</li>
* </ol>
*
* Admins can override the above logic and just send the message to all connected resources
* with highest priority by setting the system property <tt>route.all-resources</tt> to
* <tt>true</tt>.
*
* @param recipientJID the bare JID of the target local user.
* @param packet the message to send.
*/
private void routeToBareJID(JID recipientJID, Message packet) {
List<ClientSession> sessions = sessionManager.getHighestPrioritySessions(recipientJID.getNode());
if (sessions.isEmpty()) {
// No session is available so store offline
messageStrategy.storeOffline(packet);
}
else if (sessions.size() == 1) {
// Found only one session so deliver message
sessions.get(0).process(packet);
}
else {
// Many sessions have the highest priority (be smart now) :)
if (!JiveGlobals.getBooleanProperty("route.all-resources", false)) {
// Sort sessions by show value (e.g. away, xa)
Collections.sort(sessions, new Comparator<ClientSession>() {
public int compare(ClientSession o1, ClientSession o2) {
int thisVal = getShowValue(o1);
int anotherVal = getShowValue(o2);
return (thisVal<anotherVal ? -1 : (thisVal==anotherVal ? 0 : 1));
}
/**
* Priorities are: chat, available, away, xa, dnd.
*/
private int getShowValue(ClientSession session) {
Presence.Show show = session.getPresence().getShow();
if (show == Presence.Show.chat) {
return 1;
}
else if (show == null) {
return 2;
}
else if (show == Presence.Show.away) {
return 3;
}
else if (show == Presence.Show.xa) {
return 4;
}
else {
return 5;
}
}
});
// Get same sessions with same max show value
List<ClientSession> targets = new ArrayList<ClientSession>();
Presence.Show showFilter = sessions.get(0).getPresence().getShow();
for (ClientSession session : sessions) {
if (session.getPresence().getShow() == showFilter) {
targets.add(session);
}
else {
break;
}
}
// Get session with most recent activity (and highest show value)
Collections.sort(targets, new Comparator<ClientSession>() {
public int compare(ClientSession o1, ClientSession o2) {
return o1.getLastActiveDate().compareTo(o2.getLastActiveDate());
}
});
// Deliver stanza to session with highest priority, highest show value and most recent activity
targets.get(0).process(packet);
}
else {
// Deliver stanza to all connected resources with highest priority
for (ClientSession session : sessions) {
session.process(packet);
}
}
}
}
/**
* Forwards the received message to the list of users defined in the property
* <b>xmpp.forward.admins</b>. The property may include bare JIDs or just usernames separated
......
......@@ -11,9 +11,6 @@
package org.jivesoftware.openfire;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.openfire.audit.AuditStreamIDFactory;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.component.InternalComponentManager;
......@@ -27,6 +24,9 @@ import org.jivesoftware.openfire.session.*;
import org.jivesoftware.openfire.spi.BasicStreamIDFactory;
import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
import org.xmpp.packet.Packet;
......@@ -393,7 +393,8 @@ public class SessionManager extends BasicModule {
*
* @param filterAvailable flag that indicates if only available sessions should be
* considered.
* @return the default session for the user.
* @return the default session for the user or null if no session with presence priority
* greater than 0 was found.
*/
ClientSession getDefaultSession(boolean filterAvailable) {
if (priorityList.isEmpty()) {
......@@ -401,14 +402,22 @@ public class SessionManager extends BasicModule {
}
if (!filterAvailable) {
return resources.get(priorityList.getFirst());
ClientSession session = resources.get(priorityList.getFirst());
// Only consider sessions with positive presence priorities
if (session.getPresence().getPriority() >= 0) {
return session;
}
return null;
}
else {
synchronized (priorityList) {
for (int i=0; i < priorityList.size(); i++) {
ClientSession s = resources.get(priorityList.get(i));
for (String resource : priorityList) {
ClientSession s = resources.get(resource);
if (s != null && s.getPresence().isAvailable()) {
return s;
// Only consider sessions with positive presence priorities
if (s.getPresence().getPriority() >= 0) {
return s;
}
}
}
}
......@@ -416,6 +425,33 @@ public class SessionManager extends BasicModule {
}
}
/**
* Returns client sessions of the user that have the same highest priority.
*
* @return client sessions of the user that have the same highest priority.
*/
List<ClientSession> getHighestPrioritySessions() {
if (priorityList.isEmpty()) {
return Collections.emptyList();
}
List<ClientSession> answer = new ArrayList<ClientSession>();
int highest = -1;
synchronized (priorityList) {
for (String resource : priorityList) {
ClientSession s = resources.get(resource);
if (s != null && s.getPresence().isAvailable()) {
// Only consider sessions with positive presence priorities
if (s.getPresence().getPriority() >= 0 && s.getPresence().getPriority() >= highest) {
highest = s.getPresence().getPriority();
answer.add(s);
}
}
}
}
return answer;
}
/**
* Determines if this map is empty or not.
*
......@@ -740,10 +776,11 @@ public class SessionManager extends BasicModule {
// Update the order of the sessions based on the new presence of this session
sessionMap.sessionAvailable(session);
defaultSession = sessionMap.getDefaultSession(true);
JID node = new JID(defaultSession.getAddress().getNode(),
defaultSession.getAddress().getDomain(), null);
// Add route to default session (used when no resource is specified)
routingTable.addRoute(node, defaultSession);
if (defaultSession != null) {
// Add route to default session (used when no resource is specified)
JID node = new JID(session.getAddress().getNode(), session.getAddress().getDomain(), null);
routingTable.addRoute(node, defaultSession);
}
// Add route to the new session
routingTable.addRoute(session.getAddress(), session);
// Broadcast presence between the user's resources
......@@ -841,9 +878,16 @@ public class SessionManager extends BasicModule {
sessionMap.sessionUnavailable(session);
// Update the route for the session's BARE address
Session defaultSession = sessionMap.getDefaultSession(true);
routingTable.addRoute(new JID(defaultSession.getAddress().getNode(),
defaultSession.getAddress().getDomain(), ""),
defaultSession);
JID jid =
new JID(defaultSession.getAddress().getNode(), defaultSession.getAddress().getDomain(), "");
if (defaultSession != null) {
// Set the route to the bare JID to the session with highest priority
routingTable.addRoute(jid, defaultSession);
}
else {
// All sessions have a negative priority presence so delete the route to the bare JID
routingTable.removeRoute(jid);
}
}
}
catch (UserNotFoundException e) {
......@@ -876,9 +920,30 @@ public class SessionManager extends BasicModule {
defaultSession = resources.getDefaultSession(true);
}
// Update the route to the bareJID with the session with highest priority
routingTable.addRoute(new JID(defaultSession.getAddress().getNode(),
defaultSession.getAddress().getDomain(), ""),
defaultSession);
JID defaultAddress =
new JID(defaultSession.getAddress().getNode(), defaultSession.getAddress().getDomain(), "");
// Update the route to the bare JID
if (defaultSession != null) {
boolean hadDefault = routingTable.getRoute(defaultAddress) != null;
// Set the route to the bare JID to the session with highest priority
routingTable.addRoute(defaultAddress, defaultSession);
// Check if we need to deliver offline messages
if (!hadDefault) {
// User sessions had negative presence before this change so deliver messages
ClientSession session = resources.getSession(sender.getResource());
if (session != null && session.canFloodOfflineMessages()) {
OfflineMessageStore messageStore = XMPPServer.getInstance().getOfflineMessageStore();
Collection<OfflineMessage> messages = messageStore.getMessages(username, true);
for (Message message : messages) {
session.process(message);
}
}
}
}
else {
// All sessions have a negative priority presence so delete the route to the bare JID
routingTable.removeRoute(defaultAddress);
}
}
......@@ -1236,6 +1301,20 @@ public class SessionManager extends BasicModule {
return sessionList;
}
/**
* Returns client sessions of the user that have the same highest priority.
*
* @param username the user.
* @return client sessions of the user that have the same highest priority.
*/
public List<ClientSession> getHighestPrioritySessions(String username) {
SessionMap sessionMap = sessions.get(username);
if (sessionMap != null) {
return sessionMap.getHighestPrioritySessions();
}
return Collections.emptyList();
}
/**
* Returns number of client sessions that are connected to the server. Sessions that
* are authenticated and not authenticated will be included
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment