/*
* Copyright (C) 2010 Moduad Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.androidpn.server.xmpp.session;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.androidpn.server.xmpp.XmppServer;
import org.androidpn.server.xmpp.net.Connection;
import org.androidpn.server.xmpp.net.ConnectionCloseListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xmpp.packet.JID;
/**
* This class manages the sessions connected to the server.
*
* @author Sehwan Noh (devnoh@gmail.com)
*/
public class SessionManager {
private static final Log log = LogFactory.getLog(SessionManager.class);
private static final String RESOURCE_NAME = "AndroidpnClient";
private static SessionManager instance;
private String serverName;
private Map<String, ClientSession> preAuthSessions = new ConcurrentHashMap<String, ClientSession>();
private Map<String, ClientSession> clientSessions = new ConcurrentHashMap<String, ClientSession>();
private final AtomicInteger connectionsCounter = new AtomicInteger(0);
private ClientSessionListener clientSessionListener = new ClientSessionListener();
private SessionManager() {
serverName = XmppServer.getInstance().getServerName();
}
/**
* Returns the singleton instance of SessionManager.
*
* @return the instance
*/
public static SessionManager getInstance() {
if (instance == null) {
synchronized (SessionManager.class) {
instance = new SessionManager();
}
}
return instance;
}
/**
* Creates a new ClientSession and returns it.
*
* @param conn the connection
* @return a newly created session
*/
public ClientSession createClientSession(Connection conn) {
if (serverName == null) {
throw new IllegalStateException("Server not initialized");
}
Random random = new Random();
String streamId = Integer.toHexString(random.nextInt());
ClientSession session = new ClientSession(serverName, conn, streamId);
conn.init(session);
conn.registerCloseListener(clientSessionListener);
// Add to pre-authenticated sessions
preAuthSessions.put(session.getAddress().getResource(), session);
// Increment the counter of user sessions
connectionsCounter.incrementAndGet();
log.debug("ClientSession created.");
return session;
}
/**
* Adds a new session that has been authenticated.
*
* @param session the session
*/
public void addSession(ClientSession session) {
preAuthSessions.remove(session.getStreamID().toString());
clientSessions.put(session.getAddress().toString(), session);
}
/**
* Returns the session associated with the username.
*
* @param username the username of the client address
* @return the session associated with the username
*/
public ClientSession getSession(String username) {
// return getSession(new JID(username, serverName, null, true));
return getSession(new JID(username, serverName, RESOURCE_NAME, true));
}
/**
* Returns the session associated with the JID.
*
* @param from the client address
* @return the session associated with the JID
*/
public ClientSession getSession(JID from) {
if (from == null || serverName == null
|| !serverName.equals(from.getDomain())) {
return null;
}
// Check pre-authenticated sessions
if (from.getResource() != null) {
ClientSession session = preAuthSessions.get(from.getResource());
if (session != null) {
return session;
}
}
if (from.getResource() == null || from.getNode() == null) {
return null;
}
return clientSessions.get(from.toString());
}
/**
* Returns a list that contains all authenticated client sessions.
*
* @return a list that contains all client sessions
*/
public Collection<ClientSession> getSessions() {
return clientSessions.values();
}
/**
* Removes a client session.
*
* @param session the session to be removed
* @return true if the session was successfully removed
*/
public boolean removeSession(ClientSession session) {
if (session == null || serverName == null) {
return false;
}
JID fullJID = session.getAddress();
// Remove the session from list
boolean clientRemoved = clientSessions.remove(fullJID.toString()) != null;
boolean preAuthRemoved = (preAuthSessions.remove(fullJID.getResource()) != null);
// Decrement the counter of user sessions
if (clientRemoved || preAuthRemoved) {
connectionsCounter.decrementAndGet();
return true;
}
return false;
}
/**
* Closes the all sessions.
*/
public void closeAllSessions() {
try {
// Send the close stream header to all connections
Set<ClientSession> sessions = new HashSet<ClientSession>();
sessions.addAll(preAuthSessions.values());
sessions.addAll(clientSessions.values());
for (ClientSession session : sessions) {
try {
session.getConnection().systemShutdown();
} catch (Throwable t) {
}
}
} catch (Exception e) {
}
}
/**
* A listner to handle a session that has been closed.
*/
private class ClientSessionListener implements ConnectionCloseListener {
public void onConnectionClose(Object handback) {
try {
ClientSession session = (ClientSession) handback;
removeSession(session);
} catch (Exception e) {
log.error("Could not close socket", e);
}
}
}
}