package org.radargun.stages;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.util.ArrayList;
import java.util.List;
import org.radargun.DistStageAck;
import org.radargun.config.DefinitionElement;
import org.radargun.config.Property;
import org.radargun.config.Stage;
import org.radargun.utils.ReflexiveConverters;
import org.radargun.utils.TimeConverter;
/**
* Inspired by org.jgroups.tests.Probe.
*
* @author Matej Cimbora
*/
@Stage(doc = "Allows to invoke JGroups probe queries. For details on probe usage see org.jgroups.tests.Probe.")
public class JGroupsProbeStage extends AbstractDistStage {
@Property(doc = "List of queries to be performed.", optional = false, complexConverter = ListConverter.class)
private List<Query> queries = new ArrayList<>();
@Property(doc = "Diagnostic address to send queries to. Default is 224.0.75.75.")
private String address = "224.0.75.75";
@Property(doc = "Diagnostic port. Default is 7500.")
private int port = 7500;
@Property(doc = "Maximum time to wait for query responses. Default is 60 seconds. Valid only when used in conjunction " +
"with expectedResponseCount parameter.", converter = TimeConverter.class)
private long timeout = 60_000;
@Property(doc = "Minimum number of responses to wait for. Default is -1 don't wait for responses.", optional = false)
private int expectedResponseCount = -1;
@Property(doc = "Print results of operation to log (INFO level). By default trace logging needs to be enabled.")
private boolean printResultsAsInfo;
@Override
public DistStageAck executeOnSlave() {
if (queries.isEmpty()) {
log.info("No queries have been specified");
return successfulResponse();
}
MulticastSocket socket = null;
try {
socket = new MulticastSocket();
socket.setSoTimeout((int) timeout);
InetAddress targetAddress = InetAddress.getByName(address);
byte[] payload = getQuery().getBytes();
DatagramPacket packet = new DatagramPacket(payload, 0, payload.length, targetAddress, port);
socket.send(packet);
// receive responses
if (expectedResponseCount == -1) {
return successfulResponse();
}
long start = System.currentTimeMillis();
int received = 0;
while (received < expectedResponseCount) {
if (start + timeout < System.currentTimeMillis()) {
String errorMessage = "Timed out waiting for query responses";
log.error(errorMessage);
return errorResponse(errorMessage);
}
byte[] buffer = new byte[1024 * 64];
DatagramPacket result = new DatagramPacket(buffer, buffer.length);
socket.receive(result);
received++;
if (printResultsAsInfo) {
log.infof("Received response %s from %s:%d", new String(result.getData(), 0, result.getLength()), result.getAddress(), result.getPort());
} else {
log.tracef("Received response %s from %s:%d", new String(result.getData(), 0, result.getLength()), result.getAddress(), result.getPort());
}
}
} catch (IOException e) {
String errorMessage = "Exception while performing multicast socket operation. Make sure 'enable_diagnostics' property of TP is enabled " +
"and correct diagnostics port is specified";
log.error(errorMessage, e);
return errorResponse(errorMessage, e);
} finally {
if (socket != null) {
socket.close();
}
}
return successfulResponse();
}
private String getQuery() {
if (queries.size() == 1) {
return queries.get(0).query;
}
StringBuilder queryBuilder = new StringBuilder();
for (Query query : queries) {
queryBuilder.append(query.query).append(" ");
}
return queryBuilder.toString();
}
@DefinitionElement(name = "query", doc = "Single query element.")
private static class Query {
@Property(doc = "Query to be performed.", name = "value", optional = false)
private String query;
}
protected static class ListConverter extends ReflexiveConverters.ListConverter {
public ListConverter() {
super(new Class<?>[] {Query.class});
}
}
}