package org.radargun.jmx;
import java.net.InetSocketAddress;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import org.radargun.logging.Log;
import org.radargun.logging.LogFactory;
import org.radargun.utils.TimeService;
/**
*
* JMXClusterValidator for Infinispan
*
* @author Michal Linhard <mlinhard@redhat.com>
*
*/
public class InfinispanJMXClusterValidator implements JMXClusterValidator {
private static final Log log = LogFactory.getLog(InfinispanJMXClusterValidator.class);
private static final String ATTR_VIEW = "View";
private static final String ATTR_NUM_MEMBERS = "NumMembers";
private static final String ATTR_STM_JOIN_COMPLETE = "JoinComplete";
private static final String ATTR_STM_STATE_TRANSFER_IN_PROGRESS = "StateTransferInProgress";
public static class ClusterStatus {
public int numAvailableNodes;
public int numMembers;
public Set<String> jgroupsViews;
public Set<String> incompleteJoinNodes;
public Set<String> incompleteStateTransferNodes;
public ClusterStatus(int numAvailableNodes, int numMembers, Set<String> partitions,
Set<String> incompleteJoinNodes, Set<String> incompleteStateTransferNodes) {
super();
this.numAvailableNodes = numAvailableNodes;
this.numMembers = numMembers;
this.jgroupsViews = partitions;
this.incompleteJoinNodes = incompleteJoinNodes;
this.incompleteStateTransferNodes = incompleteStateTransferNodes;
}
}
private class InfinispanJMXPoller extends JMXPoller {
public InfinispanJMXPoller(List<InetSocketAddress> jmxEndpoints, long queryTimeout, String serviceUrlTemplate) {
super(jmxEndpoints, queryTimeout, serviceUrlTemplate);
}
public InfinispanJMXPoller(List<InetSocketAddress> jmxEndpoints, long queryTimeout) {
super(jmxEndpoints, queryTimeout);
}
public ClusterStatus checkStatus() {
List<Result> pollResults = poll();
Set<String> partitions = new HashSet<String>();
Set<String> incompleteJoinNodes = new HashSet<String>();
Set<String> incompleteStateTransferNodes = new HashSet<String>();
int numAvailableNodes = 0;
int numMembers = -1;
boolean numMembersEqual = true;
log.trace("Number of results of polling: " + pollResults.size());
for (int j = 0; j < pollResults.size(); j++) {
Result r = pollResults.get(j);
Object[] tuple = (Object[]) r.value;
if (tuple != null) {
partitions.add((String) tuple[0]);
if (!((Boolean) tuple[2])) {
incompleteJoinNodes.add(getEndpoints().get(j).toString());
}
if ((Boolean) tuple[3]) {
incompleteStateTransferNodes.add(getEndpoints().get(j).toString());
}
numAvailableNodes++;
if (numMembers == -1) {
numMembers = (Integer) tuple[1];
} else {
if (numMembers != (Integer) tuple[1]) {
numMembersEqual = false;
}
}
} else {
if (log.isTraceEnabled()) {
if (r.connectError != null) {
log.trace("Connection error", r.connectError);
}
if (r.pollError != null) {
log.trace("Polling error", r.pollError);
}
}
}
}
return new ClusterStatus(numAvailableNodes, (numMembersEqual ? numMembers : -1), partitions,
incompleteJoinNodes, incompleteStateTransferNodes);
}
protected Object[] pollNode(MBeanServerConnection connection, String node, int nodeIdx) throws Exception {
String view = (String) connection.getAttribute(new ObjectName(channelObjectName), ATTR_VIEW);
Integer numMembers = (Integer) connection
.getAttribute(new ObjectName(gmsProtocolObjectName), ATTR_NUM_MEMBERS);
Boolean joinComplete = (Boolean) connection.getAttribute(new ObjectName(stateTransferMgrObjectName),
ATTR_STM_JOIN_COMPLETE);
Boolean stInProgres = (Boolean) connection.getAttribute(new ObjectName(stateTransferMgrObjectName),
ATTR_STM_STATE_TRANSFER_IN_PROGRESS);
return new Object[] {view, numMembers, joinComplete, stInProgres};
}
}
private String channelObjectName;
private String gmsProtocolObjectName;
private String stateTransferMgrObjectName;
private List<InetSocketAddress> endpoints;
private long jmxConnectionTimeout;
@Override
public void init(List<InetSocketAddress> slaveAddresses, long jmxConnectionTimeout, String prop1, String prop2,
String prop3) {
this.endpoints = slaveAddresses;
this.jmxConnectionTimeout = jmxConnectionTimeout;
channelObjectName = prop1;
gmsProtocolObjectName = prop2;
stateTransferMgrObjectName = prop3;
}
@Override
public boolean waitUntilClusterFormed(long timeout) {
if (timeout < 0) {
log.info("Skipping waiting for cluster");
return true;
}
InfinispanJMXPoller poller = null;
try {
poller = new InfinispanJMXPoller(endpoints, jmxConnectionTimeout);
long giveUpTime = TimeService.currentTimeMillis() + timeout;
ClusterStatus status = poller.checkStatus();
boolean formed = false;
while (!(formed = isClusterFormed(status, endpoints.size())) && TimeService.currentTimeMillis() < giveUpTime) {
if (log.isDebugEnabled()) {
log.debug("Cluster incomplete: " + getDebugStatus(status));
}
Thread.sleep(1000);
status = poller.checkStatus();
}
if (formed) {
log.info("Cluster formed: " + status.jgroupsViews.iterator().next());
} else {
log.error("Cluster failed to form, last status: " + getDebugStatus(status));
}
return formed;
} catch (Exception e) {
log.error("Error while waiting for cluster formation", e);
return false;
} finally {
if (poller != null) {
poller.closeConnections();
}
}
}
private boolean isClusterFormed(ClusterStatus status, int expectedClusterSize) {
return status.numAvailableNodes == expectedClusterSize && status.numMembers == expectedClusterSize
&& status.jgroupsViews.size() == 1 && status.incompleteJoinNodes.isEmpty()
&& status.incompleteStateTransferNodes.isEmpty();
}
private String getDebugStatus(ClusterStatus status) {
StringBuffer s = new StringBuffer("Available nodes: ");
s.append(status.numAvailableNodes);
s.append("\nCurrent views:\n\n");
for (String view : status.jgroupsViews) {
s.append(view);
s.append("\n");
}
s.append("\nNodes with incomplete Join:\n\n");
s.append(status.incompleteJoinNodes);
s.append("\nNodes with incomplete State transfer:\n\n");
s.append(status.incompleteStateTransferNodes);
s.append("\n");
return s.toString();
}
}