⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sessionmanager.java

📁 基于Jabber协议的即时消息服务器
💻 JAVA
📖 第 1 页 / 共 5 页
字号:
/**
 * $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 + -