/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. 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.cache;

import gov.nist.core.CommonLogger;
import gov.nist.core.StackLogger;
import gov.nist.javax.sip.stack.SIPClientTransaction;
import gov.nist.javax.sip.stack.SIPDialog;
import gov.nist.javax.sip.stack.SIPServerTransaction;

import java.util.Properties;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.transaction.UserTransaction;

import org.jboss.cache.Cache;
import org.jboss.cache.CacheException;
import org.jboss.cache.DefaultCacheFactory;
import org.jboss.cache.Fqn;
import org.jboss.cache.Node;
import org.jboss.cache.config.Configuration.CacheMode;
import org.mobicents.ha.javax.sip.ClusteredSipStack;
import org.mobicents.ha.javax.sip.ReplicationStrategy;

/**
 * Implementation of the SipCache interface, backed by a JBoss Cache 3.X Cache.
 * The configuration of JBoss Cache can be set throught the following Restcomm SIP Stack property :
 * <b>org.mobicents.ha.javax.sip.JBOSS_CACHE_CONFIG_PATH</b>
 *
 * @author jean.deruelle@gmail.com
 *
 */
public class JBossSipCache implements SipCache {
	public static final String JBOSS_CACHE_CONFIG_PATH = "org.mobicents.ha.javax.sip.JBOSS_CACHE_CONFIG_PATH";
	public static final String DEFAULT_FILE_CONFIG_PATH = "META-INF/cache-configuration.xml";
	private static StackLogger clusteredlogger = CommonLogger.getLogger(JBossSipCache.class);
	ClusteredSipStack clusteredSipStack = null;
	Properties configProperties = null;

	protected Cache cache;
	protected JBossJainSipCacheListener cacheListener;

	protected Node<String, SIPDialog> dialogRootNode = null;
	protected Node<String, SIPClientTransaction> clientTxRootNode = null;
	protected Node<String, SIPServerTransaction> serverTxRootNode = null;

	/**
	 *
	 */
	public JBossSipCache() {}

