📄 sessionmanager.java
字号:
/**
* $RCSfile$
* $Revision: 3170 $
* $Date: 2005-12-07 14:00:58 -0300 (Wed, 07 Dec 2005) $
*
* Copyright (C) 2008 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, or a commercial license
* agreement with Jive.
*/
package org.jivesoftware.openfire;
import org.jivesoftware.openfire.audit.AuditStreamIDFactory;
import org.jivesoftware.openfire.auth.AuthToken;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.cluster.ClusterEventListener;
import org.jivesoftware.openfire.cluster.ClusterManager;
import org.jivesoftware.openfire.component.InternalComponentManager;
import org.jivesoftware.openfire.container.BasicModule;
import org.jivesoftware.openfire.event.SessionEventDispatcher;
import org.jivesoftware.openfire.http.HttpSession;
import org.jivesoftware.openfire.multiplex.ConnectionMultiplexerManager;
import org.jivesoftware.openfire.server.OutgoingSessionPromise;
import org.jivesoftware.openfire.session.*;
import org.jivesoftware.openfire.spi.BasicStreamIDFactory;
import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.cache.Cache;
import org.jivesoftware.util.cache.CacheFactory;
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.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
/**
* 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 implements ClusterEventListener {
public static final String COMPONENT_SESSION_CACHE_NAME = "Components Sessions";
public static final String CM_CACHE_NAME = "Connection Managers Sessions";
public static final String ISS_CACHE_NAME = "Incoming Server Sessions";
public static final String C2S_INFO_CACHE_NAME = "Client Session Info Cache";
public static final int NEVER_KICK = -1;
private XMPPServer server;
private PacketRouter router;
private String serverName;
private JID serverAddress;
private UserManager userManager;
private int conflictLimit;
/**
* Counter of user connections. A connection is counted just after it was created and not
* after the user became available. This counter only considers sessions local to this JVM.
* That means that when running inside of a cluster you will need to add up this counter
* for each cluster node.
*/
private final AtomicInteger connectionsCounter = new AtomicInteger(0);
/**
* Cache (unlimited, never expire) that holds information about client sessions (as soon as
* a resource has been bound). The cache is used by Remote sessions to avoid generating big
* number of remote calls.
* Key: full JID, Value: ClientSessionInfo
*/
private Cache<String, ClientSessionInfo> sessionInfoCache;
/**
* Cache (unlimited, never expire) that holds external component sessions.
* Key: component address, Value: nodeID
*/
private Cache<String, byte[]> componentSessionsCache;
/**
* Cache (unlimited, never expire) that holds sessions of connection managers. For each
* socket connection of the CM to the server there is going to be an entry in the cache.
* Key: full address of the CM that identifies the socket, Value: nodeID
*/
private Cache<String, byte[]> multiplexerSessionsCache;
/**
* Cache (unlimited, never expire) that holds incoming sessions of remote servers.
* Key: stream ID that identifies the socket/session, Value: nodeID
*/
private Cache<String, byte[]> incomingServerSessionsCache;
/**
* Cache (unlimited, never expire) that holds list of incoming sessions
* originated from the same remote server (domain/subdomain). For instance, jabber.org
* may have 2 connections to the server running in jivesoftware.com (one socket to
* jivesoftware.com and the other socket to conference.jivesoftware.com).
* Key: remote hostname (domain/subdomain), Value: list of stream IDs that identify each socket.
*/
private Cache<String, List<String>> hostnameSessionsCache;
/**
* Cache (unlimited, never expire) that holds domains, subdomains and virtual
* hostnames of the remote server that were validated with this server for each
* incoming server session.
* Key: stream ID, Value: Domains and subdomains of the remote server that were
* validated with this server.<p>
*
* This same information is stored in {@link LocalIncomingServerSession} but the
* reason for this duplication is that when running in a cluster other nodes
* will have access to this clustered cache even in the case of this node going
* down.
*/
private Cache<String, Set<String>> validatedDomainsCache;
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();
/**
* Local session manager responsible for keeping sessions connected to this JVM that are not
* present in the routing table.
*/
private LocalSessionManager localSessionManager;
/**
* <p>Session manager must maintain the routing table as sessions are added and
* removed.</p>
*/
private RoutingTable routingTable;
private StreamIDFactory streamIDFactory;
/**
* 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();
}
localSessionManager = new LocalSessionManager();
conflictLimit = JiveGlobals.getIntProperty("xmpp.session.conflict-limit", 0);
}
/**
* Returns the session originated from the specified address or <tt>null</tt> if none was
* found. The specified address MUST contain a resource that uniquely identifies the session.
*
* A single connection manager should connect to the same node.
*
* @param address the address of the connection manager (including resource that identifies specific socket)
* @return the session originated from the specified address.
*/
public ConnectionMultiplexerSession getConnectionMultiplexerSession(JID address) {
// Search in the list of CMs connected to this JVM
LocalConnectionMultiplexerSession session =
localSessionManager.getConnnectionManagerSessions().get(address.toString());
if (session == null && server.getRemoteSessionLocator() != null) {
// Search in the list of CMs connected to other cluster members
byte[] nodeID = multiplexerSessionsCache.get(address.toString());
if (nodeID != null) {
return server.getRemoteSessionLocator().getConnectionMultiplexerSession(nodeID, address);
}
}
return null;
}
/**
* Returns all sessions originated from connection managers.
*
* @return all sessions originated from connection managers.
*/
public List<ConnectionMultiplexerSession> getConnectionMultiplexerSessions() {
List<ConnectionMultiplexerSession> sessions = new ArrayList<ConnectionMultiplexerSession>();
// Add sessions of CMs connected to this JVM
sessions.addAll(localSessionManager.getConnnectionManagerSessions().values());
// Add sessions of CMs connected to other cluster nodes
RemoteSessionLocator locator = server.getRemoteSessionLocator();
if (locator != null) {
for (Map.Entry<String, byte[]> entry : multiplexerSessionsCache.entrySet()) {
if (!server.getNodeID().equals(entry.getValue())) {
sessions.add(locator.getConnectionMultiplexerSession(entry.getValue(), new JID(entry.getKey())));
}
}
}
return sessions;
}
/**
* 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 = new ArrayList<ConnectionMultiplexerSession>();
// Add sessions of CMs connected to this JVM
for (String address : localSessionManager.getConnnectionManagerSessions().keySet()) {
JID jid = new JID(address);
if (domain.equals(jid.getDomain())) {
sessions.add(localSessionManager.getConnnectionManagerSessions().get(address));
}
}
// Add sessions of CMs connected to other cluster nodes
RemoteSessionLocator locator = server.getRemoteSessionLocator();
if (locator != null) {
for (Map.Entry<String, byte[]> entry : multiplexerSessionsCache.entrySet()) {
if (!server.getNodeID().equals(entry.getValue())) {
JID jid = new JID(entry.getKey());
if (domain.equals(jid.getDomain())) {
sessions.add(
locator.getConnectionMultiplexerSession(entry.getValue(), new JID(entry.getKey())));
}
}
}
}
return sessions;
}
/**
* 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.
*/
public LocalConnectionMultiplexerSession createMultiplexerSession(Connection conn, JID address) {
if (serverName == null) {
throw new IllegalStateException("Server not initialized");
}
StreamID id = nextStreamID();
LocalConnectionMultiplexerSession session = new LocalConnectionMultiplexerSession(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.
boolean firstConnection = getConnectionMultiplexerSessions(address.getDomain()).isEmpty();
localSessionManager.getConnnectionManagerSessions().put(address.toString(), session);
// Keep track of the cluster node hosting the new CM connection
multiplexerSessionsCache.put(address.toString(), server.getNodeID().toByteArray());
if (firstConnection) {
// Notify ConnectionMultiplexerManager that a new connection manager
// is available
ConnectionMultiplexerManager.getInstance().multiplexerAvailable(address.getDomain());
}
return session;
}
/**
* Returns a randomly created ID to be used in a stream element.
*
* @return a randomly created ID to be used in a stream element.
*/
public StreamID nextStreamID() {
return streamIDFactory.createStreamID();
}
/**
* Creates a new <tt>ClientSession</tt>. The new Client session will have a newly created
* stream ID.
*
* @param conn the connection to create the session from.
* @return a newly created session.
*/
public LocalClientSession createClientSession(Connection conn) {
return createClientSession(conn, nextStreamID());
}
/**
* Creates a new <tt>ClientSession</tt> with the specified streamID.
*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -