📄 sessionmanager.java
字号:
/**
* $RCSfile$
* $Revision: 3170 $
* $Date: 2005-12-07 14:00:58 -0300 (Wed, 07 Dec 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;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.wildfire.audit.AuditStreamIDFactory;
import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.jivesoftware.wildfire.component.ComponentSession;
import org.jivesoftware.wildfire.component.InternalComponentManager;
import org.jivesoftware.wildfire.container.BasicModule;
import org.jivesoftware.wildfire.event.SessionEventDispatcher;
import org.jivesoftware.wildfire.handler.PresenceUpdateHandler;
import org.jivesoftware.wildfire.multiplex.ConnectionMultiplexerManager;
import org.jivesoftware.wildfire.multiplex.ConnectionMultiplexerSession;
import org.jivesoftware.wildfire.net.SocketConnection;
import org.jivesoftware.wildfire.server.IncomingServerSession;
import org.jivesoftware.wildfire.server.OutgoingServerSession;
import org.jivesoftware.wildfire.server.OutgoingSessionPromise;
import org.jivesoftware.wildfire.spi.BasicStreamIDFactory;
import org.jivesoftware.wildfire.user.UserManager;
import org.jivesoftware.wildfire.user.UserNotFoundException;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
import org.xmpp.packet.Packet;
import org.xmpp.packet.Presence;
import java.net.InetAddress;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Manages the sessions associated with an account. The information
* maintained by the Session manager is entirely transient and does
* not need to be preserved between server restarts.
*
* @author Derek DeMoro
*/
public class SessionManager extends BasicModule {
public static final int NEVER_KICK = -1;
private PresenceUpdateHandler presenceHandler;
private PacketRouter router;
private String serverName;
private JID serverAddress;
private UserManager userManager;
private int conflictLimit;
/**
* Counter of user sessions. A session is counted just after it was created and not
* after the user came available.
*/
private final AtomicInteger usersSessionsCounter = new AtomicInteger(0);
private ClientSessionListener clientSessionListener = new ClientSessionListener();
private ComponentSessionListener componentSessionListener = new ComponentSessionListener();
private IncomingServerSessionListener incomingServerListener = new IncomingServerSessionListener();
private OutgoingServerSessionListener outgoingServerListener = new OutgoingServerSessionListener();
private ConnectionMultiplexerSessionListener multiplexerSessionListener = new ConnectionMultiplexerSessionListener();
/**
* Map that holds sessions that has been created but haven't been authenticated yet. The Map
* will hold client sessions.
*/
private Map<String, ClientSession> preAuthenticatedSessions = new ConcurrentHashMap<String, ClientSession>();
/**
* Map of priority ordered SessionMap objects with username (toLowerCase) as key. The sessions
* contained in this Map are client sessions. For each username a SessionMap is kept which
* tracks the session for each user resource.
*/
private Map<String, SessionMap> sessions = new ConcurrentHashMap<String, SessionMap>();
/**
* Map of anonymous server sessions. They need to be treated separately as they
* have no associated user, and don't follow the normal routing rules for
* priority based fall over. The sessions contained in this Map are client sessions.
*/
private Map<String, ClientSession> anonymousSessions = new ConcurrentHashMap<String, ClientSession>();
/**
* The sessions contained in this List are component sessions. For each connected component
* this Map will keep the component's session.
*/
private List<ComponentSession> componentsSessions = new CopyOnWriteArrayList<ComponentSession>();
/**
* Map of connection multiplexer sessions grouped by connection managers. Each connection
* manager may have many connections to the server (i.e. connection pool). All connections
* originated from the same connection manager are grouped as a single entry in the map.
* Once all connections have been closed users that were logged using the connection manager
* will become unavailable.
*/
private Map<String, List<ConnectionMultiplexerSession>> connnectionManagerSessions =
new ConcurrentHashMap<String, List<ConnectionMultiplexerSession>>();
/**
* The sessions contained in this Map are server sessions originated by a remote server. These
* sessions can only receive packets from the remote server but are not capable of sending
* packets to the remote server. Sessions will be added to this collecion only after they were
* authenticated. The key of the Map is the hostname of the remote server. The value is a
* list of IncomingServerSession that will keep each session created by a remote server to
* this server.
*/
private final Map<String, List<IncomingServerSession>> incomingServerSessions =
new ConcurrentHashMap<String, List<IncomingServerSession>>();
/**
* The sessions contained in this Map are server sessions originated from this server to remote
* servers. These sessions can only send packets to the remote server but are not capable of
* receiving packets from the remote server. Sessions will be added to this collecion only
* after they were authenticated. The key of the Map is the hostname of the remote server.
*/
private Map<String, OutgoingServerSession> outgoingServerSessions = new ConcurrentHashMap<String, OutgoingServerSession>();
/**
* <p>Session manager must maintain the routing table as sessions are added and
* removed.</p>
*/
private RoutingTable routingTable;
private StreamIDFactory streamIDFactory;
/**
* Timer that will clean up dead or inactive sessions. Currently only outgoing server sessions
* will be analyzed.
*/
private Timer timer = new Timer("Sessions cleanup");
/**
* Task that closes idle server sessions.
*/
private ServerCleanupTask serverCleanupTask;
/**
* Returns the instance of <CODE>SessionManagerImpl</CODE> being used by the XMPPServer.
*
* @return the instance of <CODE>SessionManagerImpl</CODE> being used by the XMPPServer.
*/
public static SessionManager getInstance() {
return XMPPServer.getInstance().getSessionManager();
}
public SessionManager() {
super("Session Manager");
if (JiveGlobals.getBooleanProperty("xmpp.audit.active")) {
streamIDFactory = new AuditStreamIDFactory();
}
else {
streamIDFactory = new BasicStreamIDFactory();
}
String conflictLimitProp = JiveGlobals.getProperty("xmpp.session.conflict-limit");
if (conflictLimitProp == null) {
conflictLimit = 0;
JiveGlobals.setProperty("xmpp.session.conflict-limit", Integer.toString(conflictLimit));
}
else {
try {
conflictLimit = Integer.parseInt(conflictLimitProp);
}
catch (NumberFormatException e) {
conflictLimit = 0;
JiveGlobals.setProperty("xmpp.session.conflict-limit", Integer.toString(conflictLimit));
}
}
}
/**
* Returns the session originated from the specified address. If the address contains
* a resource then the exact session is going to be looked for and if none is found then
* <tt>null</tt> is going to be returned. On the other hand, if the address is just a domain
* then any session originated from the connection manager is going to be returned and if
* the connection manager has no sessions then <tt>null</tt> is going to be returned.
*
* @param address the address of the connection manager (no resource included) or a specific
* session of the connection manager (resource included).
* @return the session originated from the specified address.
*/
public ConnectionMultiplexerSession getConnectionMultiplexerSession(JID address) {
List<ConnectionMultiplexerSession> sessions =
connnectionManagerSessions.get(address.getDomain());
if (sessions == null || sessions.isEmpty()) {
return null;
}
if (address.getResource() != null) {
// Look for the exact session
for (ConnectionMultiplexerSession session : sessions) {
if (session.getAddress().equals(address)) {
return session;
}
}
return null;
}
else {
// Look for any session of the connection manager
return sessions.get(0);
}
}
/**
* Returns all sessions originated from connection managers.
*
* @return all sessions originated from connection managers.
*/
public List<ConnectionMultiplexerSession> getConnectionMultiplexerSessions() {
if (connnectionManagerSessions.isEmpty()) {
return Collections.emptyList();
}
List<ConnectionMultiplexerSession> answer = new ArrayList<ConnectionMultiplexerSession>();
for (List<ConnectionMultiplexerSession> sessions : connnectionManagerSessions.values()) {
answer.addAll(sessions);
}
return answer;
}
/**
* Returns a collection with all the sessions originated from the connection manager
* whose domain matches the specified domain. If there is no connection manager with
* the specified domain then an empty list is going to be returned.
*
* @param domain the domain of the connection manager.
* @return a collection with all the sessions originated from the connection manager
* whose domain matches the specified domain.
*/
public List<ConnectionMultiplexerSession> getConnectionMultiplexerSessions(String domain) {
List<ConnectionMultiplexerSession> sessions = connnectionManagerSessions.get(domain);
if (sessions == null || sessions.isEmpty()) {
return Collections.emptyList();
}
return new ArrayList<ConnectionMultiplexerSession>(sessions);
}
/**
* Returns the IP address of the connection manager whose domain matches the
* specified domain.
*
* @param domain the domain of the connection manager.
* @return the IP address of the connection manager.
*/
public InetAddress getConnectionMultiplexerInetAddress(String domain) {
List<ConnectionMultiplexerSession> sessions = connnectionManagerSessions.get(domain);
if (sessions == null || sessions.isEmpty()) {
return null;
}
return sessions.get(0).getConnection().getInetAddress();
}
/**
* Creates a new <tt>ConnectionMultiplexerSession</tt>.
*
* @param conn the connection to create the session from.
* @param address the JID (may include a resource) of the connection manager's session.
* @return a newly created session.
* @throws UnauthorizedException
*/
public ConnectionMultiplexerSession createMultiplexerSession(SocketConnection conn, JID address)
throws UnauthorizedException {
if (serverName == null) {
throw new UnauthorizedException("Server not initialized");
}
StreamID id = nextStreamID();
ConnectionMultiplexerSession session = new ConnectionMultiplexerSession(serverName, conn, id);
conn.init(session);
// Register to receive close notification on this session so we can
// figure out when users that were using this connection manager may become unavailable
conn.registerCloseListener(multiplexerSessionListener, session);
// Add to connection multiplexer session.
List<ConnectionMultiplexerSession> sessions =
connnectionManagerSessions.get(address.getDomain());
if (sessions == null) {
synchronized (address.getDomain().intern()) {
sessions = connnectionManagerSessions.get(address.getDomain());
if (sessions == null) {
sessions = new CopyOnWriteArrayList<ConnectionMultiplexerSession>();
connnectionManagerSessions.put(address.getDomain(), sessions);
// Notify ConnectionMultiplexerManager that a new connection manager
// is available
ConnectionMultiplexerManager.getInstance()
.multiplexerAvailable(address.getDomain());
}
}
}
sessions.add(session);
return session;
}
/**
* Simple data structure to track sessions for a single user (tracked by resource
* and priority).
*/
private class SessionMap {
private Map<String,ClientSession> resources = new ConcurrentHashMap<String,ClientSession>();
private final LinkedList<String> priorityList = new LinkedList<String>();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -