
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.CacheEntryModified;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
import org.infinispan.notifications.cachelistener.event.CacheEntryModifiedEvent;
import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
import org.infinispan.notifications.cachemanagerlistener.annotation.CacheStarted;
import org.infinispan.notifications.cachemanagerlistener.annotation.ViewChanged;
import org.infinispan.notifications.cachemanagerlistener.event.CacheStartedEvent;
import org.infinispan.notifications.cachemanagerlistener.event.ViewChangedEvent;
import org.infinispan.remoting.transport.Address;
import org.restcomm.cache.tree.Fqn;
import org.restcomm.cache.CacheData;
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;
import org.restcomm.cache.tree.Node;
/**
* 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.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 ){
Fqn changed = Fqn.fromString(event.getKey().toString());
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 Fqn rootFqnOfChanges = localListener.getBaseFqn();
Set children = new Node(mobicentsCache.getJBossCache(), rootFqnOfChanges).getChildNames();
boolean createdTx = false;
boolean doRollback = true;
try {
if (txMgr != null && txMgr.getTransaction() == null) {
txMgr.begin();
createdTx = true;
}
localListener.failOverClusterMember(lostMember);
for (Object childName : children) {
logger.error(childName);
// Here in values we store data and... inet node., we must match
// passed one.
final ClusteredCacheData clusteredCacheData = new ClusteredCacheData(Fqn.fromRelativeElements(rootFqnOfChanges, childName),this);
if (clusteredCacheData.exists()) {
Address address = clusteredCacheData.getClusterNodeAddress();
if (address != null && address.equals(lostMember)) {
// may need to do election using client local listener
if (useLocalListenerElector) {
if(!localAddress.equals(localListener.getElector().elect(currentView, clusteredCacheData))) {
// not elected, move on
continue;
}
}
// call back the listener
localListener.wonOwnership(clusteredCacheData);
// change ownership
clusteredCacheData.setClusterNodeAddress(localAddress);
}
}else
{
//FIXME: debug?
if(logger.isDebugEnabled())
{
logger.debug(" Attempt to index: "+Fqn.fromRelativeElements(rootFqnOfChanges, childName)+" failed, node does not exist.");
}
}
}
doRollback = false;
} catch (Exception e) {
logger.error(e.getMessage(),e);
} finally {
if (createdTx) {
try {
if (!doRollback) {
txMgr.commit();
}
else {
txMgr.rollback();
}
} catch (Exception e) {
logger.error(e.getMessage(),e);
}
}
}
}
private List getElectionView(Address deadMember) {
return currentView;
}
/*private String getBuddyBackupFqn(Address owner)
{
//FIXME: switch to BuddyFqnTransformer
String lostMemberFqnizied = owner.toString().replace(":", "_");
String fqn = BUDDY_BACKUP_FQN_ROOT + lostMemberFqnizied ;
return fqn;
}*/
// NOTE USED FOR NOW
/*
@NodeCreated
public void onNodeCreateddEvent(NodeCreatedEvent event) {
if (log.isDebugEnabled()) {
log.debug("onNodeCreateddEvent : " + event.getFqn() + " : local[" + event.isOriginLocal() + "] pre[" + event.isPre() + "] : event local address[" + event.getCache().getLocalAddress()
+ "]");
}
}
@NodeModified
public void onNodeModifiedEvent(NodeModifiedEvent event) {
if (log.isDebugEnabled()) {
log.debug("onNodeModifiedEvent : pre[" + event.isPre() + "] : event local address[" + event.getCache().getLocalAddress() + "]");
}
}
@NodeMoved
public void onNodeMovedEvent(NodeMovedEvent event) {
if (log.isDebugEnabled()) {
log.debug("onNodeMovedEvent : " + event.getFqn() + " : local[" + event.isOriginLocal() + "] pre[" + event.isPre() + "] ");
}
}
@NodeVisited
public void onNodeVisitedEvent(NodeVisitedEvent event) {
if (log.isDebugEnabled()) {
log.debug("onNodeVisitedEvent : " + event.getFqn() + " : local[" + event.isOriginLocal() + "] pre[" + event.isPre() + "] ");
}
}
@NodeLoaded
public void onNodeLoadedEvent(NodeLoadedEvent event) {
if (log.isDebugEnabled()) {
log.debug("onNodeLoadedEvent : " + event.getFqn() + " : local[" + event.isOriginLocal() + "] pre[" + event.isPre() + "] ");
}
}
@NodeEvicted
public void onNodeEvictedEvent(NodeEvictedEvent event) {
if (log.isDebugEnabled()) {
log.debug("onNodeEvictedEvent : " + event.getFqn() + " : local[" + event.isOriginLocal() + "] pre[" + event.isPre() + "] ");
}
}
@NodeInvalidated
public void onNodeInvalidatedEvent(NodeInvalidatedEvent event) {
if (log.isDebugEnabled()) {
log.debug("onNodeInvalidatedEvent : " + event.getFqn() + " : local[" + event.isOriginLocal() + "] pre[" + event.isPre() + "] ");
}
}
@NodeActivated
public void onNodeActivatedEvent(NodeActivatedEvent event) {
if (log.isDebugEnabled()) {
log.debug("onNodeActivatedEvent : " + event.getFqn() + " : local[" + event.isOriginLocal() + "] pre[" + event.isPre() + "] ");
}
}
@NodePassivated
public void onNodePassivatedEvent(NodePassivatedEvent event) {
if (log.isDebugEnabled()) {
log.debug("onNodePassivatedEvent : " + event.getFqn() + " : local[" + event.isOriginLocal() + "] pre[" + event.isPre() + "] ");
}
}
@BuddyGroupChanged
public void onBuddyGroupChangedEvent(BuddyGroupChangedEvent event) {
if (log.isDebugEnabled()) {
log.debug("onBuddyGroupChangedEvent : pre[" + event.isPre() + "] ");
}
}
@CacheStarted
public void onCacheStartedEvent(CacheStartedEvent event) {
if (log.isDebugEnabled()) {
log.debug("onCacheStartedEvent : pre[" + event.isPre() + "] ");
}
}
@CacheStopped
public void onCacheStoppedEvent(CacheStoppedEvent event) {
if (log.isDebugEnabled()) {
log.debug("onCacheStoppedEvent : pre[" + event.isPre() + "] ");
}
}
*/
// LOCAL LISTENERS MANAGEMENT
/*
* (non-Javadoc)
* @see org.mobicents.cluster.MobicentsCluster#addFailOverListener(org.mobicents.cluster.FailOverListener)
*/
public boolean addFailOverListener(FailOverListener localListener) {
if (logger.isDebugEnabled()) {
logger.debug("Adding local listener " + localListener);
}
for(FailOverListener failOverListener : failOverListeners) {
if (failOverListener.getBaseFqn().equals(localListener.getBaseFqn())) {
return false;
}
}
return failOverListeners.add(localListener);
}
/*
* (non-Javadoc)
* @see org.mobicents.cluster.MobicentsCluster#removeFailOverListener(org.mobicents.cluster.FailOverListener)
*/
public boolean removeFailOverListener(FailOverListener localListener) {
if (logger.isDebugEnabled()) {
logger.debug("Removing local listener " + localListener);
}
return failOverListeners.remove(localListener);
}
/*
* (non-Javadoc)
* @see org.mobicents.cluster.MobicentsCluster#addDataRemovalListener(org.mobicents.cluster.DataRemovalListener)
*/
public boolean addDataRemovalListener(DataRemovalListener listener) {
return dataRemovalListeners.putIfAbsent(listener.getBaseFqn(), listener) == null;
}
/*
* (non-Javadoc)
* @see org.mobicents.cluster.MobicentsCluster#removeDataRemovalListener(org.mobicents.cluster.DataRemovalListener)
*/
public boolean removeDataRemovalListener(DataRemovalListener listener) {
return dataRemovalListeners.remove(listener.getBaseFqn()) != null;
}
/*
* (non-Javadoc)
* @see org.mobicents.cluster.MobicentsCluster#getMobicentsCache()
*/
public MobicentsCache getMobicentsCache() {
return mobicentsCache;
}
/* (non-Javadoc)
* @see org.mobicents.cluster.MobicentsCluster#getClusteredCacheDataIndexingHandler()
*/
public ClusteredCacheDataIndexingHandler getClusteredCacheDataIndexingHandler() {
return clusteredCacheDataIndexingHandler;
}
@Override
public void startCluster() {
synchronized (this) {
if (started) {
throw new IllegalStateException("cluster already started");
}
mobicentsCache.startCache();
if (!mobicentsCache.getJBossCache().getCacheConfiguration().clustering().cacheMode().equals(CacheMode.LOCAL)) {
logger.info("registring listener!");
// get current cluster members
currentView = new ArrayList(mobicentsCache.getJBossCache().getCacheManager().getMembers());
// start listening to cache events
//cache.getCache().addListener(this);
// start listening to cache manager events
//cache.getCache().getCacheManager().addListener(this);
mobicentsCache.getJBossCache().getCacheManager().addListener(this);
mobicentsCache.getJBossCache().addListener(this);
}
started = true;
}
}
@Override
public boolean isStarted() {
synchronized (this) {
return started;
}
}
@Override
public void stopCluster() {
synchronized (this) {
if (!started) {
throw new IllegalStateException("cluster already started");
}
mobicentsCache.stopCache();
started = false;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy