/*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.mobicents.servlet.sip.core.session;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;
import org.apache.catalina.Container;
import org.apache.log4j.Logger;
import org.mobicents.servlet.sip.message.SipFactoryImpl;
import org.mobicents.servlet.sip.startup.SipContext;
/**
* This class handles the management of sip sessions and sip application sessions for a given container (context)
* It is a delegate since it is used by many manager implementations classes (Standard and clustered ones)
*
* @author <A HREF="mailto:jean.deruelle@gmail.com">Jean Deruelle</A>
*
*/
public abstract class SipManagerDelegate {
private static transient Logger logger = Logger.getLogger(SipManagerDelegate.class);
protected ConcurrentHashMap<SipApplicationSessionKey, MobicentsSipApplicationSession> sipApplicationSessions =
new ConcurrentHashMap<SipApplicationSessionKey, MobicentsSipApplicationSession>();
protected ConcurrentHashMap<String, MobicentsSipApplicationSession> sipApplicationSessionsByAppGeneratedKey =
new ConcurrentHashMap<String, MobicentsSipApplicationSession>();
//if it's never cleaned up a memory leak will occur
//Shall we have a thread scanning for invalid sessions and removing them accordingly ?
//=> after a chat with ranga the better way to go for now is removing on processDialogTerminated
protected ConcurrentHashMap<SipSessionKey, MobicentsSipSession> sipSessions =
new ConcurrentHashMap<SipSessionKey, MobicentsSipSession>();
protected SipFactoryImpl sipFactoryImpl;
protected Container container;
/**
* The maximum number of active Sip Sessions allowed, or -1 for no limit.
*/
protected int maxActiveSipSessions = -1;
/**
* The maximum number of active Sip Application Sessions allowed, or -1 for no limit.
*/
protected int maxActiveSipApplicationSessions = -1;
/**
* Number of sip session creations that failed due to maxActiveSipSessions.
*/
protected int rejectedSipSessions = 0;
/**
* Number of sip application session creations that failed due to maxActiveSipApplicationSessions.
*/
protected int rejectedSipApplicationSessions = 0;
/**
* The longest time (in seconds) that an expired sip session had been alive.
*/
protected int sipSessionMaxAliveTime;
/**
* Average time (in seconds) that expired sip sessions had been alive.
*/
protected int sipSessionAverageAliveTime;
/**
* Number of sip sessions that have expired.
*/
protected int expiredSipSessions = 0;
/**
* The longest time (in seconds) that an expired Sip Application session had been alive.
*/
protected int sipApplicationSessionMaxAliveTime;
/**
* Average time (in seconds) that expired Sip Application Sessions had been alive.
*/
protected int sipApplicationSessionAverageAliveTime;
/**
* Number of sip application sessions that have expired.
*/
protected int expiredSipApplicationSessions = 0;
// Number of sip sessions created by this manager
protected int sipSessionCounter=0;
// Number of sip Application sessions created by this manager
protected int sipApplicationSessionCounter=0;
/**
* @return the SipFactoryImpl
*/
public SipFactoryImpl getSipFactoryImpl() {
return sipFactoryImpl;
}
/**
* @param sipFactoryImpl the SipFactoryImpl to set
*/
public void setSipFactoryImpl(SipFactoryImpl sipFactoryImpl) {
this.sipFactoryImpl = sipFactoryImpl;
}
/**
* @return the container
*/
public Container getContainer() {
return container;
}
/**
* @param container the container to set
*/
public void setContainer(Container container) {
this.container = container;
}
/**
* Removes a sip session from the manager by its key
* @param key the identifier for this session
* @return the sip session that had just been removed, null otherwise
*/
public MobicentsSipSession removeSipSession(final SipSessionKey key) {
if(logger.isDebugEnabled()) {
logger.debug("Removing a sip session with the key : " + key);
}
return sipSessions.remove(key);
}
/**
* Removes a sip application session from the manager by its key
* @param key the identifier for this session
* @return the sip application session that had just been removed, null otherwise
*/
public MobicentsSipApplicationSession removeSipApplicationSession(final SipApplicationSessionKey key) {
if(logger.isDebugEnabled()) {
logger.debug("Removing a sip application session with the key : " + key);
}
MobicentsSipApplicationSession sipApplicationSession = sipApplicationSessions.remove(key);
if(sipApplicationSession != null) {
final String appGeneratedKey = sipApplicationSession.getKey().getAppGeneratedKey();
if(appGeneratedKey != null) {
sipApplicationSessionsByAppGeneratedKey.remove(appGeneratedKey);
}
}
return sipApplicationSession;
}
/**
* Retrieve a sip application session from its key. If none exists, one can enforce
* the creation through the create parameter to true.
* @param key the key identifying the sip application session to retrieve
* @param create if set to true, if no session has been found one will be created
* @return the sip application session matching the key
*/
public MobicentsSipApplicationSession getSipApplicationSession(final SipApplicationSessionKey key, final boolean create) {
MobicentsSipApplicationSession sipApplicationSessionImpl = null;
//first we check if the app session can be found by its app generated key
final String appGeneratedKey = key.getAppGeneratedKey();
if(appGeneratedKey != null) {
sipApplicationSessionImpl = sipApplicationSessionsByAppGeneratedKey.get(appGeneratedKey);
}
if(sipApplicationSessionImpl == null) {
sipApplicationSessionImpl = sipApplicationSessions.get(key);
}
if(sipApplicationSessionImpl == null && create) {
sipApplicationSessionImpl = createSipApplicationSession(key, create);
}
return sipApplicationSessionImpl;
}
protected MobicentsSipApplicationSession createSipApplicationSession(final SipApplicationSessionKey key, final boolean create) {
//http://dmy999.com/article/34/correct-use-of-concurrenthashmap
MobicentsSipApplicationSession sipApplicationSessionImpl = null;
final MobicentsSipApplicationSession newSipApplicationSessionImpl =
getNewMobicentsSipApplicationSession(key, (SipContext) container);
sipApplicationSessionImpl = sipApplicationSessions.putIfAbsent(key, newSipApplicationSessionImpl);
if (sipApplicationSessionImpl == null) {
// put succeeded, use new value
if(logger.isDebugEnabled()) {
logger.debug("Adding a sip application session with the key : " + key);
}
sipApplicationSessionImpl = newSipApplicationSessionImpl;
final String appGeneratedKey = key.getAppGeneratedKey();
if(appGeneratedKey != null) {
sipApplicationSessionsByAppGeneratedKey.putIfAbsent(appGeneratedKey, sipApplicationSessionImpl);
}
}
return sipApplicationSessionImpl;
}
/**
* Retrieve a sip session from its key. If none exists, one can enforce
* the creation through the create parameter to true. the sip factory cannot be null
* if create is set to true.
* @param key the key identifying the sip session to retrieve
* @param create if set to true, if no session has been found one will be created
* @param sipFactoryImpl needed only for sip session creation.
* @param sipApplicationSessionImpl to associate the SipSession with if create is set to true, if false it won't be used
* @return the sip session matching the key
* @throws IllegalArgumentException if create is set to true and sip Factory is null
*/
public MobicentsSipSession getSipSession(final SipSessionKey key, final boolean create, final SipFactoryImpl sipFactoryImpl, final MobicentsSipApplicationSession sipApplicationSessionImpl) {
if(create && sipFactoryImpl == null) {
throw new IllegalArgumentException("the sip factory should not be null");
}
//http://dmy999.com/article/34/correct-use-of-concurrenthashmap
MobicentsSipSession sipSessionImpl = sipSessions.get(key);
if(sipSessionImpl == null && create) {
sipSessionImpl = createSipSession(key, create, sipFactoryImpl, sipApplicationSessionImpl);
}
// check if this session key has a to tag.
if(sipSessionImpl != null) {
return setToTag(key, sipSessionImpl);
}
return sipSessionImpl;
}
protected MobicentsSipSession createSipSession(final SipSessionKey key, final boolean create, final SipFactoryImpl sipFactoryImpl, final MobicentsSipApplicationSession sipApplicationSessionImpl) {
MobicentsSipSession sipSessionImpl = null;
final MobicentsSipSession newSipSessionImpl = getNewMobicentsSipSession(key, sipFactoryImpl, sipApplicationSessionImpl);
sipSessionImpl = sipSessions.putIfAbsent(key, newSipSessionImpl);
if(sipSessionImpl == null) {
if(logger.isDebugEnabled()) {
logger.debug("Adding a sip session with the key : " + key);
}
// put succeeded, use new value
sipSessionImpl = newSipSessionImpl;
}
return sipSessionImpl;
}
protected MobicentsSipSession setToTag(final SipSessionKey key, final MobicentsSipSession sipSession) {
final String currentKeyToTag = key.getToTag();
final SipSessionKey existingKey = sipSession.getKey();
final String toTag = existingKey.getToTag();
if(toTag == null && currentKeyToTag != null) {
existingKey.setToTag(currentKeyToTag );
if(logger.isDebugEnabled()) {
logger.debug("Setting the To tag " + currentKeyToTag +
" to the session " + key);
}
} else if (currentKeyToTag != null && !toTag.equals(currentKeyToTag)) {
MobicentsSipSession derivedSipSession = sipSession.findDerivedSipSession(currentKeyToTag );
if(derivedSipSession == null) {
// if the to tag is different a sip session is created
if(logger.isDebugEnabled()) {
logger.debug("Original session " + key + " with To Tag " + toTag +
" creates new derived session with following to Tag " + currentKeyToTag );
}
derivedSipSession = createDerivedSipSession(sipSession, key);
} else {
if(logger.isDebugEnabled()) {
logger.debug("Original session " + key + " with To Tag " + toTag +
" already has a derived session with following to Tag " + currentKeyToTag + " - reusing it");
}
}
return derivedSipSession;
}
return sipSession;
}
public void changeSessionKey(SipSessionKey oldKey, SipSessionKey newKey) {
MobicentsSipSession session = this.sipSessions.get(oldKey);
if(session == null)
throw new IllegalArgumentException("oldKey doesn't exist in this application session.");
this.sipSessions.put(newKey, session);
this.sipSessions.remove(oldKey);
}
/**
* clone the parent sip session given in parameter except its attributes (they will be shared)
* and add it to the internal map of derived sessions identifying it by its ToTag
*
* @param parentSipSession the parent sip session holding the newly created derived session
* @param sessionKey the key of the new derived session to create
* @return the newly created derived session
*/
protected MobicentsSipSession createDerivedSipSession(MobicentsSipSession parentSipSession, SipSessionKey sessionKey) {
// clone the session and add it to the map of derived sessions
MobicentsSipSession sipSessionImpl = getNewMobicentsSipSession(sessionKey, sipFactoryImpl, parentSipSession.getSipApplicationSession());
sipSessionImpl.setSipSessionAttributeMap(parentSipSession.getSipSessionAttributeMap());
try {
sipSessionImpl.setHandler(parentSipSession.getHandler());
} catch (ServletException e) {
//cannot happen
logger.error("Problem creating derived session", e);
}
sipSessionImpl.setRoutingRegion(parentSipSession.getRegion());
// dialog will be set when the response will be associated with this session
// sipSessionImpl.sessionCreatingDialog = dialog;
sipSessionImpl.setState(parentSipSession.getState());
sipSessionImpl.setStateInfo(parentSipSession.getStateInfo());
sipSessionImpl.setProxy(parentSipSession.getProxy());
if(parentSipSession.getSipSubscriberURI() != null) {
sipSessionImpl.setSipSubscriberURI(parentSipSession.getSipSubscriberURI().clone());
}
sipSessionImpl.setUserPrincipal(parentSipSession.getUserPrincipal());
sipSessionImpl.setParentSession(parentSipSession);
parentSipSession.addDerivedSipSessions(sipSessionImpl);
return sipSessionImpl;
}
/**
* Retrieve all sip sessions currently hold by the session manager
* @return an iterator on the sip sessions
*/
public Iterator<MobicentsSipSession> getAllSipSessions() {
return sipSessions.values().iterator();
}
/**
* Retrieve all sip application sessions currently hold by the session manager
* @return an iterator on the sip sessions
*/
public int getNumberOfSipApplicationSessions() {
return sipApplicationSessions.size();
}
/**
* Retrieve all sip sessions currently hold by the session manager
* @return an iterator on the sip sessions
*/
public int getNumberOfSipSessions() {
return sipSessions.size();
}
/**
* Retrieve all sip application sessions currently hold by the session manager
* @return an iterator on the sip sessions
*/
public Iterator<MobicentsSipApplicationSession> getAllSipApplicationSessions() {
return sipApplicationSessions.values().iterator();
}
/**
* Retrieves the sip application session holding the converged http session in parameter
* @param convergedHttpSession the converged session to look up
* @return the sip application session holding a reference to it or null if none references it
*/
public MobicentsSipApplicationSession findSipApplicationSession(HttpSession httpSession) {
for (MobicentsSipApplicationSession sipApplicationSessionImpl : sipApplicationSessions.values()) {
if(sipApplicationSessionImpl.findHttpSession(httpSession.getId()) != null) {
return sipApplicationSessionImpl;
}
}
return null;
}
/**
*
*/
public void dumpSipSessions() {
if(logger.isDebugEnabled()) {
logger.debug("sip sessions present in the session manager");
for (SipSessionKey sipSessionKey : sipSessions.keySet()) {
logger.debug(sipSessionKey.toString());
}
}
}
/**
*
*/
public void dumpSipApplicationSessions() {
if(logger.isDebugEnabled()) {
logger.debug("sip application sessions present in the session manager");
for (SipApplicationSessionKey sipApplicationSessionKey : sipApplicationSessions.keySet()) {
logger.debug(sipApplicationSessionKey.toString() + "/hashed_app_name=" + sipFactoryImpl.getSipApplicationDispatcher().getHashFromApplicationName(sipApplicationSessionKey.getApplicationName()));
}
}
}
/**
* Remove the sip sessions and sip application sessions
*/
public void removeAllSessions() {
List<SipSessionKey> sipSessionsToRemove = new ArrayList<SipSessionKey>();
for (SipSessionKey sipSessionKey : sipSessions.keySet()) {
sipSessionsToRemove.add(sipSessionKey);
}
for (SipSessionKey sipSessionKey : sipSessionsToRemove) {
removeSipSession(sipSessionKey);
}
List<SipApplicationSessionKey> sipApplicationSessionsToRemove = new ArrayList<SipApplicationSessionKey>();
for (SipApplicationSessionKey sipApplicationSessionKey : sipApplicationSessions.keySet()) {
sipApplicationSessionsToRemove.add(sipApplicationSessionKey);
}
for (SipApplicationSessionKey sipApplicationSessionKey : sipApplicationSessionsToRemove) {
removeSipApplicationSession(sipApplicationSessionKey);
}
}
protected abstract MobicentsSipSession getNewMobicentsSipSession(SipSessionKey key, SipFactoryImpl sipFactoryImpl, MobicentsSipApplicationSession mobicentsSipApplicationSession);
protected abstract MobicentsSipApplicationSession getNewMobicentsSipApplicationSession(SipApplicationSessionKey key, SipContext sipContext);
//JMX statistics
/**
* Return the maximum number of active Sessions allowed, or -1 for no limit.
*/
public int getMaxActiveSipSessions() {
return (this.maxActiveSipSessions);
}
/**
* Set the maximum number of actives Sip Sessions allowed, or -1 for no
* limit.
*
* @param max
* The new maximum number of sip sessions
*/
public void setMaxActiveSipSessions(int max) {
this.maxActiveSipSessions = max;
}
/**
* Return the maximum number of active Sessions allowed, or -1 for no limit.
*/
public int getMaxActiveSipApplicationSessions() {
return (this.maxActiveSipApplicationSessions);
}
/**
* Set the maximum number of actives Sip Application Sessions allowed, or -1
* for no limit.
*
* @param max
* The new maximum number of sip application sessions
*/
public void setMaxActiveSipApplicationSessions(int max) {
this.maxActiveSipApplicationSessions = max;
}
/**
* Number of sip session creations that failed due to maxActiveSipSessions
*
* @return The count
*/
public int getRejectedSipSessions() {
return rejectedSipSessions;
}
public void setRejectedSipSessions(int rejectedSipSessions) {
this.rejectedSipSessions = rejectedSipSessions;
}
/**
* Number of sip session creations that failed due to maxActiveSipSessions
*
* @return The count
*/
public int getRejectedSipApplicationSessions() {
return rejectedSipApplicationSessions;
}
public void setRejectedSipApplicationSessions(
int rejectedSipApplicationSessions) {
this.rejectedSipApplicationSessions = rejectedSipApplicationSessions;
}
public void setSipSessionCounter(int sipSessionCounter) {
this.sipSessionCounter = sipSessionCounter;
}
/**
* Total sessions created by this manager.
*
* @return sessions created
*/
public int getSipSessionCounter() {
return sipSessionCounter;
}
/**
* Returns the number of active sessions
*
* @return number of sessions active
*/
public int getActiveSipSessions() {
return sipSessions.size();
}
/**
* Gets the longest time (in seconds) that an expired session had been
* alive.
*
* @return Longest time (in seconds) that an expired session had been alive.
*/
public int getSipSessionMaxAliveTime() {
return sipSessionMaxAliveTime;
}
/**
* Sets the longest time (in seconds) that an expired session had been
* alive.
*
* @param sessionMaxAliveTime
* Longest time (in seconds) that an expired session had been
* alive.
*/
public void setSipSessionMaxAliveTime(int sipSessionMaxAliveTime) {
this.sipSessionMaxAliveTime = sipSessionMaxAliveTime;
}
/**
* Gets the average time (in seconds) that expired sessions had been alive.
*
* @return Average time (in seconds) that expired sessions had been alive.
*/
public int getSipSessionAverageAliveTime() {
return sipSessionAverageAliveTime;
}
/**
* Sets the average time (in seconds) that expired sessions had been alive.
*
* @param sessionAverageAliveTime
* Average time (in seconds) that expired sessions had been
* alive.
*/
public void setSipSessionAverageAliveTime(int sipSessionAverageAliveTime) {
this.sipSessionAverageAliveTime = sipSessionAverageAliveTime;
}
public void setSipApplicationSessionCounter(int sipApplicationSessionCounter) {
this.sipApplicationSessionCounter = sipApplicationSessionCounter;
}
/**
* Total sessions created by this manager.
*
* @return sessions created
*/
public int getSipApplicationSessionCounter() {
return sipApplicationSessionCounter;
}
/**
* Returns the number of active sessions
*
* @return number of sessions active
*/
public int getActiveSipApplicationSessions() {
return sipApplicationSessions.size();
}
/**
* Gets the longest time (in seconds) that an expired session had been
* alive.
*
* @return Longest time (in seconds) that an expired session had been alive.
*/
public int getSipApplicationSessionMaxAliveTime() {
return sipApplicationSessionMaxAliveTime;
}
/**
* Sets the longest time (in seconds) that an expired session had been
* alive.
*
* @param sessionMaxAliveTime
* Longest time (in seconds) that an expired session had been
* alive.
*/
public void setSipApplicationSessionMaxAliveTime(
int sipApplicationSessionMaxAliveTime) {
this.sipApplicationSessionMaxAliveTime = sipApplicationSessionMaxAliveTime;
}
/**
* Gets the average time (in seconds) that expired sessions had been alive.
*
* @return Average time (in seconds) that expired sessions had been alive.
*/
public int getSipApplicationSessionAverageAliveTime() {
return sipApplicationSessionAverageAliveTime;
}
/**
* Sets the average time (in seconds) that expired sessions had been alive.
*
* @param sessionAverageAliveTime
* Average time (in seconds) that expired sessions had been
* alive.
*/
public void setSipApplicationSessionAverageAliveTime(
int sipApplicationSessionAverageAliveTime) {
this.sipApplicationSessionAverageAliveTime = sipApplicationSessionAverageAliveTime;
}
/**
* Gets the number of sessions that have expired.
*
* @return Number of sessions that have expired
*/
public int getExpiredSipSessions() {
return expiredSipSessions;
}
/**
* Sets the number of sessions that have expired.
*
* @param expiredSessions
* Number of sessions that have expired
*/
public void setExpiredSipSessions(int expiredSipSessions) {
this.expiredSipSessions = expiredSipSessions;
}
/**
* Gets the number of sessions that have expired.
*
* @return Number of sessions that have expired
*/
public int getExpiredSipApplicationSessions() {
return expiredSipApplicationSessions;
}
/**
* Sets the number of sessions that have expired.
*
* @param expiredSessions
* Number of sessions that have expired
*/
public void setExpiredSipApplicationSessions(
int expiredSipApplicationSessions) {
this.expiredSipApplicationSessions = expiredSipApplicationSessions;
}
}