org.jgroups.tests.Probe Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
package org.jgroups.tests;
import org.jgroups.Global;
import org.jgroups.util.ByteArray;
import org.jgroups.util.DefaultThreadFactory;
import org.jgroups.util.StackType;
import org.jgroups.util.Util;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.*;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
/**
* Discovers all UDP-based members running on a certain mcast address
* @author Bela Ban
*/
public class Probe {
protected final Set senders=new HashSet<>();
protected boolean weed_out_duplicates;
protected String match;
protected static final int DEFAULT_DIAG_PORT=7500;
protected static final String MEMBER_ADDRS="member-addrs";
protected ExecutorService thread_pool;
protected final List requesters=new ArrayList<>();
protected final AtomicInteger matched=new AtomicInteger(), not_matched=new AtomicInteger(), count=new AtomicInteger();
protected boolean verbose;
protected static final String PREFIX="**";
public boolean verbose() {return verbose;}
public Probe verbose(boolean b) {verbose=b; return this;}
public void start(List addrs, InetAddress bind_addr, int port, int ttl,
final long timeout, String request, String match,
boolean weed_out_duplicates, String passcode, boolean udp, boolean tcp) throws Exception {
this.weed_out_duplicates=weed_out_duplicates;
this.match=match;
thread_pool=Executors.newCachedThreadPool(new DefaultThreadFactory("probe", true, true));
if(verbose)
System.out.printf("%s addrs: %s\n%s udp: %b, tcp: %b\n\n", PREFIX, addrs, PREFIX, udp, tcp);
for(InetAddress addr: addrs) {
boolean unicast_dest=addr != null && !addr.isMulticastAddress();
if(unicast_dest)
fetchAddressesAndInvoke(new InetSocketAddress(addr, port), bind_addr, request, passcode, timeout, ttl, udp, tcp);
else { // multicast - requires a UdpRequester
Requester req=new UdpRequester(new InetSocketAddress(addr, port), request, passcode, null)
.start(bind_addr, timeout, ttl);
requesters.add(req);
thread_pool.execute(req);
}
}
Util.sleep(timeout);
requesters.forEach(Requester::stop);
thread_pool.shutdown();
thread_pool.awaitTermination(timeout, TimeUnit.MILLISECONDS);
System.out.printf("%d responses (%d matches, %d non matches)\n", count.get(), matched.get(), not_matched.get());
}
protected void fetchAddressesAndInvoke(SocketAddress dest, InetAddress bind_addr, String request, String passcode,
long timeout, int ttl, boolean udp, boolean tcp) throws IOException {
Consumer on_rsp_handler=buf -> {
String response=new String(buf.getArray(), 0, buf.getLength());
try {
Collection targets=parseAddresses(response, ((InetSocketAddress)dest).getPort());
if(targets == null || targets.isEmpty())
return;
for(SocketAddress target: targets) {
Requester req;
if(udp) {
if(verbose)
System.out.printf("%s sending UDP request to %s\n", PREFIX, target);
req=new UdpRequester(target, request, passcode, null).start(bind_addr, timeout, ttl);
req.run();
}
if(tcp) {
if(verbose)
System.out.printf("%s sending TCP request to %s\n", PREFIX, target);
req=new TcpRequester(target, request, passcode, null).start(bind_addr, timeout, ttl);
req.run();
}
}
}
catch(Throwable t) {
}
};
if(udp) {
Requester r=new UdpRequester(dest, MEMBER_ADDRS, passcode, on_rsp_handler)
.start(bind_addr, timeout, ttl);
requesters.add(r);
thread_pool.execute(r);
}
if(tcp) {
Requester r=new TcpRequester(dest, MEMBER_ADDRS, passcode, on_rsp_handler)
.start(bind_addr, timeout, ttl);
requesters.add(r);
thread_pool.execute(r);
}
}
/**
* Returns a list of addr:port responses. Counting the occurrences of a given address, e.g. 3 assumes we have 3
* processes on the same host, yielding requests to host:port, host:port+1 and host_port+3. This is a quick-n-dirty
* scheme and it will fail when processes don't occupy adjacent ports.
*/
protected static Collection parseAddresses(String input, int port) throws Exception {
final String ADDRS=MEMBER_ADDRS + "=";
Map map=new ConcurrentHashMap<>();
Collection retval=new ArrayList<>();
int start_index=-1, end_index=-1;
if(input != null && (start_index=input.indexOf(ADDRS)) >= 0) {
input=input.substring(start_index + ADDRS.length()).trim();
end_index=input.indexOf('\n');
if(end_index > 0)
input=input.substring(0, end_index);
List rsps=Util.parseStringList(input,",");
for(String tmp: rsps) {
int index2=tmp.lastIndexOf(':');
if(index2 != -1)
tmp=tmp.substring(0, index2);
InetAddress key=InetAddress.getByName(tmp);
Integer val=map.putIfAbsent(key, 1);
if(val != null)
map.put(key, val+1);
}
}
for(Map.Entry entry: map.entrySet()) {
InetAddress key=entry.getKey();
int val=entry.getValue();
for(int j=0; j < val; j++)
retval.add(new InetSocketAddress(key, port+j));
}
return retval;
}
private boolean checkDuplicateResponse(String response) {
int index=response.indexOf("local_addr");
if(index != -1) {
String addr=parseAddress(response.substring(index+1 + "local_addr".length()));
return senders.add(addr) == false;
}
return false;
}
private static String parseAddress(String response) {
StringTokenizer st=new StringTokenizer(response);
return st.nextToken();
}
private static boolean matches(String response, String match) {
if(response == null)
return false;
if(match == null)
return true;
int index=response.indexOf(match);
return index > -1;
}
public static void main(String[] args) throws Exception {
InetAddress bind_addr=null;
StackType ip_version=Util.getIpStackType();
List addrs=new ArrayList<>();
int port=0;
int ttl=32;
long timeout=500;
StringBuilder request=new StringBuilder();
String match=null;
boolean weed_out_duplicates=false, udp=true, tcp=false, verbose=false;
String passcode=null;
for(int i=0; i < args.length; i++) {
if("-addr".equals(args[i])) {
InetAddress addr=Util.getByName(args[++i], ip_version);
if(addr instanceof Inet6Address)
ip_version=StackType.IPv6;
addrs.add(addr);
args[i]=args[i-1]=null;
continue;
}
if("-bind_addr".equals(args[i])) {
bind_addr=Util.getByName(args[++i], ip_version);
if(bind_addr instanceof Inet6Address)
ip_version=StackType.IPv6;
args[i]=args[i-1]=null;
continue;
}
if("-4".equals(args[i])) {
ip_version=StackType.IPv4;
args[i]=null;
continue;
}
if("-6".equals(args[i])) {
ip_version=StackType.IPv6;
args[i]=null;
continue;
}
if("-v".equals(args[i])) {
verbose=true;
args[i]=null;
}
}
try {
for(int i=0; i < args.length; i++) {
if(args[i] == null)
continue;
if("-port".equals(args[i])) {
port=Integer.parseInt(args[++i]);
continue;
}
if("-ttl".equals(args[i])) {
ttl=Integer.parseInt(args[++i]);
continue;
}
if("-timeout".equals(args[i])) {
timeout=Long.parseLong(args[++i]);
continue;
}
if("-match".equals(args[i])) {
match=args[++i];
continue;
}
if("-weed_out_duplicates".equals(args[i])) {
weed_out_duplicates=true;
continue;
}
if("-passcode".equals(args[i])) {
passcode=args[++i];
continue;
}
if("-cluster".equals(args[i])) {
String cluster=args[++i];
request.append("cluster=" + cluster + " ");
continue;
}
if("-udp".equals(args[i])) {
udp=Boolean.parseBoolean(args[++i]);
continue;
}
if("-tcp".equals(args[i])) {
tcp=Boolean.parseBoolean(args[++i]);
continue;
}
if("-help".equals(args[i]) || "-h".equals(args[i]) || "--help".equals(args[i])) {
help();
return;
}
request.append(args[i] + " ");
}
if(!udp && !tcp)
throw new IllegalArgumentException("either UDP or TCP mode has to be enabled");
if(tcp) udp=false;
if(ip_version == StackType.IPv6 && bind_addr == null)
bind_addr=Util.getLoopback(ip_version);
Probe p=new Probe().verbose(verbose);
if(addrs.isEmpty()) {
if(udp) {
InetAddress mcast_addr=InetAddress.getByName(ip_version == StackType.IPv6?
Global.DEFAULT_DIAG_ADDR_IPv6 : Global.DEFAULT_DIAG_ADDR);
if(!addrs.contains(mcast_addr))
addrs.add(mcast_addr);
}
if(tcp) {
InetAddress local=Util.getNonLoopbackAddress();
if(local != null && !addrs.contains(local))
addrs.add(local);
}
}
if(port == 0)
port=DEFAULT_DIAG_PORT;
p.start(addrs, bind_addr, port, ttl, timeout, request.toString(), match, weed_out_duplicates, passcode, udp, tcp);
}
catch(Throwable t) {
t.printStackTrace();
}
}
protected static void help() {
System.out.println("Probe [-help] [-addr ] [-4] [-6] [-bind_addr ] " +
"[-port ] [-ttl ] [-timeout ] [-passcode ] [-weed_out_duplicates] " +
"[-cluster regexp-pattern] [-match pattern] [-udp true|false] [-tcp true|false] " +
"[-v] [key[=value]]*\n\n" +
"Examples:\n" +
"probe.sh keys // dumps all valid commands\n" +
"probe.sh jmx=NAKACK // dumps JMX info about all NAKACK protocols\n" +
"probe.sh op=STABLE.runMessageGarbageCollection // invokes the method in all STABLE protocols\n" +
"probe.sh jmx=UDP.oob,thread_pool // dumps all attrs of UDP starting with oob* or thread_pool*\n" +
"probe.sh jmx=FLUSH.bypass=true\n");
}
protected abstract class Requester implements Runnable {
protected final SocketAddress dest;
protected final String request, passcode;
protected Consumer on_rsp;
protected final Consumer ON_RSP=buf -> {
if(buf == null) {
System.out.println("\n");
return;
}
String response=new String(buf.getArray(), 0, buf.getLength());
if(weed_out_duplicates && checkDuplicateResponse(response))
return;
count.incrementAndGet();
if(matches(response, Probe.this.match)) {
matched.incrementAndGet();
System.out.printf("#%d (%d bytes):\n%s\n", count.get(), buf.getLength(), response);
}
else
not_matched.incrementAndGet();
};
protected Requester(SocketAddress dest, String request, String passcode, Consumer on_rsp) {
this.dest=dest;
this.request=request;
this.passcode=passcode;
this.on_rsp=on_rsp != null? on_rsp : ON_RSP;
}
protected abstract T start(InetAddress bind_addr, long timeout, int ttl) throws IOException;
protected abstract T stop();
protected abstract boolean isRunning();
protected abstract T sendRequest(byte[] request) throws IOException;
protected abstract ByteArray fetchResponse();
protected T setResponseHandler(Consumer rh) {this.on_rsp=rh; return (T)this;}
public void run() {
try {
byte[] req=createRequest();
sendRequest(req);
// System.out.println("\n");
while(isRunning()) {
ByteArray data=fetchResponse();
if(data == null)
break;
if(on_rsp != null)
on_rsp.accept(data);
}
}
catch(Throwable t) {
System.err.printf("failed sending request to %s: %s\n", dest, t);
}
}
protected byte[] createRequest() throws IOException, NoSuchAlgorithmException {
byte[] authenticationDigest=null;
if(passcode != null) {
long t1=(new Date()).getTime();
double q1=Math.random();
authenticationDigest=Util.createAuthenticationDigest(passcode, t1, q1);
}
byte[] queryPayload=request.getBytes();
byte[] payload=queryPayload;
if(authenticationDigest != null) {
payload=new byte[authenticationDigest.length + queryPayload.length];
System.arraycopy(authenticationDigest, 0, payload, 0, authenticationDigest.length);
System.arraycopy(queryPayload, 0, payload, authenticationDigest.length, queryPayload.length);
}
return payload;
}
}
protected class UdpRequester extends Requester {
protected MulticastSocket sock;
protected final byte[] buf=new byte[70000];
protected UdpRequester(SocketAddress dest, String request, String passcode, Consumer on_rsp) {
super(dest, request, passcode, on_rsp);
}
protected T start(InetAddress bind_addr, long timeout, int ttl) throws IOException {
sock=new MulticastSocket();
sock.setSoTimeout((int)timeout);
sock.setTimeToLive(ttl);
if(bind_addr != null)
sock.setNetworkInterface(NetworkInterface.getByInetAddress(bind_addr));
return (T)this;
}
protected T stop() {
Util.close(sock);
return (T)this;
}
protected boolean isRunning() {
return sock != null && !sock.isClosed();
}
protected T sendRequest(byte[] request) throws IOException {
DatagramPacket probe=new DatagramPacket(request, 0, request.length, dest);
sock.send(probe);
return (T)this;
}
protected ByteArray fetchResponse() {
DatagramPacket rsp=new DatagramPacket(buf, 0, buf.length);
try {
sock.receive(rsp);
return new ByteArray(rsp.getData(), 0, rsp.getLength());
}
catch(Throwable t) {
return null;
}
}
}
protected class TcpRequester extends Requester {
protected Socket sock;
protected InputStream in;
protected OutputStream out;
protected TcpRequester(SocketAddress dest, String request, String passcode,
Consumer on_rsp) {
super(dest, request, passcode, on_rsp);
}
protected T start(InetAddress bind_addr, long timeout, int ttl) throws IOException {
sock=new Socket();
sock.setSoTimeout((int)timeout);
sock.bind(new InetSocketAddress(bind_addr, 0));
sock.connect(dest);
in=sock.getInputStream();
out=sock.getOutputStream();
return (T)this;
}
protected T stop() {
Util.close(sock,in,out);
return (T)this;
}
protected boolean isRunning() {
return sock != null && !sock.isClosed();
}
protected T sendRequest(byte[] request) throws IOException {
out.write(request);
out.write('\n');
// out.flush();
return (T)this;
}
protected ByteArray fetchResponse() {
byte[] buf=new byte[1024];
int index=0;
for(;;) {
try {
int bytes_read=in.read(buf, index, buf.length - index);
if(bytes_read == -1) {
if(index > 0)
break;
return null;
}
index+=bytes_read;
if(index >= buf.length) {
byte[] tmp=new byte[buf.length + 1024];
System.arraycopy(buf, 0, tmp, 0, index);
buf=tmp;
}
}
catch(IOException e) {
break;
}
}
return new ByteArray(buf, 0, index);
}
}
}