org.restcomm.cluster.DefaultMobicentsCluster Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of core Show documentation
Show all versions of core Show documentation
Restcomm Cluster Framework Core
/*
* TeleStax, Open Source Cloud Communications
* Copyright 2011-2016, Telestax Inc and individual contributors
* by the @authors tag.
*
* This program is free software: you can redistribute it and/or modify
* under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see
*/
package org.restcomm.cluster;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import javax.transaction.TransactionManager;
import org.apache.log4j.Logger;
import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.notifications.Listener;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
import org.infinispan.notifications.cachemanagerlistener.annotation.ViewChanged;
import org.infinispan.notifications.cachemanagerlistener.event.ViewChangedEvent;
import org.infinispan.remoting.transport.Address;
import org.infinispan.tree.Fqn;
import org.infinispan.tree.NodeKey;
import org.infinispan.tree.NodeKey.Type;
import org.infinispan.tree.TreeCache;
import org.restcomm.cache.MobicentsCache;
import org.restcomm.cluster.cache.ClusteredCacheData;
import org.restcomm.cluster.cache.ClusteredCacheDataIndexingHandler;
import org.restcomm.cluster.cache.DefaultClusteredCacheDataIndexingHandler;
import org.restcomm.cluster.election.ClientLocalListenerElector;
import org.restcomm.cluster.election.ClusterElector;
/**
* Listener that is to be used for cluster wide replication(meaning no buddy
* replication, no data gravitation). It will index activity on nodes marking
* current node as owner(this is semi-gravitation behavior (we don't delete, we
* just mark)).
*
* Indexing is only at node level, i.e., there is no
* reverse indexing, so it has to iterate through whole resource group data FQNs to check which
* nodes should be taken over.
*
* @author Bartosz Baranowski
* @author martins
* @author András Kőkuti
*/
@Listener
public class DefaultMobicentsCluster implements MobicentsCluster {
private static final Logger logger = Logger.getLogger(DefaultMobicentsCluster.class);
private final SortedSet failOverListeners;
@SuppressWarnings("unchecked")
private final ConcurrentHashMap dataRemovalListeners;
private final MobicentsCache mobicentsCache;
private final TransactionManager txMgr;
private final ClusterElector elector;
private final DefaultClusteredCacheDataIndexingHandler clusteredCacheDataIndexingHandler;
private List currentView;
private boolean started;
@SuppressWarnings("unchecked")
public DefaultMobicentsCluster(MobicentsCache watchedCache, TransactionManager txMgr, ClusterElector elector) {
this.failOverListeners = Collections.synchronizedSortedSet(new TreeSet(new FailOverListenerPriorityComparator()));
this.dataRemovalListeners = new ConcurrentHashMap();
this.mobicentsCache = watchedCache;
this.txMgr = txMgr;
this.elector = elector;
this.clusteredCacheDataIndexingHandler = new DefaultClusteredCacheDataIndexingHandler();
}
/* (non-Javadoc)
* @see org.mobicents.cluster.MobicentsCluster#getLocalAddress()
*/
public Address getLocalAddress() {
return mobicentsCache.getJBossCache().getCache().getCacheManager().getAddress();
}
/* (non-Javadoc)
* @see org.mobicents.cluster.MobicentsCluster#getClusterMembers()
*/
public List getClusterMembers() {
if (currentView != null) {
return Collections.unmodifiableList(currentView);
}
else {
final Address localAddress = getLocalAddress();
if (localAddress == null) {
return Collections.emptyList();
}
else {
final List list = new ArrayList();
list.add(localAddress);
return Collections.unmodifiableList(list);
}
}
}
/* (non-Javadoc)
* @see org.mobicents.cluster.MobicentsCluster#isHeadMember()
*/
public boolean isHeadMember() {
final Address localAddress = getLocalAddress();
if (localAddress != null) {
final List clusterMembers = getClusterMembers();
return !clusterMembers.isEmpty() && clusterMembers.get(0).equals(localAddress);
}
else {
return true;
}
}
/*
* (non-Javadoc)
* @see org.mobicents.cluster.MobicentsCluster#isSingleMember()
*/
public boolean isSingleMember() {
final Address localAddress = getLocalAddress();
if (localAddress != null) {
final List clusterMembers = getClusterMembers();
return clusterMembers.size() == 1;
}
else {
return true;
}
}
/**
* Method handle a change on the cluster members set
* @param event
*/
@ViewChanged
public synchronized void viewChanged(ViewChangedEvent event) {
if (logger.isDebugEnabled()) {
logger.debug("onViewChangeEvent : id[" + event.getViewId() + "] : event local address[" + event.getLocalAddress() + "]");
}
final List oldView = currentView;
currentView = new ArrayList(event.getNewMembers());
final Address localAddress = getLocalAddress();
//just a precaution, it can be null!
if (oldView != null) {
// recover stuff from lost members
Runnable runnable = new Runnable() {
public void run() {
for (Address oldMember : oldView) {
if (!currentView.contains(oldMember)) {
if (logger.isDebugEnabled()) {
logger.debug("onViewChangeEvent : processing lost member " + oldMember);
}
for (FailOverListener localListener : failOverListeners) {
ClientLocalListenerElector localListenerElector = localListener.getElector();
if (localListenerElector != null) {
// going to use the local listener elector instead, which gives results based on data
performTakeOver(localListener,oldMember,localAddress, true);
}
else {
List electionView = getElectionView(oldMember);
if(electionView!=null && elector.elect(electionView).equals(localAddress))
{
performTakeOver(localListener, oldMember, localAddress, false);
}
//cleanAfterTakeOver(localListener, oldMember);
}
}
}
}
}
};
Thread t = new Thread(runnable);
t.start();
}
}
@SuppressWarnings("rawtypes")
@CacheEntryRemoved
public void cacheEntryRemoved(CacheEntryRemovedEvent event){
if (logger.isDebugEnabled()) {
logger.debug("cacheEntryRemoved : event[ "+ event +"]");
}
if(!event.isPre() && !event.isOriginLocal() && event.getKey() != null && (event.getKey() instanceof NodeKey) && ((NodeKey)event.getKey()).getContents() == Type.STRUCTURE){
Fqn changed = ((NodeKey)event.getKey()).getFqn();
final DataRemovalListener dataRemovalListener = dataRemovalListeners.get(changed.getParent());
if (dataRemovalListener != null) {
dataRemovalListener.dataRemoved(changed);
}
}
}
/**
*
*/
@SuppressWarnings("unchecked")
private void performTakeOver(FailOverListener localListener, Address lostMember, Address localAddress, boolean useLocalListenerElector) {
//WARNING1: avoid using string representation, it may look ok, but hash is different if Fqn is not composed only from strings
//WARNING2: use Fqn.fromRelativeElemenets(); -- Fqn.fromElements(); adds Fqn.SEPARATOR at beggin of Fqn.
if (logger.isDebugEnabled()) {
logger.debug("onViewChangeEvent : " + localAddress + " failing over lost member " + lostMember + ", useLocalListenerElector=" + useLocalListenerElector);
}
final TreeCache jbossCache = mobicentsCache.getJBossCache();
final Fqn rootFqnOfChanges = localListener.getBaseFqn();
//final String rootCacheOfChanges = localListener.getCacheName();
boolean createdTx = false;
boolean doRollback = true;
try {
if (txMgr != null && txMgr.getTransaction() == null) {
txMgr.begin();
createdTx = true;
}
if (createdTx) {
txMgr.commit();
createdTx = false;
}
if (txMgr != null && txMgr.getTransaction() == null) {
txMgr.begin();
createdTx = true;
}
localListener.failOverClusterMember(lostMember);
Set © 2015 - 2025 Weber Informatics LLC | Privacy Policy