org.jgroups.stack.DiagnosticsHandler Maven / Gradle / Ivy
package org.jgroups.stack;
import org.jgroups.Global;
import org.jgroups.logging.Log;
import org.jgroups.util.SocketFactory;
import org.jgroups.util.ThreadFactory;
import org.jgroups.util.Util;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.*;
import java.security.MessageDigest;
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* @author Bela Ban
* @since 3.0
*/
public class DiagnosticsHandler implements Runnable {
public static final String THREAD_NAME = "DiagnosticsHandler";
protected Thread thread=null;
protected MulticastSocket diag_sock=null;
protected InetAddress diagnostics_addr=null;
protected int diagnostics_port=7500;
protected int ttl=8;
protected List bind_interfaces=null;
protected final Set handlers=new CopyOnWriteArraySet<>();
protected final Log log;
protected final SocketFactory socket_factory;
protected final ThreadFactory thread_factory;
protected final String passcode;
public DiagnosticsHandler(InetAddress diagnostics_addr, int diagnostics_port,
Log log, SocketFactory socket_factory, ThreadFactory thread_factory) {
this(diagnostics_addr,diagnostics_port,log,socket_factory,thread_factory,null);
}
public DiagnosticsHandler(InetAddress diagnostics_addr, int diagnostics_port,
Log log, SocketFactory socket_factory, ThreadFactory thread_factory, String passcode) {
this.diagnostics_addr=diagnostics_addr;
this.diagnostics_port=diagnostics_port;
this.log=log;
this.socket_factory=socket_factory;
this.thread_factory=thread_factory;
this.passcode = passcode;
}
public DiagnosticsHandler(InetAddress diagnostics_addr, int diagnostics_port,
List bind_interfaces, int diagnostics_ttl,
Log log, SocketFactory socket_factory, ThreadFactory thread_factory, String passcode) {
this(diagnostics_addr, diagnostics_port, log, socket_factory, thread_factory, passcode);
this.bind_interfaces=bind_interfaces;
this.ttl=diagnostics_ttl;
}
public Thread getThread(){
return thread;
}
public Set getProbeHandlers() {return handlers;}
public void registerProbeHandler(ProbeHandler handler) {
if(handler != null)
handlers.add(handler);
}
public void unregisterProbeHandler(ProbeHandler handler) {
if(handler != null)
handlers.remove(handler);
}
public void start() throws IOException {
// https://jira.jboss.org/jira/browse/JGRP-777 - this doesn't work on MacOS, and we don't have
// cross talking on Windows anyway, so we just do it for Linux. (How about Solaris ?)
// if(can_bind_to_mcast_addr)
// diag_sock=Util.createMulticastSocket(getSocketFactory(),
// Global.TP_DIAG_MCAST_SOCK, diagnostics_addr, diagnostics_port, log);
//else
diag_sock=socket_factory.createMulticastSocket("jgroups.tp.diag.mcast_sock", diagnostics_port);
diag_sock.setTimeToLive(ttl);
List interfaces=bind_interfaces != null? bind_interfaces : Util.getAllAvailableInterfaces();
bindToInterfaces(interfaces, diag_sock);
if(thread == null || !thread.isAlive()) {
thread=thread_factory.newThread(this, THREAD_NAME);
thread.setDaemon(true);
thread.start();
}
}
public void stop() {
Thread tmp=thread;
thread=null;
if(diag_sock != null)
socket_factory.close(diag_sock);
if(tmp != null) {
try {
tmp.join(Global.THREAD_SHUTDOWN_WAIT_TIME);
}
catch(InterruptedException e){
Thread.currentThread().interrupt(); // set interrupt flag
}
}
}
public boolean isRunning() {return thread != null && thread.isAlive() && diag_sock != null && !diag_sock.isClosed();}
public void run() {
byte[] buf;
DatagramPacket packet;
while(Thread.currentThread().equals(thread)) {
buf=new byte[10000]; // requests are small (responses might be bigger)
packet=new DatagramPacket(buf, 0, buf.length);
try {
diag_sock.receive(packet);
int payloadStartOffset = 0;
if(isAuthorizationRequired()){
payloadStartOffset = authorizeProbeRequest(packet);
}
handleDiagnosticProbe(packet.getSocketAddress(), diag_sock,
new String(packet.getData(), packet.getOffset() + payloadStartOffset, packet.getLength()));
}
catch(IOException socket_ex) {
}
catch(Throwable e) {
log.error(Util.getMessage("FailureHandlingDiagnosticsRequest"), e);
}
}
}
protected void handleDiagnosticProbe(SocketAddress sender, DatagramSocket sock, String request) {
StringTokenizer tok=new StringTokenizer(request);
List list=new ArrayList<>(10);
while(tok.hasMoreTokens()) {
String req=tok.nextToken().trim();
if(!req.isEmpty())
list.add(req);
}
String[] tokens=new String[list.size()];
for(int i=0; i < list.size(); i++)
tokens[i]=list.get(i);
for(ProbeHandler handler: handlers) {
Map map=null;
try {
map=handler.handleProbe(tokens);
}
catch(IllegalArgumentException ex) {
log.warn(ex.getMessage());
return;
}
if(map == null || map.isEmpty())
continue;
StringBuilder info=new StringBuilder();
for(Map.Entry entry: map.entrySet())
info.append(entry.getKey()).append("=").append(entry.getValue()).append("\r\n");
byte[] diag_rsp=info.toString().getBytes();
log.debug("sending diag response to %s", sender);
try {
sendResponse(sock, sender, diag_rsp);
}
catch(Throwable t) {
log.error(Util.getMessage("FailedSendingDiagRspTo") + sender, t);
}
}
}
/**
* Performs authorization on given DatagramPacket.
*
* @param packet to authorize
* @return offset in DatagramPacket where request payload starts
* @throws Exception thrown if passcode received from client does not match set passcode
*/
protected int authorizeProbeRequest(DatagramPacket packet) throws Exception {
int offset = 0;
ByteArrayInputStream bis = new ByteArrayInputStream(packet.getData());
DataInputStream in = new DataInputStream(bis);
long t1 = in.readLong();
double q1 = in.readDouble();
int length = in.readInt();
byte[] digest = new byte[length];
in.readFully(digest);
offset = 8 + 8 + 4 + digest.length;
byte[] local = Util.createDigest(passcode, t1, q1);
if (!MessageDigest.isEqual(digest, local)) {
throw new Exception("Authorization failed! Make sure correct passcode is used");
} else {
if(log.isDebugEnabled())
log.debug("Request authorized");
}
return offset;
}
protected static void sendResponse(DatagramSocket sock, SocketAddress sender, byte[] buf) throws IOException {
DatagramPacket p=new DatagramPacket(buf, 0, buf.length, sender);
sock.send(p);
}
protected void bindToInterfaces(List interfaces, MulticastSocket s) {
SocketAddress group_addr=new InetSocketAddress(diagnostics_addr, diagnostics_port);
for(Iterator it=interfaces.iterator(); it.hasNext();) {
NetworkInterface i=it.next();
try {
if (i.isUp()) {
List inet_addrs=i.getInterfaceAddresses();
if(inet_addrs != null && !inet_addrs.isEmpty()) { // fix for VM crash - suggested by [email protected]
s.joinGroup(group_addr, i);
log.trace("joined %s on %s", group_addr, i.getName());
}
}
}
catch(Exception e) { // also catches NPE in getInterfaceAddresses() (https://issues.jboss.org/browse/JGRP-1845)
log.warn("failed to join " + group_addr + " on " + i.getName() + ": " + e);
}
}
}
protected boolean isAuthorizationRequired(){
return passcode != null;
}
public interface ProbeHandler {
/**
* Handles a probe. For each key that is handled, the key and its result should be in the returned map.
* @param keys
* @return Map. A map of keys and values. A null return value is permissible.
*/
Map handleProbe(String... keys);
/** Returns a list of supported keys */
String[] supportedKeys();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy