/*
* JBoss, Home of Professional Open Source
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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.ha.javax.sip;
import gov.nist.core.StackLogger;
import gov.nist.javax.sip.message.SIPRequest;
import gov.nist.javax.sip.message.SIPResponse;
import gov.nist.javax.sip.stack.HASipDialog;
import gov.nist.javax.sip.stack.SIPClientTransaction;
import gov.nist.javax.sip.stack.SIPDialog;
import gov.nist.javax.sip.stack.SIPTransaction;
import java.util.Properties;
import javax.sip.DialogState;
import javax.sip.PeerUnavailableException;
import javax.sip.ProviderDoesNotExistException;
import javax.sip.SipException;
import org.mobicents.ha.javax.sip.cache.SipCache;
import org.mobicents.ha.javax.sip.cache.SipCacheException;
import org.mobicents.ha.javax.sip.cache.SipCacheFactory;
/**
* This class extends the regular NIST SIP Stack Implementation to cache Dialogs in a replicated cache
* and make use of Fault Tolerant Timers so that the NIST SIP Stack can be distributed in a cluster
* and calls can be failed over
*
* This class will instantiate an instance of a class implementing the org.mobicents.ha.javax.sip.cache.SipCache interface to be able to set/get dialogs and txs into/from it.
* The cache class name is retrieved through the Properties given to the Sip Stack upon creation under the following property name : org.mobicents.ha.javax.sip.CACHE_CLASS_NAME
*
* It will override all the calls to get/remove/put Dialogs and Txs so that we can fetch/remove/put them from/into the Cache
* and populate the local datastructure of the NIST SIP Stack
*
* @author jean.deruelle@gmail.com
*
*/
public abstract class ClusteredSipStackImpl extends gov.nist.javax.sip.SipStackImpl implements ClusteredSipStack {
private SipCache sipCache = null;
public ClusteredSipStackImpl(Properties configurationProperties) throws PeerUnavailableException {
super(configurationProperties);
// get/create the jboss cache instance to store all sip stack related data into it
sipCache = SipCacheFactory.createSipCache(this, configurationProperties);
try {
sipCache.init();
} catch (Exception e) {
throw new PeerUnavailableException("Unable to initialize the SipCache", e);
}
}
@Override
public void start() throws ProviderDoesNotExistException, SipException {
try {
sipCache.start();
} catch (Exception e) {
throw new SipException("Unable to start the SipCache", e);
}
super.start();
}
@Override
public void stop() {
super.stop();
try {
sipCache.stop();
} catch (Exception e) {
getStackLogger().logError("Unable to stop the SipCache", e);
}
}
@Override
public SIPDialog createDialog(SIPTransaction transaction) {
SIPDialog retval = null;
if (transaction instanceof SIPClientTransaction) {
String dialogId = ((SIPRequest) transaction.getRequest()).getDialogId(false);
if (this.earlyDialogTable.get(dialogId) != null) {
SIPDialog dialog = this.earlyDialogTable.get(dialogId);
if (dialog.getState() == null || dialog.getState() == DialogState.EARLY) {
retval = dialog;
} else {
retval = new HASipDialog(transaction);
this.earlyDialogTable.put(dialogId, retval);
}
} else {
retval = new HASipDialog(transaction);
this.earlyDialogTable.put(dialogId, retval);
}
} else {
retval = new HASipDialog(transaction);
}
return retval;
}
@Override
public SIPDialog createDialog(SIPClientTransaction transaction, SIPResponse sipResponse) {
String dialogId = ((SIPRequest) transaction.getRequest()).getDialogId(false);
SIPDialog retval = null;
if (this.earlyDialogTable.get(dialogId) != null) {
retval = this.earlyDialogTable.get(dialogId);
if (sipResponse.isFinalResponse()) {
this.earlyDialogTable.remove(dialogId);
}
} else {
retval = new HASipDialog(transaction, sipResponse);
}
return retval;
}
@Override
public SIPDialog getDialog(String dialogId) {
if(getStackLogger().isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
getStackLogger().logDebug("checking if the dialog " + dialogId + " is present in the local cache");
}
SIPDialog sipDialog = super.getDialog(dialogId);
if(sipDialog == null) {
if(getStackLogger().isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
getStackLogger().logDebug("local dialog " + dialogId + " is null, checking in the distributed cache");
}
sipDialog = getDialogFromDistributedCache(dialogId);
if(sipDialog != null) {
if(getStackLogger().isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
getStackLogger().logDebug("dialog " + dialogId + " found in the distributed cache, storing it locally");
}
super.putDialog(sipDialog);
} else {
if(getStackLogger().isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
getStackLogger().logDebug("dialog " + dialogId + " not found in the distributed cache");
}
}
}
return sipDialog;
}
@Override
public void putDialog(SIPDialog dialog) {
putDialogIntoDistributedCache(dialog);
super.putDialog(dialog);
}
@Override
public void removeDialog(SIPDialog dialog) {
removeDialogFromDistributedCache(dialog.getDialogId());
super.removeDialog(dialog);
}
/**
* Retrieve the dialog from the distributed cache
* @param dialogId the id of the dialog to fetch
* @return the SIPDialog from the distributed cache, null if nothing has been found in the cache
*/
protected SIPDialog getDialogFromDistributedCache(String dialogId) {
if(getStackLogger().isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
getStackLogger().logDebug("sipStack " + this + " checking if the dialog " + dialogId + " is present in the distributed cache");
}
// fetch the corresponding dialog from the cache instance
SIPDialog sipDialog = null;
try {
sipDialog = sipCache.getDialog(dialogId);
} catch (SipCacheException e) {
getStackLogger().logError("sipStack " + this + " problem getting dialog " + dialogId + " from the distributed cache", e);
}
if(sipDialog != null) {
((HASipDialog)sipDialog).initAfterLoad(this);
}
return sipDialog;
}
/**
* Store the dialog into the distributed cache
* @param dialog the dialog to store
*/
protected void putDialogIntoDistributedCache(SIPDialog dialog) {
String dialogId = dialog.getDialogId();
if(getStackLogger().isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
getStackLogger().logDebug("sipStack " + this + " storing the dialog " + dialogId + " in the distributed cache");
}
// put the corresponding dialog into the cache instance
try {
sipCache.putDialog(dialog);
} catch (SipCacheException e) {
getStackLogger().logError("sipStack " + this + " problem storing the dialog " + dialogId + " into the distributed cache", e);
}
}
/**
* Remove the dialog from the distributed cache
* @param dialogId the id of the dialog to remove
*/
protected void removeDialogFromDistributedCache(String dialogId) {
if(getStackLogger().isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
getStackLogger().logDebug("sipStack " + this + " removing the dialog " + dialogId + " from the distributed cache");
}
// remove the corresponding dialog from the cache instance
// put the corresponding dialog into the cache instance
try {
sipCache.removeDialog(dialogId);
} catch (SipCacheException e) {
getStackLogger().logError("sipStack " + this + " problem removing dialog " + dialogId + " from the distributed cache", e);
}
}
/**
* @param sipCache the sipCache to set
*/
public void setSipCache(SipCache sipCache) {
this.sipCache = sipCache;
}
/**
* @return the sipCache
*/
public SipCache getSipCache() {
return sipCache;
}
}