
org.mobicents.cluster.DefaultMobicentsCluster Maven / Gradle / Ivy
/*
* 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.cluster;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
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.jboss.cache.Cache;
import org.jboss.cache.Fqn;
import org.jboss.cache.Node;
import org.jboss.cache.buddyreplication.BuddyGroup;
import org.jboss.cache.config.Configuration;
import org.jboss.cache.config.Configuration.CacheMode;
import org.jboss.cache.notifications.annotation.BuddyGroupChanged;
import org.jboss.cache.notifications.annotation.CacheListener;
import org.jboss.cache.notifications.annotation.NodeRemoved;
import org.jboss.cache.notifications.annotation.ViewChanged;
import org.jboss.cache.notifications.event.BuddyGroupChangedEvent;
import org.jboss.cache.notifications.event.NodeRemovedEvent;
import org.jboss.cache.notifications.event.ViewChangedEvent;
import org.jgroups.Address;
import org.mobicents.cache.MobicentsCache;
import org.mobicents.cluster.cache.ClusteredCacheData;
import org.mobicents.cluster.cache.ClusteredCacheDataIndexingHandler;
import org.mobicents.cluster.cache.DefaultClusteredCacheDataIndexingHandler;
import org.mobicents.cluster.election.ClientLocalListenerElector;
import org.mobicents.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
*/
@CacheListener(sync = false)
public class DefaultMobicentsCluster implements MobicentsCluster {
private static final String FQN_SEPARATOR = Fqn.SEPARATOR;
private static final String BUDDY_BACKUP_FQN_ROOT = "/_BUDDY_BACKUP_/";
private static final Logger logger = Logger.getLogger(DefaultMobicentsCluster.class);
private static final String BUDDIES_STORE = "MC_BUDDIES";
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().getLocalAddress();
}
/* (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 onViewChangeEvent(ViewChangedEvent event) {
if (logger.isDebugEnabled()) {
logger.debug("onViewChangeEvent : pre[" + event.isPre() + "] : event local address[" + event.getCache().getLocalAddress() + "]");
}
final List oldView = currentView;
currentView = new ArrayList(event.getNewView().getMembers());
final Address localAddress = getLocalAddress();
//just a precaution, it can be null!
if (oldView != null) {
final Cache jbossCache = mobicentsCache.getJBossCache();
final Configuration config = jbossCache.getConfiguration();
final boolean isBuddyReplicationEnabled = config.getBuddyReplicationConfig() != null && config.getBuddyReplicationConfig().isEnabled();
// 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 && !isBuddyReplicationEnabled) {
// going to use the local listener elector instead, which gives results based on data
performTakeOver(localListener,oldMember,localAddress, true, isBuddyReplicationEnabled);
}
else {
List electionView = getElectionView(oldMember);
if(electionView!=null && elector.elect(electionView).equals(localAddress))
{
performTakeOver(localListener, oldMember, localAddress, false, isBuddyReplicationEnabled);
}
cleanAfterTakeOver(localListener, oldMember);
}
}
}
}
}
};
Thread t = new Thread(runnable);
t.start();
}
}
@BuddyGroupChanged
public void onBuddyGroupChangedEvent(BuddyGroupChangedEvent event) {
//here we only update stored information, it happens after view change, so take over should be taken care off.
Node root = event.getCache().getRoot();
root.put(BUDDIES_STORE, event.getBuddyGroup().getBuddies());
}
/**
*
*/
@SuppressWarnings("unchecked")
private void performTakeOver(FailOverListener localListener, Address lostMember, Address localAddress, boolean useLocalListenerElector, boolean isBuddyReplicationEnabled) {
//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 + ", isBuddyReplicationEnabled=" + isBuddyReplicationEnabled);
}
//final boolean useLocalListenerElector = localListener.getElector()!=null;
final Cache jbossCache = mobicentsCache.getJBossCache();
final Fqn rootFqnOfChanges = localListener.getBaseFqn();
boolean createdTx = false;
boolean doRollback = true;
try {
if (txMgr != null && txMgr.getTransaction() == null) {
txMgr.begin();
createdTx = true;
}
if(isBuddyReplicationEnabled) {
// replace column to underscore in the couple ipaddress:port of the jgroups address
// to match the BUDDY GROUP Fqn pattern in the cache
String fqn = getBuddyBackupFqn(lostMember) + localListener.getBaseFqn();
Node buddyGroupRootNode = jbossCache.getNode(Fqn.fromString(fqn));
if (buddyGroupRootNode != null) {
Set children = buddyGroupRootNode.getChildren();
if (logger.isDebugEnabled()) {
logger.debug("Fqn : " + fqn + " : children " + children);
}
// force data gravitation for each node under the base fqn
// we want to retrieve from the buddy that died
for (Node child : children) {
Fqn childFqn = Fqn.fromRelativeElements(localListener.getBaseFqn(), child.getFqn().getLastElement());
if (logger.isDebugEnabled()) {
logger.debug("forcing data gravitation on following child fqn " + childFqn);
}
jbossCache.getInvocationContext().getOptionOverrides().setForceDataGravitation(true);
Node n = jbossCache.getNode(childFqn);
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("Fqn : " + fqn + " : doesn't return any node, this node " + localAddress
+ "might not be a buddy group of the failed node " + lostMember);
}
}
}
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