	/* (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.SipCache#getDialog(java.lang.String)
	 */
	public SIPDialog getDialog(String dialogId) throws SipCacheException {
		try {
			Node dialogNode = ((Node) dialogRootNode.getChild(Fqn.fromString(dialogId)));
			if(dialogNode != null) {
				return (SIPDialog) dialogNode.get(dialogId);
			} else {
				return null;
			}
		} catch (CacheException e) {
			throw new SipCacheException("A problem occured while retrieving the following dialog " + dialogId + " from JBoss Cache", e);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.SipCache#updateDialog(gov.nist.javax.sip.stack.SIPDialog)
	 */
	public void updateDialog(SIPDialog sipDialog) throws SipCacheException {
		Node dialogNode = ((Node) dialogRootNode.getChild(Fqn.fromString(sipDialog.getDialogId())));
		if(dialogNode != null) {
			if(dialogNode != null) {
				sipDialog = (SIPDialog) dialogNode.get(sipDialog.getDialogId());
			}
		}
	}

	/* (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.SipCache#putDialog(gov.nist.javax.sip.stack.SIPDialog)
	 */
	public void putDialog(SIPDialog dialog) throws SipCacheException {
		UserTransaction tx = null;
		try {
			Properties prop = new Properties();
			prop.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.cache.transaction.DummyContextFactory");
			tx = (UserTransaction) new InitialContext(prop).lookup("UserTransaction");
			if(tx != null) {
				tx.begin();
			}
			Node dialogNode = dialogRootNode.addChild(Fqn.fromString(dialog.getDialogId()));
			dialogNode.put(dialog.getDialogId(), dialog);
			if(tx != null) {
				tx.commit();
			}
		} catch (Exception e) {
			if(tx != null) {
				try { tx.rollback(); } catch(Throwable t) {}
			}
			throw new SipCacheException("A problem occured while putting the following dialog " + dialog.getDialogId() + "  into JBoss Cache", e);
		}
	}

	/* (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.SipCache#removeDialog(java.lang.String)
	 */
	public void removeDialog(String dialogId) throws SipCacheException {
		UserTransaction tx = null;
		try {
			Properties prop = new Properties();
			prop.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.cache.transaction.DummyContextFactory");
			tx = (UserTransaction) new InitialContext(prop).lookup("UserTransaction");
			if(tx != null) {
				tx.begin();
			}
			dialogRootNode.removeChild(dialogId);
			if(tx != null) {
				tx.commit();
			}
		} catch (Exception e) {
			if(tx != null) {
				try { tx.rollback(); } catch(Throwable t) {}
			}
			throw new SipCacheException("A problem occured while removing the following dialog " + dialogId + " from JBoss Cache", e);
		}
	}

	/* (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.SipCache#setClusteredSipStack(org.mobicents.ha.javax.sip.ClusteredSipStack)
	 */
	public void setClusteredSipStack(ClusteredSipStack clusteredSipStack) {
		this.clusteredSipStack  = clusteredSipStack;
	}

	/* (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.SipCache#setConfigurationProperties(java.util.Properties)
	 */
	public void setConfigurationProperties(Properties configurationProperties) {
		this.configProperties = configurationProperties;
	}

	public void init() throws SipCacheException {
		String pojoConfigurationPath = configProperties.getProperty(JBOSS_CACHE_CONFIG_PATH, DEFAULT_FILE_CONFIG_PATH);
		if (clusteredlogger.isLoggingEnabled(StackLogger.TRACE_INFO)) {
			clusteredlogger.logInfo(
					"Mobicents JAIN SIP JBoss Cache Configuration path is : " + pojoConfigurationPath);
		}
		try {
			cache = new DefaultCacheFactory<String, SIPDialog>().createCache(pojoConfigurationPath);
			cache.create();
			cacheListener = new JBossJainSipCacheListener(clusteredSipStack);
			cache.addCacheListener(cacheListener);
		} catch (Exception e) {
			throw new SipCacheException("Couldn't init JBoss Cache", e);
		}
	}

	public void start() throws SipCacheException {
		try {
			cache.start();
		} catch (Exception e) {
			throw new SipCacheException("Couldn't start JBoss Cache", e);
		}
		dialogRootNode = cache.getRoot().getChild(SipCache.DIALOG_PARENT_FQN_ELEMENT);
		if(dialogRootNode == null) {
			dialogRootNode = cache.getRoot().addChild(Fqn.fromElements(SipCache.DIALOG_PARENT_FQN_ELEMENT));
		}
		if(clusteredSipStack.getReplicationStrategy() == ReplicationStrategy.EarlyDialog) {
			serverTxRootNode = cache.getRoot().getChild(SipCache.SERVER_TX_PARENT_FQN_ELEMENT);
			if(serverTxRootNode == null) {
				serverTxRootNode = cache.getRoot().addChild(Fqn.fromElements(SipCache.SERVER_TX_PARENT_FQN_ELEMENT));
			}
		}
	}

	public void stop() throws SipCacheException {
		cache.stop();
		cache.destroy();
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.SipCache#inLocalMode()
	 */
	public boolean inLocalMode() {
		return cache.getConfiguration().getCacheMode() == CacheMode.LOCAL;
	}

	public void evictDialog(String dialogId) {
		cache.evict(Fqn.fromElements(dialogRootNode.getFqn(), Fqn.fromString(dialogId)));
	}

	public SIPServerTransaction getServerTransaction(String transactionId)
			throws SipCacheException {
		try {
			Node serverTransactionNode = ((Node) serverTxRootNode.getChild(Fqn.fromString(transactionId)));
			if(serverTransactionNode != null) {
				return (SIPServerTransaction) serverTransactionNode.get(transactionId);
			} else {
				return null;
			}
		} catch (CacheException e) {
			throw new SipCacheException("A problem occured while retrieving the following server transaction " + transactionId + " from JBoss Cache", e);
		}
	}

	public void putServerTransaction(SIPServerTransaction serverTransaction)
			throws SipCacheException {
		UserTransaction tx = null;
		try {
			Properties prop = new Properties();
			prop.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.cache.transaction.DummyContextFactory");
			tx = (UserTransaction) new InitialContext(prop).lookup("UserTransaction");
			if(tx != null) {
				tx.begin();
			}
			Node serverTransactionNode = serverTxRootNode.addChild(Fqn.fromString(serverTransaction.getTransactionId()));
			serverTransactionNode.put(serverTransaction.getTransactionId(), serverTransaction);
			if(tx != null) {
				tx.commit();
			}
		} catch (Exception e) {
			if(tx != null) {
				try { tx.rollback(); } catch(Throwable t) {}
			}
			throw new SipCacheException("A problem occured while putting the following server transaction " + serverTransaction.getTransactionId() + "  into JBoss Cache", e);
		}
	}

	public void removeServerTransaction(String transactionId)
			throws SipCacheException {
		UserTransaction tx = null;
		try {
			Properties prop = new Properties();
			prop.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.cache.transaction.DummyContextFactory");
			tx = (UserTransaction) new InitialContext(prop).lookup("UserTransaction");
			if(tx != null) {
				tx.begin();
			}
			serverTxRootNode.removeChild(transactionId);
			if(tx != null) {
				tx.commit();
			}
		} catch (Exception e) {
			if(tx != null) {
				try { tx.rollback(); } catch(Throwable t) {}
			}
			throw new SipCacheException("A problem occured while removing the following server transaction " + transactionId + " from JBoss Cache", e);
		}
	}

	public SIPClientTransaction getClientTransaction(String transactionId)
			throws SipCacheException {
		try {
			Node clientTransactionNode = ((Node) clientTxRootNode.getChild(Fqn
					.fromString(transactionId)));
			if (clientTransactionNode != null) {
				return (SIPClientTransaction) clientTransactionNode
						.get(transactionId);
			} else {
				return null;
			}
		} catch (CacheException e) {
			throw new SipCacheException(
					"A problem occured while retrieving the following client transaction "
							+ transactionId + " from JBoss Cache", e);
		}
	}

	public void putClientTransaction(SIPClientTransaction clientTransaction)
			throws SipCacheException {
		UserTransaction tx = null;
		try {
			Properties prop = new Properties();
			prop.put(Context.INITIAL_CONTEXT_FACTORY,
					"org.jboss.cache.transaction.DummyContextFactory");
			tx = (UserTransaction) new InitialContext(prop)
					.lookup("UserTransaction");
			if (tx != null) {
				tx.begin();
			}
			Node clientTransactionNode = clientTxRootNode.addChild(Fqn
					.fromString(clientTransaction.getTransactionId()));
			clientTransactionNode.put(clientTransaction.getTransactionId(),
					clientTransaction);
			if (tx != null) {
				tx.commit();
			}
		} catch (Exception e) {
			if (tx != null) {
				try {
					tx.rollback();
				} catch (Throwable t) {
				}
			}
			throw new SipCacheException(
					"A problem occured while putting the following client transaction "
							+ clientTransaction.getTransactionId()
							+ "  into JBoss Cache", e);
		}
	}

	public void removeClientTransaction(String transactionId)
			throws SipCacheException {
		UserTransaction tx = null;
		try {
			Properties prop = new Properties();
			prop.put(Context.INITIAL_CONTEXT_FACTORY,
					"org.jboss.cache.transaction.DummyContextFactory");
			tx = (UserTransaction) new InitialContext(prop)
					.lookup("UserTransaction");
			if (tx != null) {
				tx.begin();
			}
			clientTxRootNode.removeChild(transactionId);
			if (tx != null) {
				tx.commit();
			}
		} catch (Exception e) {
			if (tx != null) {
				try {
					tx.rollback();
				} catch (Throwable t) {
				}
			}
			throw new SipCacheException(
					"A problem occured while removing the following client transaction "
							+ transactionId + " from JBoss Cache", e);
		}
	}
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. 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.cache;

import gov.nist.core.CommonLogger;
import gov.nist.core.StackLogger;
import gov.nist.javax.sip.stack.SIPClientTransaction;
import gov.nist.javax.sip.stack.SIPDialog;
import gov.nist.javax.sip.stack.SIPServerTransaction;

import org.jboss.cache.CacheManager;
import org.jboss.cache.Node;
import org.jboss.ha.framework.server.CacheManagerLocator;
import org.mobicents.cache.MobicentsCache;
import org.mobicents.cluster.DefaultMobicentsCluster;

/**
 * Implementation of the SipCache interface, backed by a Restcomm Cache (JBoss Cache 3.X Cache).
 * The configuration of Restcomm Cache can be set throught the following Restcomm SIP Stack property :
 * <b>org.mobicents.ha.javax.sip.JBOSS_CACHE_CONFIG_PATH</b>
 *
 * @author jean.deruelle@gmail.com
 *
 */
public class ManagedMobicentsSipCache extends MobicentsSipCache {

	public static final String JBOSS_CACHE_CONFIG_PATH = "org.mobicents.ha.javax.sip.JBOSS_CACHE_CONFIG_PATH";
	public static final String DEFAULT_FILE_CONFIG_PATH = "META-INF/cache-configuration.xml";
	public static final String STANDALONE = "org.mobicents.ha.javax.sip.cache.MobicentsSipCache.standalone";
	public static final String CACHE_NAME = "org.mobicents.ha.javax.sip.cache.MobicentsSipCache.cacheName";
	public static final String DEFAULT_CACHE_NAME = "jain-sip-cache";

	private static StackLogger clusteredlogger = CommonLogger.getLogger(ManagedMobicentsSipCache.class);

	protected Node<String, SIPDialog> dialogRootNode = null;
	protected Node<String, SIPClientTransaction> clientTxRootNode = null;
	protected Node<String, SIPServerTransaction> serverTxRootNode = null;

	/**
	 *
	 */
	public ManagedMobicentsSipCache() {
		super();
	}

	@Override
	public void init() throws SipCacheException {
		try {
			if(configProperties.getProperty(ManagedMobicentsSipCache.STANDALONE) == null || "false".equals(configProperties.getProperty(ManagedMobicentsSipCache.STANDALONE))) {
				ClassLoader previousClassLoader = Thread.currentThread().getContextClassLoader();
				Thread.currentThread().setContextClassLoader(CacheManagerLocator.class.getClassLoader());

				CacheManagerLocator locator = CacheManagerLocator.getCacheManagerLocator();
				// Locator accepts as param a set of JNDI properties to help in lookup;
				// this isn't necessary inside the AS
				CacheManager cacheManager = locator.getCacheManager(null);
//				Context ctx = new InitialContext();
//				CacheManager cacheManager = (CacheManager) ctx.lookup("java:CacheManager");
				if (clusteredlogger.isLoggingEnabled(StackLogger.TRACE_INFO)) {
					clusteredlogger.logInfo(
							"Mobicents JAIN SIP JBoss Cache Manager instance : " + cacheManager);
				}
				cluster = new DefaultMobicentsCluster(new MobicentsCache(cacheManager, configProperties.getProperty(CACHE_NAME,DEFAULT_CACHE_NAME), false), null, null);
				Thread.currentThread().setContextClassLoader(previousClassLoader);
			} else {
				String pojoConfigurationPath = configProperties.getProperty(JBOSS_CACHE_CONFIG_PATH, DEFAULT_FILE_CONFIG_PATH);
				if (clusteredlogger.isLoggingEnabled(StackLogger.TRACE_INFO)) {
					clusteredlogger.logInfo(
							"Mobicents JAIN SIP JBoss Cache Configuration path is : " + pojoConfigurationPath);
				}
				cluster = new DefaultMobicentsCluster(new MobicentsCache(pojoConfigurationPath), null, null);
				JBossJainSipCacheListener listener = new JBossJainSipCacheListener(clusteredSipStack);
				cluster.getMobicentsCache().getJBossCache().addCacheListener(listener);
			}
		} catch (Exception e) {
			throw new SipCacheException("Couldn't init Restcomm Cache", e);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.MobicentsSipCache#stop()
	 */
	@Override
	public void stop() throws SipCacheException {
		if(configProperties.getProperty(ManagedMobicentsSipCache.STANDALONE) != null || "true".equals(configProperties.getProperty(ManagedMobicentsSipCache.STANDALONE))) {
			super.stop();
		}
	}

}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. 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.address.SipUri;

import java.text.ParseException;

import javax.sip.address.Address;
import javax.sip.address.AddressFactory;

/**
 * Base code for a {@link LoadBalancerElector} which is also a
 * {@link LoadBalancerHeartBeatingService}.
 *
 * @author martins
 *
 */
public abstract class AbstractLoadBalancerElector implements
		LoadBalancerElector, LoadBalancerHeartBeatingListener {

	/**
	 *
	 */
	protected LoadBalancerHeartBeatingService service;

	/**
	 *
	 */
	protected AddressFactory addressFactory;

	/**
	 *
	 */
	protected StackLogger logger;

	/**
	 * Adds the specified load balancer {@link Address}.
	 * @param address
	 */
	abstract void addLoadBalancer(SipLoadBalancer address);

	/**
	 * Creates a {@link Address} with a {@link SipUri}, from the specified
	 * balancer description. Note that the uri has no transport set.
	 *
	 * @param balancerDescription
	 * @return
	 */
	private Address createAddress(String balancerDescription) {
		String host = balancerDescription;
		int sipPort = LoadBalancerHeartBeatingServiceImpl.DEFAULT_LB_SIP_PORT;
		if (balancerDescription
				.indexOf(LoadBalancerHeartBeatingServiceImpl.BALANCER_SIP_PORT_CHAR_SEPARATOR) != -1) {
			String[] balancerDescriptionSplitted = balancerDescription
					.split(LoadBalancerHeartBeatingServiceImpl.BALANCER_SIP_PORT_CHAR_SEPARATOR);
			host = balancerDescriptionSplitted[0];
			try {
				sipPort = Integer.parseInt(balancerDescriptionSplitted[1]);
			} catch (NumberFormatException e) {
				logger.logError(
						"Impossible to parse the following sip balancer port "
								+ balancerDescriptionSplitted[1], e);
				return null;
			}
		}
		return createAddress(host, sipPort);
	}

	/**
	 * Creates a {@link Address} with a {@link SipUri}, from the specified host
	 * and port. Note that the uri has no transport set.
	 *
	 * @param host
	 * @param port
	 * @return
	 */
	private Address createAddress(String host, int port) {
		SipUri sipUri = new SipUri();
		try {
			sipUri.setHost(host);
		} catch (ParseException e) {
			logger.logError("Bad load balancer host " + host, e);
			return null;
		}
		sipUri.setPort(port);
		return addressFactory.createAddress(sipUri);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.mobicents.ha.javax.sip.LoadBalancerElector#getLoadBalancer()
	 */
	public abstract Address getLoadBalancer();

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.mobicents.ha.javax.sip.LoadBalancerHeartBeatingListener#loadBalancerAdded
	 * (org.mobicents.ha.javax.sip.SipLoadBalancer)
	 */
	public void loadBalancerAdded(SipLoadBalancer balancerDescription) {
			addLoadBalancer(balancerDescription);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @seeorg.mobicents.ha.javax.sip.LoadBalancerHeartBeatingListener#
	 * loadBalancerRemoved(org.mobicents.ha.javax.sip.SipLoadBalancer)
	 */
	public void loadBalancerRemoved(SipLoadBalancer balancerDescription) {
		Address address = createAddress(balancerDescription.getAddress()
				.getHostName(), balancerDescription.getSipPort());
		if (address != null) {
			removeLoadBalancer(address);
		}
	}

	/* (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.LoadBalancerElector#setAddressFactory(javax.sip.address.AddressFactory)
	 */
	public void setAddressFactory(AddressFactory addressFactory) throws NullPointerException {
		if (addressFactory == null) {
			throw new NullPointerException("null addressFactory");
		}
		this.addressFactory = addressFactory;
	}

	/* (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.LoadBalancerElector#setService(org.mobicents.ha.javax.sip.LoadBalancerHeartBeatingService)
	 */
	public void setService(LoadBalancerHeartBeatingService service) throws IllegalStateException, NullPointerException {
		if (service == null) {
			throw new NullPointerException("null service");
		}
		if (addressFactory == null || logger == null) {
			throw new IllegalStateException();
		}
		this.service = service;
		service.addLoadBalancerHeartBeatingListener(this);
		for (SipLoadBalancer balancer : service.getLoadBalancers()) {
			addLoadBalancer(balancer);
		}
	}

	/* (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.LoadBalancerElector#setStackLogger(gov.nist.core.StackLogger)
	 */
	public void setStackLogger(StackLogger logger) throws NullPointerException {
		if (logger == null) {
			throw new NullPointerException("null logger");
		}
		this.logger = logger;
	}

	/**
	 * Removes the specified load balancer {@link Address}.
	 * @param address
	 */
	abstract void removeLoadBalancer(Address address);
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. 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.CommonLogger;
import gov.nist.core.StackLogger;
import gov.nist.javax.sip.stack.MessageProcessor;

import java.util.Properties;

import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.sip.PeerUnavailableException;
import javax.sip.ProviderDoesNotExistException;
import javax.sip.SipException;

import org.mobicents.ha.javax.sip.cache.SipCache;

/**
 * This class extends the ClusteredSipStack to provide an implementation backed by JBoss Cache 3.X
 *
 * @author jean.deruelle@gmail.com
 *
 */
public class SipStackImpl extends ClusteredSipStackImpl implements SipStackImplMBean, NotificationListener {
	private static StackLogger logger = CommonLogger.getLogger(SipStackImpl.class);
	public static String LOG4J_SERVICE_MBEAN_NAME = "jboss.system:service=Logging,type=Log4jService";

	public SipStackImpl(Properties configurationProperties) throws PeerUnavailableException {
		super(updateConfigProperties(configurationProperties));
	}

	@Override
	public void start() throws ProviderDoesNotExistException, SipException {
		super.start();
		try {
			if(logger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
				logger.logDebug("Adding notification listener for logging mbean \"" + LOG4J_SERVICE_MBEAN_NAME + "\" to server " + getMBeanServer());
			}
			getMBeanServer().addNotificationListener(new ObjectName(LOG4J_SERVICE_MBEAN_NAME), this, null, null);
		} catch (Exception e) {
			logger.logWarning("Could not register the stack as a Notification Listener of " + LOG4J_SERVICE_MBEAN_NAME + " runtime changes to log4j.xml won't affect SIP Stack Logging");
		}
	}

	private static final Properties updateConfigProperties(Properties configurationProperties) {
		if(configurationProperties.getProperty(ClusteredSipStack.CACHE_CLASS_NAME_PROPERTY) == null) {
			configurationProperties.setProperty(ClusteredSipStack.CACHE_CLASS_NAME_PROPERTY, SipCache.SIP_DEFAULT_CACHE_CLASS_NAME);
		}
		return configurationProperties;
	}

	public int getNumberOfClientTransactions() {
		return getClientTransactionTableSize();
	}

	public int getNumberOfDialogs() {
		return dialogTable.size();
	}

	public int getNumberOfEarlyDialogs() {
		return earlyDialogTable.size();
	}

	public int getNumberOfServerTransactions() {
		return getServerTransactionTableSize();
	}

	public boolean isLocalMode() {
		return getSipCache().inLocalMode();
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.ClusteredSipStack#passivateDialog(org.mobicents.ha.javax.sip.HASipDialog)
	 */
	public void passivateDialog(HASipDialog dialog) {
		String dialogId = dialog.getDialogIdToReplicate();
		sipCache.evictDialog(dialogId);
		String mergeId = dialog.getMergeId();
        if (mergeId != null) {
            serverDialogMergeTestTable.remove(mergeId);
        }
		dialogTable.remove(dialogId);
	}

	public MessageProcessor[] getStackMessageProcessors() {
		return getMessageProcessors();
	}
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. 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.Separators;
import gov.nist.core.StackLogger;
import gov.nist.javax.sip.SipProviderImpl;
import gov.nist.javax.sip.message.SIPRequest;
import gov.nist.javax.sip.message.SIPResponse;
import gov.nist.javax.sip.stack.MessageChannel;
import gov.nist.javax.sip.stack.MobicentsHASIPClientTransaction;
import gov.nist.javax.sip.stack.SIPClientTransaction;
import gov.nist.javax.sip.stack.SIPDialog;
import gov.nist.javax.sip.stack.SIPServerTransaction;
import gov.nist.javax.sip.stack.SIPTransaction;

import java.util.Properties;
import java.util.StringTokenizer;

import javax.sip.DialogState;
import javax.sip.ListeningPoint;
import javax.sip.ObjectInUseException;
import javax.sip.PeerUnavailableException;
import javax.sip.ProviderDoesNotExistException;
import javax.sip.ServerTransaction;
import javax.sip.SipException;
import javax.sip.SipFactory;
import javax.sip.SipProvider;
import javax.sip.message.Request;

import org.mobicents.ext.javax.sip.SipStackImpl;
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
 * @author martins
 *
 */
public abstract class ClusteredSipStackImpl extends SipStackImpl implements ClusteredSipStack {

	protected SipCache sipCache = null;
	protected LoadBalancerHeartBeatingService loadBalancerHeartBeatingService = null;
	protected ReplicationStrategy replicationStrategy = ReplicationStrategy.ConfirmedDialog;
	protected LoadBalancerElector loadBalancerElector = null;
	private boolean replicateApplicationData = false;

	public ClusteredSipStackImpl(Properties configurationProperties) throws PeerUnavailableException {

		super(configurationProperties);

		String lbHbServiceClassName = configurationProperties.getProperty(LoadBalancerHeartBeatingService.LB_HB_SERVICE_CLASS_NAME);
		if(lbHbServiceClassName != null) {
			try {
	            loadBalancerHeartBeatingService = (LoadBalancerHeartBeatingService) Class.forName(lbHbServiceClassName).newInstance();
	        } catch (Exception e) {
	            String errmsg = "The loadBalancerHeartBeatingService class name: "
	                    + lbHbServiceClassName
	                    + " could not be instantiated. Ensure the " + LoadBalancerHeartBeatingService.LB_HB_SERVICE_CLASS_NAME + " property has been set correctly and that the class is on the classpath.";
	            throw new PeerUnavailableException(errmsg, e);
	        }
	        // create the load balancer elector if specified
	        String lbElectorClassName = configurationProperties.getProperty(LoadBalancerElector.IMPLEMENTATION_CLASS_NAME_PROPERTY);
			if(lbElectorClassName != null) {
				try {
		            loadBalancerElector = (LoadBalancerElector) Class.forName(lbElectorClassName).newInstance();
		        } catch (Exception e) {
		            String errmsg = "The loadBalancerElector class name: "
		                    + lbElectorClassName
		                    + " could not be instantiated. Ensure the " + LoadBalancerElector.IMPLEMENTATION_CLASS_NAME_PROPERTY + " property has been set correctly and that the class is on the classpath.";
		            throw new PeerUnavailableException(errmsg, e);
		        }
		        loadBalancerElector.setAddressFactory(SipFactory.getInstance().createAddressFactory());
		        loadBalancerElector.setStackLogger(getStackLogger());
		        loadBalancerElector.setService(loadBalancerHeartBeatingService);
			}
		}

		String replicationStrategyProperty = configurationProperties.getProperty(ClusteredSipStack.REPLICATION_STRATEGY_PROPERTY);
		if(replicationStrategyProperty != null) {
			replicationStrategy = ReplicationStrategy.valueOf(replicationStrategyProperty);
			if(replicationStrategy == ReplicationStrategy.EarlyDialog && transactionFactory == null) {
				// when using EarlyDialog replication strategy we need to have an HA transaction factory to replicate transactions
				transactionFactory = new MobicentsHATransactionFactory();
				transactionFactory.setSipStack(this);
			}
		}

		// 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);
		}
		if(loadBalancerHeartBeatingService != null) {
			loadBalancerHeartBeatingService.init(this, configurationProperties);
		}
		String replicateApplicationDataProperty = configurationProperties.getProperty(ClusteredSipStack.REPLICATE_APPLICATION_DATA);
		if(replicateApplicationDataProperty != null) {
			replicateApplicationData = Boolean.valueOf(replicateApplicationDataProperty);
		}
		// backward compatible hack to make sure old applications still replicate the app data if they don't use the new property
		if(replicateApplicationDataProperty == null && replicationStrategy == ReplicationStrategy.ConfirmedDialog) {
			replicateApplicationData = true;
		}
		if(getStackLogger().isLoggingEnabled(StackLogger.TRACE_INFO)) {
			getStackLogger().logInfo("Replication Strategy is " + replicationStrategy + " replicating application data " + replicateApplicationData);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see gov.nist.javax.sip.SipStackImpl#start()
	 */
	@Override
	public void start() throws ProviderDoesNotExistException, SipException {
		try {
			sipCache.start();
		} catch (Exception e) {
			throw new SipException("Unable to start the SipCache", e);
		}
		if(loadBalancerHeartBeatingService != null) {
			loadBalancerHeartBeatingService.start();
		}
		super.start();
	}

	public void closeAllTcpSockets() {

		ioHandler.closeAll();
	}

	/*
	 * (non-Javadoc)
	 * @see gov.nist.javax.sip.SipStackImpl#stop()
	 */
	@Override
	public void stop() {
		super.stop();
		try {
			sipCache.stop();
		} catch (Exception e) {
			getStackLogger().logError("Unable to stop the SipCache", e);
		}
		if(loadBalancerHeartBeatingService != null) {
			loadBalancerHeartBeatingService.stop();
		}
	}

	/*
	 * (non-Javadoc)
	 * @see gov.nist.javax.sip.stack.SIPTransactionStack#createDialog(gov.nist.javax.sip.stack.SIPTransaction)
	 */
	@Override
	public SIPDialog createDialog(SIPTransaction transaction) {
		if (sipCache.inLocalMode()) {
			return super.createDialog(transaction);
		}
		else {
			SIPDialog retval = null;
			if (transaction instanceof SIPClientTransaction) {
				final String dialogId = ((SIPRequest) transaction.getRequest()).getDialogId(false);
				retval = this.earlyDialogTable.get(dialogId);
				if (retval == null || (retval.getState() != null && retval.getState() != DialogState.EARLY)) {
					retval = (SIPDialog) HASipDialogFactory.createHASipDialog(replicationStrategy, transaction);
					this.earlyDialogTable.put(dialogId, retval);
				}
			} else {
				retval = (SIPDialog) HASipDialogFactory.createHASipDialog(replicationStrategy, transaction);
			}
			return retval;
		}
	}

	/*
	 * (non-Javadoc)
	 * @see gov.nist.javax.sip.stack.SIPTransactionStack#createDialog(gov.nist.javax.sip.stack.SIPClientTransaction, gov.nist.javax.sip.message.SIPResponse)
	 */
	@Override
	public SIPDialog createDialog(SIPClientTransaction transaction, SIPResponse sipResponse) {
		if (sipCache.inLocalMode()) {
			return super.createDialog(transaction,sipResponse);
		}
		else {
			final String dialogId = ((SIPRequest) transaction.getRequest()).getDialogId(false);
			SIPDialog retval = this.earlyDialogTable.get(dialogId);
			if (retval != null && sipResponse.isFinalResponse()) {
				this.earlyDialogTable.remove(dialogId);
			} else {
				retval = (SIPDialog) HASipDialogFactory.createHASipDialog(replicationStrategy, transaction, sipResponse);
			}
			return retval;
		}
    }

	/*
	 * (non-Javadoc)
	 * @see gov.nist.javax.sip.stack.SIPTransactionStack#createDialog(gov.nist.javax.sip.SipProviderImpl, gov.nist.javax.sip.message.SIPResponse)
	 */
	@Override
	public SIPDialog createDialog(SipProviderImpl sipProvider,
			SIPResponse sipResponse) {
		if (sipCache.inLocalMode()) {
			return super.createDialog(sipProvider, sipResponse);
		}
		else {
			return (SIPDialog) HASipDialogFactory.createHASipDialog(replicationStrategy, sipProvider, sipResponse);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see gov.nist.javax.sip.stack.SIPTransactionStack#getDialog(java.lang.String)
	 */
	@Override
	public SIPDialog getDialog(String dialogId) {
		if (sipCache.inLocalMode()) {
			return super.getDialog(dialogId);
		}
		else {
			if(getStackLogger().isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
				getStackLogger().logDebug("checking if the dialog " + dialogId + " is present in the local cache");
			}
			SIPDialog sipDialog = super.getDialog(dialogId);
			int nbToken = new StringTokenizer(dialogId, Separators.COLON).countTokens();
			// we should only check the cache for dialog Id where the remote tag is set since we support only established dialog failover
			// Issue 1378 : http://code.google.com/p/restcomm/issues/detail?id=1378
			// there can be more than 3 tokens if the callid part of the dialog id contains a COLON as well
			if(nbToken >= 3) {
				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");
						}
						SIPDialog existingDialog = super.putDialog(sipDialog);
						// avoid returning wrong dialog if 2 threads try to recreate
						// the dialog after failover, we use the one that won the race
						if(existingDialog != null) {
							sipDialog = existingDialog;
						}
					} else {
						if(getStackLogger().isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
							getStackLogger().logDebug("dialog " + dialogId + " not found in the distributed cache");
						}
					}
				} else {
					// we check for updates only if the dialog is confirmed
					if(sipDialog.getState() == DialogState.CONFIRMED) {
						if(getStackLogger().isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
							getStackLogger().logDebug("local dialog " + dialogId + " is present locally " + sipDialog + " checking if it needs to be updated from the cache");
						}
						try {
							sipCache.updateDialog(sipDialog);
						} catch (SipCacheException e) {
							getStackLogger().logError("sipStack " + this + " problem updating dialog " + dialogId + " from the distributed cache", e);
						}
					}
				}
			}
			return sipDialog;
		}
	}

	/*
	 * (non-Javadoc)
	 * @see gov.nist.javax.sip.stack.SIPTransactionStack#putDialog(gov.nist.javax.sip.stack.SIPDialog)
	 */
	@Override
	public SIPDialog putDialog(SIPDialog dialog) {
		return super.putDialog(dialog);
		// not needed it was causing the dialog to be put in the cache even for 1xx with a to tag
//		if (!sipCache.inLocalMode() && DialogState.CONFIRMED == dialog.getState()) {
//			// only replicate dialogs in confirmed state
//			putDialogIntoDistributedCache(dialog);
//		}
	}

	/*
	 * (non-Javadoc)
	 * @see gov.nist.javax.sip.stack.SIPTransactionStack#removeDialog(gov.nist.javax.sip.stack.SIPDialog)
	 */
	@Override
	public void removeDialog(SIPDialog dialog) {
		if (!sipCache.inLocalMode()) {
			removeDialogFromDistributedCache(dialog.getDialogId());
		}
		super.removeDialog(dialog);
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.ClusteredSipStack#remoteDialogRemoval(java.lang.String)
	 */
	public void remoteDialogRemoval(String dialogId) {
		// note we don't want a dialog terminated event, thus we need to go directly to map removal
		// assuming it's a confirmed dialog there is no chance it is on early dialogs too
		if (getStackLogger().isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
			getStackLogger().logDebug("sipStack " + this +
					" remote Dialog Removal of dialogId : " + dialogId);
		}
		SIPDialog sipDialog = super.dialogTable.remove(dialogId);
		if (sipDialog != null) {
			String mergeId = sipDialog.getMergeId();
			if (mergeId != null) {
				super.serverDialogMergeTestTable.remove(mergeId);
			}
		}
	}

	/**
	 * 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) {
			if(getStackLogger().isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
				getStackLogger().logDebug("sipStack " + this + " dialog " + dialogId + " was present in the distributed cache, initializing it after the load");
			}
			((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);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see gov.nist.javax.sip.stack.SIPTransactionStack#findTransaction(java.lang.String, boolean)
	 */
	@Override
	public SIPTransaction findTransaction(String transactionId, boolean isServer) {
		if(sipCache.inLocalMode() || replicationStrategy != ReplicationStrategy.EarlyDialog) {
			return super.findTransaction(transactionId,isServer);
		}
		final String txId = transactionId.toLowerCase();
		SIPTransaction sipTransaction = super.findTransaction(txId, isServer);
		if(sipTransaction == null && transactionFactory != null) {
			if(getStackLogger().isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
				getStackLogger().logDebug("local transaction " + txId + " server = " + isServer + " is null, checking in the distributed cache");
			}
			if(getStackLogger().isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
				getStackLogger().logDebug("sipStack " + this + " checking if the transaction " + txId + " server = " + isServer + " is present in the distributed cache");
			}
			if(isServer) {
				// fetch the corresponding server transaction from the cache instance
				try {
					sipTransaction = sipCache.getServerTransaction(txId);
					if(sipTransaction != null) {
						if(getStackLogger().isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
							getStackLogger().logDebug("sipStack " + this + " transaction " + txId + " server = " + isServer + " is present in the distributed cache");
						}
						SIPServerTransaction retval = serverTransactionTable.putIfAbsent(txId, (SIPServerTransaction) sipTransaction);
						if(retval != null) {
							sipTransaction = retval;
						}
					} else {
						if(getStackLogger().isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
							getStackLogger().logDebug("sipStack " + this + " transaction " + txId + " server = " + isServer + " is not present in the distributed cache");
						}
					}
				} catch (SipCacheException e) {
					getStackLogger().logError("sipStack " + this + " problem getting transaction " + txId + " server = " + isServer + " from the distributed cache", e);
				}
			} else {
				// fetch the corresponding client transaction from the cache instance
				try {
					sipTransaction = sipCache.getClientTransaction(txId);
					if(sipTransaction != null) {
						if(getStackLogger().isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
							getStackLogger().logDebug("sipStack " + this + " transaction " + txId + " server = " + isServer + " is present in the distributed cache");
						}
						SIPClientTransaction retval = clientTransactionTable.putIfAbsent(txId, (SIPClientTransaction) sipTransaction);
						if(retval != null) {
							sipTransaction = retval;
						} else {
							// start the transaction timer only when the transaction has been added to the stack
							// to avoid leaks on retransmissions
							((MobicentsHASIPClientTransaction)sipTransaction).startTransactionTimerOnFailover();
						}
					} else {
						if(getStackLogger().isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
							getStackLogger().logDebug("sipStack " + this + " transaction " + txId + " server = " + isServer + " is not present in the distributed cache");
						}
					}
				} catch (SipCacheException e) {
					getStackLogger().logError("sipStack " + this + " problem getting transaction " + txId + " server = " + isServer + " from the distributed cache", e);
				}
			}
		}
		return sipTransaction;
	}

	@Override
	public void removeTransaction(SIPTransaction sipTransaction) {
		super.removeTransaction(sipTransaction);
		if(sipCache.inLocalMode() && transactionFactory == null) {
			return;
		}
		if(transactionFactory != null && sipTransaction != null && replicationStrategy == ReplicationStrategy.EarlyDialog && sipTransaction.getMethod().equalsIgnoreCase(Request.INVITE)) {
			if(sipTransaction instanceof ServerTransaction) {
				// remove the corresponding server transaction from the cache instance
				try {
					sipCache.removeServerTransaction(sipTransaction.getTransactionId());
				} catch (SipCacheException e) {
					getStackLogger().logError("sipStack " + this + " problem getting transaction " + sipTransaction.getTransactionId() + " from the distributed cache", e);
				}
			} else {
				// remove the corresponding client transaction from the cache instance
				try {
					sipCache.removeClientTransaction(sipTransaction.getTransactionId());
				} catch (SipCacheException e) {
					getStackLogger().logError("sipStack " + this + " problem getting transaction " + sipTransaction.getTransactionId() + " from the distributed cache", e);
				}
			}
		}
	}

	@Override
	protected void removeTransactionHash(SIPTransaction sipTransaction) {
		super.removeTransactionHash(sipTransaction);
		if(sipCache.inLocalMode() && transactionFactory == null) {
			return;
		}
		if(transactionFactory != null && sipTransaction != null && replicationStrategy == ReplicationStrategy.EarlyDialog && sipTransaction.getMethod().equalsIgnoreCase(Request.INVITE)) {
			if(sipTransaction instanceof ServerTransaction) {
				// remove the corresponding server transaction from the cache instance
				try {
					sipCache.removeServerTransaction(sipTransaction.getTransactionId());
				} catch (SipCacheException e) {
					getStackLogger().logError("sipStack " + this + " problem getting transaction " + sipTransaction.getTransactionId() + " from the distributed cache", e);
				}
			} else {
				// remove the corresponding client transaction from the cache instance
				try {
					sipCache.removeClientTransaction(sipTransaction.getTransactionId());
				} catch (SipCacheException e) {
					getStackLogger().logError("sipStack " + this + " problem getting transaction " + sipTransaction.getTransactionId() + " from the distributed cache", e);
				}
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.ClusteredSipStack#setSipCache(org.mobicents.ha.javax.sip.cache.SipCache)
	 */
	public void setSipCache(SipCache sipCache) {
		this.sipCache = sipCache;
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.ClusteredSipStack#getSipCache()
	 */
	public SipCache getSipCache() {
		return sipCache;
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.ClusteredSipStack#getLoadBalancerHeartBeatingService()
	 */
	public LoadBalancerHeartBeatingService getLoadBalancerHeartBeatingService() {
		return loadBalancerHeartBeatingService;
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.ClusteredSipStack#getReplicationStrategy()
	 */
	public ReplicationStrategy getReplicationStrategy() {
		return replicationStrategy;
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.ClusteredSipStack#getLoadBalancerElector()
	 */
	public LoadBalancerElector getLoadBalancerElector() {
		return loadBalancerElector;
	}

	@Override
	public SIPClientTransaction createClientTransaction(SIPRequest sipRequest,
			MessageChannel encapsulatedMessageChannel) {
		if(sipCache.inLocalMode() || transactionFactory == null) {
			return super.createClientTransaction(sipRequest, encapsulatedMessageChannel);
		}
		return transactionFactory.createClientTransaction(sipRequest, encapsulatedMessageChannel);
	}

	@Override
	public SIPServerTransaction createServerTransaction(MessageChannel encapsulatedMessageChannel) {
		if(sipCache.inLocalMode() || transactionFactory == null) {
			return super.createServerTransaction(encapsulatedMessageChannel);
		}
		return transactionFactory.createServerTransaction(encapsulatedMessageChannel);
	}

	/**
	 * @return the replicateApplicationData
	 */
	public boolean isReplicateApplicationData() {
		return replicateApplicationData;
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.ClusteredSipStack#remoteServerTransactionRemoval(java.lang.String)
	 */
	public void remoteServerTransactionRemoval(String transactionId) {
		final String txId = transactionId.toLowerCase();
		// note we don't want a dialog terminated event, thus we need to go directly to map removal
		// assuming it's a confirmed dialog there is no chance it is on early dialogs too
		if (getStackLogger().isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
			getStackLogger().logDebug("sipStack " + this +
					" remote Server Transaction Removal of transaction Id : " + txId);
		}
		// the transaction id is set to lower case in the cache so it might not remove it correctly
		SIPServerTransaction sipServerTransaction = super.serverTransactionTable.remove(txId);
		if (sipServerTransaction != null) {
			super.removeFromMergeTable(sipServerTransaction);
			super.removePendingTransaction(sipServerTransaction);
			super.removeTransactionPendingAck(sipServerTransaction);
		}
	}
	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.ClusteredSipStack#remoteClientTransactionRemoval(java.lang.String)
	 */
	public void remoteClientTransactionRemoval(String transactionId) {
		final String txId = transactionId.toLowerCase();
		// note we don't want a dialog terminated event, thus we need to go directly to map removal
		// assuming it's a confirmed dialog there is no chance it is on early dialogs too
		if (getStackLogger().isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
			getStackLogger().logDebug("sipStack " + this +
					" remote Client Transaction Removal of transaction Id : " + txId);
		}
		// the transaction id is set to lower case in the cache so it might not remove it correctly
		SIPClientTransaction sipClientTransaction = super.clientTransactionTable.remove(txId);
	}

	public SipProvider createSipProvider(ListeningPoint listeningPoint)
			throws ObjectInUseException {
		if (sipProviderFactory != null) {
			return sipProviderFactory.createSipProvider(listeningPoint);
		}
		return super.createSipProvider(listeningPoint);
	}
}


package org.mobicents.ha.javax.sip.cache.hz;

import gov.nist.core.StackLogger;
import gov.nist.javax.sip.stack.MessageChannel;
import gov.nist.javax.sip.stack.MessageProcessor;
import gov.nist.javax.sip.stack.MobicentsHASIPClientTransaction;
import gov.nist.javax.sip.stack.SIPClientTransaction;
import gov.nist.javax.sip.stack.SIPTransactionStack;

import java.io.IOException;
import java.net.InetAddress;
import java.text.ParseException;
import java.util.Map;

import javax.sip.PeerUnavailableException;

import org.mobicents.ha.javax.sip.ClusteredSipStack;
import org.mobicents.ha.javax.sip.cache.SipCacheException;

import com.hazelcast.core.IMap;

public class SIPClientTransactionCacheData {

	private ClusteredSipStack stack;
	private StackLogger logger;
	private IMap<String, Object> clientTransactions;
	private IMap<String, Object> clientTransactionsApp;

	public SIPClientTransactionCacheData(
			ClusteredSipStack s, IMap<String, Object> clientTXCache,
			IMap<String, Object> clientTxAppCache) {
		stack = s;
		logger = s.getStackLogger();
		clientTransactions = clientTXCache;
		clientTransactionsApp = clientTxAppCache;
	}

	public SIPClientTransaction getClientTransaction(String txId)
			throws SipCacheException {
		SIPClientTransaction haSipClientTransaction = null;
		if(logger.isLoggingEnabled(StackLogger.TRACE_TRACE))
			logger.logDebug("getServerTransaction(" + txId + ")");

		try {
			final Map<String, Object> transactionMetaData = (Map<String, Object>)clientTransactions.get(txId);
			final Object txAppData = clientTransactionsApp.get(txId);

			haSipClientTransaction = createClientTransaction(txId, transactionMetaData, txAppData);

		} catch (Exception e) {
			throw new SipCacheException(e);
		}

		return haSipClientTransaction;
	}

	public void putClientTransaction(SIPClientTransaction clientTransaction)
			throws SipCacheException {
		if(logger.isLoggingEnabled(StackLogger.TRACE_TRACE))
			logger.logDebug("putClientTransaction(" + clientTransaction.getTransactionId() + ")");

		try {
			final MobicentsHASIPClientTransaction haClientTransaction = (MobicentsHASIPClientTransaction) clientTransaction;

			// metadata
			Map<String, Object> metaData = haClientTransaction.getMetaDataToReplicate();
			clientTransactions.put(clientTransaction.getTransactionId(), metaData);

			// app data
			final Object transactionAppData = haClientTransaction.getApplicationDataToReplicate();
			if(transactionAppData != null) {
				clientTransactionsApp.put(clientTransaction.getTransactionId(), transactionAppData);
			}
		} catch (Exception e) {
			throw new SipCacheException(e);
		}
	}

	public void removeClientTransaction(String txId)
			throws SipCacheException {
		if(logger.isLoggingEnabled(StackLogger.TRACE_TRACE))
			logger.logDebug("removeClientTransaction(" + txId + ")");
		clientTransactions.remove(txId);
		clientTransactionsApp.remove(txId);
	}

	public MobicentsHASIPClientTransaction createClientTransaction(String txId, Map<String, Object> transactionMetaData, Object transactionAppData) throws SipCacheException {
		MobicentsHASIPClientTransaction haClientTransaction = null;
		if(transactionMetaData != null) {
			if(logger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
				logger.logDebug("sipStack " + this + " client transaction " + txId + " is present in the distributed cache, recreating it locally");
			}
			String channelTransport = (String) transactionMetaData.get(MobicentsHASIPClientTransaction.TRANSPORT);
			if (logger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
				logger.logDebug(txId + " : transport " + channelTransport);
			}
			InetAddress channelIp = (InetAddress) transactionMetaData.get(MobicentsHASIPClientTransaction.PEER_IP);
			if (logger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
				logger.logDebug(txId + " : channel peer Ip address " + channelIp);
			}
			Integer channelPort = (Integer) transactionMetaData.get(MobicentsHASIPClientTransaction.PEER_PORT);
			if (logger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
				logger.logDebug(txId + " : channel peer port " + channelPort);
			}
			Integer myPort = (Integer) transactionMetaData.get(MobicentsHASIPClientTransaction.MY_PORT);
			if (logger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
				logger.logDebug(txId + " : my port " + myPort);
			}
			MessageChannel messageChannel = null;
			MessageProcessor[] messageProcessors = stack.getStackMessageProcessors();
			for (MessageProcessor messageProcessor : messageProcessors) {
				if(messageProcessor.getTransport().equalsIgnoreCase(channelTransport)) {
					try {
						messageChannel = messageProcessor.createMessageChannel(channelIp, channelPort);
					} catch (IOException e) {
						logger.logError("couldn't recreate the message channel on ip address "
								+ channelIp + " and port " + channelPort, e);
					}
					break;
				}
			}

			haClientTransaction = new MobicentsHASIPClientTransaction((SIPTransactionStack) stack, messageChannel);
			haClientTransaction.setBranch(txId);
			try {
				updateClientTransactionMetaData(transactionMetaData, transactionAppData, haClientTransaction, true);
			} catch (PeerUnavailableException e) {
				throw new SipCacheException("A problem occured while retrieving the following transaction " + txId + " from the Cache", e);
			} catch (ParseException e) {
				throw new SipCacheException("A problem occured while retrieving the following transaction " + txId + " from the Cache", e);
			}
		} else {
			if(logger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
				logger.logDebug("sipStack " + this + " client transaction " + txId + " not found in the distributed cache");
			}
		}

		return haClientTransaction;
	}

	/**
	 * Update the haSipDialog passed in param with the dialogMetaData and app meta data
	 * @param transactionMetaData
	 * @param transactionAppData
	 * @param haClientTransaction
	 * @throws ParseException
	 * @throws PeerUnavailableException
	 */
	private void updateClientTransactionMetaData(Map<String, Object> transactionMetaData, Object transactionAppData, MobicentsHASIPClientTransaction haClientTransaction, boolean recreation) throws ParseException,
			PeerUnavailableException {
		haClientTransaction.setMetaDataToReplicate(transactionMetaData, recreation);
		if(logger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
			logger.logDebug("updating application data with the one from cache " + transactionAppData);
		}
		haClientTransaction.setApplicationDataToReplicate(transactionAppData);
	}

}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. 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 java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;

import javax.sip.address.Address;

/**
 * Round robin implementation of the abstract load balancer elector.
 *
 * @author martins
 *
 */
public class RoundRobinLoadBalancerElector extends AbstractLoadBalancerElector {

	/**
	 * the list of balancer addresses
	 */
	final ArrayList<SipLoadBalancer> balancers = new ArrayList<SipLoadBalancer>();

	/**
	 * index use to iterate the balancer's list
	 */
	AtomicInteger index = new AtomicInteger(0);

	/* (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.AbstractLoadBalancerElector#addLoadBalancer(javax.sip.address.Address)
	 */
	@Override
	void addLoadBalancer(SipLoadBalancer address) {
		synchronized (balancers) {
			balancers.add(address);
		}
	}

	/**
	 * Computes the index of the next balancer to retrieve. Adaptation of the {@link AtomicInteger} incrementAndGet() code.
	 *
	 * @return
	 */
	private int getNextIndex() {
		for (;;) {
            int current = index.get();
            int next = (current == balancers.size() ? 1 : current + 1);
            if (index.compareAndSet(current, next))
                return next-1;
        }
	}

	/* (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.AbstractLoadBalancerElector#getLoadBalancer()
	 */
	@Override
	public Address getLoadBalancer() {
		return getLoadBalancer(true).getSipAddress();
	}

	private SipLoadBalancer getLoadBalancer(boolean secondChance) {
		SipLoadBalancer result = null;
		switch (balancers.size()) {
		case 1:
			result = balancers.get(0);
			if(!result.isAvailable()) return null;
			return result;
		case 0:
			return null;
		default:
			try {
				result = balancers.get(getNextIndex());
				if(!result.isAvailable()) {
					for(int tries = 0; tries<balancers.size(); tries++) {
						result = balancers.get(getNextIndex());
						if(result.isAvailable()) return result;
					}
				}
			}
			catch(IndexOutOfBoundsException e) {
				if (secondChance) {
					// the exception can happen when removing a balancer, since this is not frequent lets give a second chance
					return getLoadBalancer(false);
				}
				else {
					if (logger.isLoggingEnabled()) {
						logger.logError("Failed to get load balancer",e);
					}
					return null;
				}
			}
		}
		return result;
	}

	/* (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.AbstractLoadBalancerElector#removeLoadBalancer(javax.sip.address.Address)
	 */
	@Override
	void removeLoadBalancer(Address address) {
		synchronized (balancers) {
			balancers.remove(address);
		}
	}

	public SipLoadBalancer getLoadBalancerExt() {
		return getLoadBalancer(true);
	}

	public void pingingloadBalancer(SipLoadBalancer balancerDescription) {
		// TODO Auto-generated method stub

	}

	public void pingedloadBalancer(SipLoadBalancer balancerDescription) {
		// TODO Auto-generated method stub

	}

}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. 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.cache;

import gov.nist.javax.sip.stack.SIPClientTransaction;
import gov.nist.javax.sip.stack.SIPDialog;
import gov.nist.javax.sip.stack.SIPServerTransaction;

import java.util.Properties;

import org.jboss.cache.Fqn;
import org.jboss.cache.Region;
import org.mobicents.cache.MobicentsCache;
import org.mobicents.cluster.MobicentsCluster;
import org.mobicents.ha.javax.sip.ClusteredSipStack;
import org.mobicents.ha.javax.sip.ReplicationStrategy;

/**
 * Implementation of the SipCache interface, backed by a Restcomm Cache (JBoss Cache 3.X Cache).
 * The configuration of Restcomm Cache can be set throught the following Restcomm SIP Stack property :
 * <b>org.mobicents.ha.javax.sip.JBOSS_CACHE_CONFIG_PATH</b>
 *
 * @author jean.deruelle@gmail.com
 * @author martins
 *
 */
public abstract class MobicentsSipCache implements SipCache {

	ClusteredSipStack clusteredSipStack = null;
	protected Properties configProperties;
	protected MobicentsCluster cluster;

	private final String name;
	private final ClassLoader serializationClassLoader;

	private static final String DEFAULT_NAME = "jain-sip-ha";

	private SIPDialogCacheData dialogsCacheData;
	private DialogDataRemovalListener dialogDataRemovalListener;
	private ServerTransactionCacheData serverTransactionCacheData;
	private ServerTransactionDataRemovalListener serverTransactionDataRemovalListener;
	private ClientTransactionCacheData clientTransactionCacheData;
	private ClientTransactionDataRemovalListener clientTransactionDataRemovalListener;

	/**
	 *
	 */
	public MobicentsSipCache() {
		this(DEFAULT_NAME,null);
	}

	public MobicentsSipCache(String name) {
		this(name,null);
	}

	public MobicentsSipCache(ClassLoader serializationClassLoader) {
		this(DEFAULT_NAME,serializationClassLoader);
	}

	public MobicentsSipCache(String name, ClassLoader serializationClassLoader) {
		if (name == null) {
			throw new NullPointerException("null name");
		}
		this.name = name;
		this.serializationClassLoader = serializationClassLoader;
	}

	/* (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.SipCache#putDialog(gov.nist.javax.sip.stack.SIPDialog)
	 */
	public void putDialog(SIPDialog dialog) throws SipCacheException {
		dialogsCacheData.putSIPDialog(dialog);
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.SipCache#evictDialog(java.lang.String)
	 */
	public void evictDialog(String dialogId) {
		dialogsCacheData.evictSIPDialog(dialogId);
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.SipCache#updateDialog(gov.nist.javax.sip.stack.SIPDialog)
	 */
	public void updateDialog(SIPDialog dialog) throws SipCacheException {
		dialogsCacheData.updateSIPDialog(dialog);
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.SipCache#getDialog(java.lang.String)
	 */
	public SIPDialog getDialog(String dialogId) throws SipCacheException {
		return dialogsCacheData.getSIPDialog(dialogId);
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.SipCache#removeDialog(java.lang.String)
	 */
	public void removeDialog(String dialogId) throws SipCacheException {
		dialogsCacheData.removeSIPDialog(dialogId);
	}

	/* (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.SipCache#setClusteredSipStack(org.mobicents.ha.javax.sip.ClusteredSipStack)
	 */
	public void setClusteredSipStack(ClusteredSipStack clusteredSipStack) {
		this.clusteredSipStack  = clusteredSipStack;
	}

	/* (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.SipCache#setConfigurationProperties(java.util.Properties)
	 */
	public void setConfigurationProperties(Properties configurationProperties) {
		this.configProperties = configurationProperties;
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.SipCache#init()
	 */
	public abstract void init() throws SipCacheException;

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.SipCache#start()
	 */
	public void start() throws SipCacheException {
		cluster.getMobicentsCache().startCache();
		dialogsCacheData = new SIPDialogCacheData(Fqn.fromElements(name,SipCache.DIALOG_PARENT_FQN_ELEMENT),cluster.getMobicentsCache(), clusteredSipStack);
		dialogsCacheData.create();
		dialogDataRemovalListener = new DialogDataRemovalListener(dialogsCacheData.getNodeFqn(), clusteredSipStack);
		cluster.addDataRemovalListener(dialogDataRemovalListener);
		if(clusteredSipStack.getReplicationStrategy() == ReplicationStrategy.EarlyDialog) {
			serverTransactionCacheData = new ServerTransactionCacheData(Fqn.fromElements(name,SipCache.SERVER_TX_PARENT_FQN_ELEMENT),cluster.getMobicentsCache(), clusteredSipStack);
			serverTransactionCacheData.create();
			serverTransactionDataRemovalListener = new ServerTransactionDataRemovalListener(serverTransactionCacheData.getNodeFqn(), clusteredSipStack);
			cluster.addDataRemovalListener(serverTransactionDataRemovalListener);
			clientTransactionCacheData = new ClientTransactionCacheData(Fqn.fromElements(name,SipCache.CLIENT_TX_PARENT_FQN_ELEMENT),cluster.getMobicentsCache(), clusteredSipStack);
			clientTransactionCacheData.create();
			clientTransactionDataRemovalListener = new ClientTransactionDataRemovalListener(clientTransactionCacheData.getNodeFqn(), clusteredSipStack);
			cluster.addDataRemovalListener(clientTransactionDataRemovalListener);
		}
		if (serializationClassLoader != null) {
			Region region = getMobicentsCache().getJBossCache().getRegion(dialogsCacheData.getNodeFqn(),true);
			region.registerContextClassLoader(serializationClassLoader);
			region.activate();
			if(clusteredSipStack.getReplicationStrategy() == ReplicationStrategy.EarlyDialog) {
				Region stxRegion = getMobicentsCache().getJBossCache().getRegion(serverTransactionCacheData.getNodeFqn(),true);
				stxRegion.registerContextClassLoader(serializationClassLoader);
				stxRegion.activate();
				Region ctxRegion = getMobicentsCache().getJBossCache().getRegion(clientTransactionCacheData.getNodeFqn(),true);
				ctxRegion.registerContextClassLoader(serializationClassLoader);
				ctxRegion.activate();
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.SipCache#stop()
	 */
	public void stop() throws SipCacheException {
		dialogsCacheData.remove();
		cluster.removeDataRemovalListener(dialogDataRemovalListener);
		if(clusteredSipStack.getReplicationStrategy() == ReplicationStrategy.EarlyDialog) {
			serverTransactionCacheData.remove();
			cluster.removeDataRemovalListener(serverTransactionDataRemovalListener);
			clientTransactionCacheData.remove();
			cluster.removeDataRemovalListener(clientTransactionDataRemovalListener);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.SipCache#inLocalMode()
	 */
	public boolean inLocalMode() {
		return getMobicentsCache().isLocalMode();
	}

	public String getName() {
		return name;
	}

	public ClassLoader getSerializationClassLoader() {
		return serializationClassLoader;
	}

	public MobicentsCache getMobicentsCache() {
		return cluster.getMobicentsCache();
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.SipCache#getServerTransaction(java.lang.String)
	 */
	public SIPServerTransaction getServerTransaction(String transactionId) throws SipCacheException {
		return serverTransactionCacheData.getServerTransaction(transactionId);
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.SipCache#putServerTransaction(gov.nist.javax.sip.stack.SIPServerTransaction)
	 */
	public void putServerTransaction(SIPServerTransaction serverTransaction) throws SipCacheException {
		serverTransactionCacheData.putServerTransaction(serverTransaction);
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.SipCache#removeServerTransaction(java.lang.String)
	 */
	public void removeServerTransaction(String transactionId) throws SipCacheException {
		serverTransactionCacheData.removeServerTransaction(transactionId);
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.SipCache#getClientTransaction(java.lang.String)
	 */
	public SIPClientTransaction getClientTransaction(String transactionId) throws SipCacheException {
		return clientTransactionCacheData.getClientTransaction(transactionId);
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.SipCache#putClientTransaction(gov.nist.javax.sip.stack.SIPClientTransaction)
	 */
	public void putClientTransaction(SIPClientTransaction clientTransaction) throws SipCacheException {
		clientTransactionCacheData.putClientTransaction(clientTransaction);
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.ha.javax.sip.cache.SipCache#removeClientTransaction(java.lang.String)
	 */
	public void removeClientTransaction(String transactionId) throws SipCacheException {
		clientTransactionCacheData.removeClientTransaction(transactionId);
	}
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. 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.javax.sip.message.SIPRequest;
import gov.nist.javax.sip.stack.MessageChannel;
import gov.nist.javax.sip.stack.MobicentsHASIPClientTransaction;
import gov.nist.javax.sip.stack.MobicentsHASIPServerTransaction;
import gov.nist.javax.sip.stack.SIPClientTransaction;
import gov.nist.javax.sip.stack.SIPServerTransaction;
import gov.nist.javax.sip.stack.SIPTransactionStack;

import javax.sip.SipStack;

import org.mobicents.ext.javax.sip.TransactionFactory;

/**
 * @author jean.deruelle@gmail.com
 *
 */
public class MobicentsHATransactionFactory implements TransactionFactory {

	private SIPTransactionStack sipStack;

	/* (non-Javadoc)
	 * @see org.mobicents.ext.javax.sip.TransactionFactory#createClientTransaction(gov.nist.javax.sip.message.SIPRequest, gov.nist.javax.sip.stack.MessageChannel)
	 */
	public SIPClientTransaction createClientTransaction(SIPRequest sipRequest,
			MessageChannel encapsulatedMessageChannel) {
		MobicentsHASIPClientTransaction ct = new MobicentsHASIPClientTransaction(sipStack,
				encapsulatedMessageChannel);
        ct.setOriginalRequest(sipRequest);
        return ct;
	}

	/* (non-Javadoc)
	 * @see org.mobicents.ext.javax.sip.TransactionFactory#createServerTransaction(gov.nist.javax.sip.stack.MessageChannel)
	 */
	public SIPServerTransaction createServerTransaction(
			MessageChannel encapsulatedMessageChannel) {
		return new MobicentsHASIPServerTransaction(sipStack, encapsulatedMessageChannel);
	}

	/* (non-Javadoc)
	 * @see org.mobicents.ext.javax.sip.TransactionFactory#setSipStack(javax.sip.SipStack)
	 */
	public void setSipStack(SipStack sipStack) {
		this.sipStack = (SIPTransactionStack) sipStack;
	}

}