/*
* Tigase Jabber/XMPP Server
* Copyright (C) 2004-2012 "Artur Hefczyc" <artur.hefczyc@tigase.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. Look for COPYING file in the top folder.
* If not, see http://www.gnu.org/licenses/.
*
* $Rev$
* Last modified by $Author$
* $Date$
*/
package tigase.xmpp;
import tigase.util.TigaseStringprepException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Describe class XMPPSession here.
*
*
* Created: Wed Feb 8 22:14:28 2006
*
* @author <a href="mailto:artur.hefczyc@tigase.org">Artur Hefczyc</a>
* @version $Rev$
*/
public class XMPPSession {
/**
* Private logger for class instances.
*/
private static Logger log = Logger.getLogger(XMPPSession.class.getName());
private CopyOnWriteArrayList<XMPPResourceConnection> activeResources = null;
private long creationTime = 0;
private Map<String, Object> sessionData = null;
/**
* User name - part of user's JID
*/
private String username = null;
private long packets_counter = 0;
/**
* Creates a new <code>XMPPSession</code> instance.
*
*
* @param username
*/
public XMPPSession(final String username) {
sessionData = new ConcurrentHashMap<String, Object>();
activeResources = new CopyOnWriteArrayList<XMPPResourceConnection>();
this.username = username;
this.creationTime = System.currentTimeMillis();
}
/**
* This method is called each time the resource is set for connection.
*
* @param conn
* @throws TigaseStringprepException
*/
public void addResourceConnection(XMPPResourceConnection conn)
throws TigaseStringprepException {
if (log.isLoggable(Level.FINEST)) {
log.finest("Adding resource connection for username : " + username + ", id: " + conn);
}
// There is a bug somewhere which causes to allow for 2 or more connections
// with the same resource. Let's try to catch the case here and fix it....
String resource = conn.getResource();
if (resource != null) {
ArrayDeque<XMPPResourceConnection> old_ress = new ArrayDeque<XMPPResourceConnection>();
for (XMPPResourceConnection act_conn : activeResources) {
if (log.isLoggable(Level.FINEST)) {
log.finest("Resource checking: " + act_conn.getResource() + ", connectionID: "
+ act_conn);
}
if (resource.equalsIgnoreCase(act_conn.getResource())) {
old_ress.add(act_conn);
} // end of if (resource.equals(conn.getResource()))
} // end of for (XMPPResourceConnection conn: activeResources)
XMPPResourceConnection old_res = null;
while ((old_res = old_ress.poll()) != null) {
// If they are equal, just ignore this. It may happen only for USER_STATUS
// command where the user session is artificially created....
if (old_res != conn) {
if (log.isLoggable(Level.FINEST)) {
log.finest("Found old resource connection, id: " + old_res);
}
try {
old_res.putSessionData(XMPPResourceConnection.ERROR_KEY, "conflict");
old_res.logout();
} catch (Exception e) {
log.log(Level.INFO, "Exception during closing old connection, ignoring.", e);
}
removeResourceConnection(old_res);
}
}
}
// The connection could have been already added with null resource
// to avoid adding it twice let's check if it is already there
XMPPResourceConnection old_res;
try {
old_res = getResourceForConnectionId(conn.getConnectionId());
} catch (NoConnectionIdException ex) {
old_res = null;
}
if (old_res == null) {
activeResources.add(conn);
conn.setParentSession(this);
}
if (log.isLoggable(Level.FINEST)) {
log.finest("Number of active resources is: " + activeResources.size());
if (activeResources.size() > 1) {
int i = 0;
for (XMPPResourceConnection res : activeResources) {
log.finest("RES " + (++i) + ": " + res);
} // end of for (XMPPResourceConnection res: activeResources)
} // end of if (activeResources.size() > 1)
}
}
/**
* Method description
*
*
* @return
*/
@SuppressWarnings({ "unchecked" })
public List<XMPPResourceConnection> getActiveResources() {
return (List<XMPPResourceConnection>) activeResources.clone();
}
/**
* Method description
*
*
* @return
*/
public int getActiveResourcesSize() {
return activeResources.size();
}
/**
*
* @param key
* @return
*/
public Object getCommonSessionData(String key) {
return sessionData.get(key);
}
/**
* Method description
*
*
* @return
*
*/
public JID[] getConnectionIds() {
JID[] result = new JID[activeResources.size()];
int idx = 0;
for (XMPPResourceConnection conn : activeResources) {
try {
result[idx] = conn.getConnectionId();
++idx;
} catch (NoConnectionIdException ex) {
// Skip connection with no connectionId set
}
} // end of for (XMPPResourceConnection conn: activeResources)
return result;
}
/**
* Method description
*
*
* @return
*
*/
public JID[] getJIDs() {
JID[] result = new JID[activeResources.size()];
int idx = 0;
for (XMPPResourceConnection conn : activeResources) {
result[idx++] = conn.getjid();
} // end of for (XMPPResourceConnection conn: activeResources)
return result;
}
/**
* Method description
*
*
* @return
*/
public long getLiveTime() {
return (System.currentTimeMillis() - creationTime);
}
/**
* Method description
*
*
* @param jid
*
* @return
*/
public synchronized XMPPResourceConnection getResourceConnection(JID jid) {
if (log.isLoggable(Level.FINEST)) {
log.finest("Called for: " + jid);
}
if (activeResources.size() == 0) {
return null;
} // end of if (activeResources.size() == 0)
if (activeResources.size() == 1) {
XMPPResourceConnection result = activeResources.get(0);
if (log.isLoggable(Level.FINEST)) {
log.finest("Only 1 active resource: " + result.getResource());
}
return result;
} // end of if (activeResources.size() == 1)
XMPPResourceConnection conn = getResourceForJID(jid);
if (conn != null) {
if (log.isLoggable(Level.FINEST)) {
log.finest("Number of resources: " + activeResources.size()
+ ", got resource for jid: " + jid);
}
return conn;
} // end of if (conn != null)
// There is no active resource for this jid, so let's return
// connection with the highest priority:
ArrayList<XMPPResourceConnection> al = new ArrayList<XMPPResourceConnection>();
// al.add(activeResources.get(0));
// int highest_priority = al.get(0).getPriority();
int highest_priority = 0;
for (Iterator<XMPPResourceConnection> it = activeResources.iterator(); it.hasNext(); ) {
XMPPResourceConnection conn_tmp = it.next();
if ( !conn_tmp.isAuthorized()) {
log.info("Old XMPP connection which is not authorized anymore, removing..."
+ conn_tmp);
activeResources.remove(conn_tmp);
}
if (conn_tmp.getPriority() == highest_priority) {
al.add(conn_tmp);
continue;
} // end of if (conn_tmp.getPriority() == highest_priority)
if (conn_tmp.getPriority() > highest_priority) {
al.clear();
al.add(conn_tmp);
highest_priority = conn_tmp.getPriority();
}
}
if (al.size() == 1) {
// We found 1 connection with highest priority
return al.get(0);
} // end of if (al.size() == 1)
// We have a few connections with the same highest priority
// Let's return the one which was the most recently used.
XMPPResourceConnection conn_last = al.get(0);
long time = conn_last.getLastAccessed();
for (int i = 1; i < al.size(); ++i) {
if (al.get(i).getLastAccessed() > time) {
conn_last = al.get(i);
time = conn_last.getLastAccessed();
} // end of if (al.get(i).getLastAccessed() > time)
}
return conn_last;
}
/**
* Method description
*
*
* @param connectionId
*
* @return
*/
public XMPPResourceConnection getResourceForConnectionId(JID connectionId) {
try {
for (XMPPResourceConnection conn : activeResources) {
if (connectionId.equals(conn.getConnectionId())) {
return conn;
} // end of if (resource.equals(conn.getResource()))
} // end of for (XMPPResourceConnection conn: activeResources)
} catch (NoConnectionIdException ex) {
//Logger.getLogger(XMPPSession.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
/**
* Method description
*
*
* @param jid
*
* @return
*/
public XMPPResourceConnection getResourceForJID(JID jid) {
final String resource = jid.getResource();
return getResourceForResource(resource);
}
/**
* Method description
*
*
* @param resource
*
* @return
*/
public XMPPResourceConnection getResourceForResource(String resource) {
if ((resource != null) && (resource.length() > 0)) {
for (XMPPResourceConnection conn : activeResources) {
if (log.isLoggable(Level.FINEST)) {
log.finest("Resource checking: " + conn.getResource() + ", connectionID: " + conn);
}
if (resource.equalsIgnoreCase(conn.getResource())) {
return conn;
} // end of if (resource.equals(conn.getResource()))
} // end of for (XMPPResourceConnection conn: activeResources)
} // end of if (resource.length() > 0)
return null;
}
/**
* Method description
*
*
* @return
*/
public String getUserName() {
return username;
}
/**
* Method description
*
*
* @param conn
*/
public void removeResourceConnection(XMPPResourceConnection conn) {
activeResources.remove(conn);
conn.removeParentSession(null);
}
/**
* Method description
*
*
* @param conn
*/
public void streamClosed(XMPPResourceConnection conn) {
removeResourceConnection(conn);
}
protected void putCommonSessionData(String key, Object value) {
sessionData.put(key, value);
}
protected Object removeCommonSessionData(String key) {
return sessionData.remove(key);
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("XMPPSession=[");
sb.append("username: ").append(username);
sb.append(", resources: ").append(activeResources.toString());
sb.append("]");
return sb.toString();
}
/**
*
*/
public void incPacketsCounter() {
++packets_counter;
}
public long getPacketsCounter() {
return packets_counter;
}
} // XMPPSession