Clover coverage report -
Coverage timestamp: Thu Jul 5 2007 20:02:32 EDT
file stats: LOC: 179   Methods: 5
NCLOC: 127   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
NextMemberBuddyLocator.java 78.9% 91.7% 100% 87.4%
coverage coverage
 1    /*
 2    * JBoss, Home of Professional Open Source
 3    *
 4    * Distributable under LGPL license.
 5    * See terms of license at gnu.org.
 6    */
 7    package org.jboss.cache.buddyreplication;
 8   
 9    import net.jcip.annotations.ThreadSafe;
 10    import org.apache.commons.logging.Log;
 11    import org.apache.commons.logging.LogFactory;
 12    import org.jboss.cache.config.BuddyReplicationConfig.BuddyLocatorConfig;
 13    import org.jgroups.Address;
 14    import org.jgroups.stack.IpAddress;
 15   
 16    import java.net.InetAddress;
 17    import java.net.NetworkInterface;
 18    import java.net.SocketException;
 19    import java.util.ArrayList;
 20    import java.util.Enumeration;
 21    import java.util.List;
 22    import java.util.Map;
 23   
 24    /**
 25    * This buddy locator uses a next-in-line algorithm to select buddies for a buddy group.
 26    * This algorithm allows for the following properties, all of which are optional.
 27    * <p/>
 28    * <ul>
 29    * <li>More than one buddy per group - the <b>numBuddies</b> property, defaulting to 1 if ommitted.</li>
 30    * <li>Ability to skip buddies on the same host when selecting buddies - the <b>ignoreColocatedBuddies</b>
 31    * property, defaulting to true if ommitted. Note that this is just a hint though, and if all nstances in
 32    * a cluster are colocated, the algorithm will be forced to pick a colocated instance even if this is property
 33    * set to true.</li>
 34    * </ul>
 35    *
 36    * @author <a href="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
 37    */
 38    @ThreadSafe
 39    public class NextMemberBuddyLocator implements BuddyLocator
 40    {
 41    private Log log = LogFactory.getLog(NextMemberBuddyLocator.class);
 42   
 43    private NextMemberBuddyLocatorConfig config = new NextMemberBuddyLocatorConfig();
 44   
 45  343 public BuddyLocatorConfig getConfig()
 46    {
 47  343 return config;
 48    }
 49   
 50  189 public void init(BuddyLocatorConfig buddyLocatorConfig)
 51    {
 52  189 if (buddyLocatorConfig instanceof NextMemberBuddyLocatorConfig)
 53    {
 54  24 this.config = (NextMemberBuddyLocatorConfig) buddyLocatorConfig;
 55    }
 56  165 else if (buddyLocatorConfig != null)
 57    {
 58  164 this.config = new NextMemberBuddyLocatorConfig(buddyLocatorConfig);
 59    }
 60    else
 61    {
 62    // We were passed null; just use a default config
 63  1 this.config = new NextMemberBuddyLocatorConfig();
 64    }
 65    }
 66   
 67  358 public List<Address> locateBuddies(Map<Address, String> buddyPoolMap, List<Address> currentMembership, Address dataOwner)
 68    {
 69  358 int numBuddiesToFind = Math.min(config.getNumBuddies(), currentMembership.size());
 70  358 List<Address> buddies = new ArrayList<Address>(numBuddiesToFind);
 71   
 72    // find where we are in the list.
 73  358 int dataOwnerSubscript = currentMembership.indexOf(dataOwner);
 74  358 int i = 0;
 75  358 boolean ignoreColocatedBuddiesForSession = config.isIgnoreColocatedBuddies();
 76   
 77   
 78  358 while (buddies.size() < numBuddiesToFind)
 79    {
 80  4809 int subscript = i + dataOwnerSubscript + 1;
 81    // make sure we loop around the list
 82  3793 if (subscript >= currentMembership.size()) subscript = subscript - currentMembership.size();
 83   
 84    // now if subscript is STILL greater than or equal to the current membership size, we've looped around
 85    // completely and still havent found any more suitable candidates. Try with colocation hint disabled.
 86  4809 if (subscript >= currentMembership.size() && ignoreColocatedBuddiesForSession)
 87    {
 88  669 ignoreColocatedBuddiesForSession = false;
 89  669 i = 0;
 90  669 if (log.isInfoEnabled())
 91    {
 92  669 log.info("Expected to look for " + numBuddiesToFind + " buddies but could only find " + buddies.size() + " suitable candidates - trying with colocated buddies as well.");
 93    }
 94   
 95  669 continue;
 96    }
 97   
 98    // now try disabling the buddy pool
 99  4140 if (subscript >= currentMembership.size() && buddyPoolMap != null)
 100    {
 101  317 buddyPoolMap = null;
 102  317 ignoreColocatedBuddiesForSession = config.isIgnoreColocatedBuddies();// reset this flag
 103  317 i = 0;
 104  317 if (log.isInfoEnabled())
 105    {
 106  317 log.info("Expected to look for " + numBuddiesToFind + " buddies but could only find " + buddies.size() + " suitable candidates - trying again, ignoring buddy pool hints.");
 107    }
 108  317 continue;
 109    }
 110   
 111    // now if subscript is STILL greater than or equal to the current membership size, we've looped around
 112    // completely and still havent found any more suitable candidates. Give up with however many we have.
 113  3823 if (subscript >= currentMembership.size())
 114    {
 115  72 if (log.isInfoEnabled())
 116    {
 117  72 log.info("Expected to look for " + numBuddiesToFind + " buddies but could only find " + buddies.size() + " suitable candidates!");
 118    }
 119  72 break;
 120    }
 121   
 122  3751 Address candidate = currentMembership.get(subscript);
 123  3751 if (
 124  3751 !candidate.equals(dataOwner) && // ignore self from selection as buddy
 125    !buddies.contains(candidate) && // havent already considered this candidate
 126    (!ignoreColocatedBuddiesForSession || !isColocated(candidate, dataOwner)) && // ignore colocated buddies
 127    (isInSameBuddyPool(buddyPoolMap, candidate, dataOwner))// try and find buddies in the same buddy pool first
 128    )
 129    {
 130  342 buddies.add(candidate);
 131    }
 132  3751 i++;
 133    }
 134   
 135  0 if (log.isTraceEnabled()) log.trace("Selected buddy group as " + buddies);
 136  358 return buddies;
 137    }
 138   
 139  1093 private boolean isInSameBuddyPool(Map<Address, String> buddyPoolMap, Address candidate, Address dataOwner)
 140    {
 141  314 if (buddyPoolMap == null) return true;
 142  779 Object ownerPoolName = buddyPoolMap.get(dataOwner);
 143  779 Object candidatePoolName = buddyPoolMap.get(candidate);
 144  779 return !(ownerPoolName == null || candidatePoolName == null) && ownerPoolName.equals(candidatePoolName);
 145    }
 146   
 147  1604 private boolean isColocated(Address candidate, Address dataOwner)
 148    {
 149    // assume they're both IpAddresses??
 150  1604 InetAddress inetC = ((IpAddress) candidate).getIpAddress();
 151  1604 InetAddress inetD = ((IpAddress) dataOwner).getIpAddress();
 152   
 153  1573 if (inetC.equals(inetD)) return true;
 154   
 155    // now check other interfaces.
 156  31 try
 157    {
 158  31 for (Enumeration<NetworkInterface> nics = NetworkInterface.getNetworkInterfaces(); nics.hasMoreElements();)
 159    {
 160  46 NetworkInterface i = nics.nextElement();
 161  46 for (Enumeration<InetAddress> addrs = i.getInetAddresses(); addrs.hasMoreElements();)
 162    {
 163  69 InetAddress addr = addrs.nextElement();
 164  16 if (addr.equals(inetC)) return true;
 165    }
 166    }
 167    }
 168    catch (SocketException e)
 169    {
 170  0 if (log.isDebugEnabled()) log.debug("Unable to read NICs on host", e);
 171  0 if (log.isWarnEnabled())
 172    {
 173  0 log.warn("UNable to read all network interfaces on host " + inetD + " to determine colocation of " + inetC + ". Assuming " + inetC + " is NOT colocated with " + inetD);
 174    }
 175    }
 176   
 177  15 return false;
 178    }
 179    }