// Copyright 2003-2005 Arthur van Hoff, Rick Blair // Licensed under Apache License version 2.0 // Original license LGPL package org.jolokia.discovery; import java.io.IOException; import java.net.*; import org.jolokia.restrictor.Restrictor; import org.jolokia.util.LogHandler; import org.jolokia.util.NetworkUtil; import static org.jolokia.discovery.AbstractDiscoveryMessage.MessageType.RESPONSE; /** * A listener runnable which should be used in thread and which reads from a multicast socket * incoming request to which it responds with this agent details. * * @since 31.01.2014 */ class MulticastSocketListenerThread extends Thread { // From where to get agent details private final AgentDetailsHolder agentDetailsHolder; // Restrictor checking for remote address private final Restrictor restrictor; private final LogHandler logHandler; // Address to listen to private final InetAddress address; // Lifecycle flag private boolean running; // Socket used for listening private MulticastSocket socket; /** * Constructor, used internally. * * @param pHostAddress host address for creating a socket to listen to * @param pAgentDetailsHolder the holder which has the agent details * @param pRestrictor restrictor to check whether an incoming package should be answered which * is done only when {@link Restrictor#isRemoteAccessAllowed(String...)} returns true for * the address from which the packet was received. * @param pLogHandler log handler used for logging */ MulticastSocketListenerThread(InetAddress pHostAddress, AgentDetailsHolder pAgentDetailsHolder, Restrictor pRestrictor, LogHandler pLogHandler) throws IOException { address = pHostAddress != null ? pHostAddress : NetworkUtil.getLocalAddressWithMulticast(); agentDetailsHolder = pAgentDetailsHolder; restrictor = pRestrictor; logHandler = pLogHandler; // For debugging, uncomment: //logHandler = new LogHandler.StdoutLogHandler(true); socket = MulticastUtil.newMulticastSocket(address,logHandler); logHandler.debug(address + "<-- Listening for queries"); setDaemon(true); } /** {@inheritDoc} */ public void run() { setRunning(true); try { while (isRunning()) { refreshSocket(); logHandler.debug(address + "<-- Waiting"); DiscoveryIncomingMessage msg = receiveMessage(); if (shouldMessageBeProcessed(msg)) { handleQuery(msg); } } } catch (IllegalStateException e) { logHandler.error(address + "<-- Cannot reopen socket, exiting listener thread: " + e.getCause(),e.getCause()); } finally { if (socket != null) { socket.close(); } logHandler.debug(address + "<-- Stop listening"); } } private synchronized void setRunning(boolean pRunning) { running = pRunning; } public synchronized boolean isRunning() { return running; } public synchronized void shutdown() { setRunning(false); interrupt(); socket.close(); } // ==================================================================================== private boolean shouldMessageBeProcessed(DiscoveryIncomingMessage pMsg) { return pMsg != null && restrictor.isRemoteAccessAllowed(pMsg.getSourceAddress().getHostAddress()) && pMsg.isQuery(); } private DiscoveryIncomingMessage receiveMessage() { byte buf[] = new byte[AbstractDiscoveryMessage.MAX_MSG_SIZE]; DatagramPacket packet = new DatagramPacket(buf, buf.length); try { packet.setLength(buf.length); socket.receive(packet); return new DiscoveryIncomingMessage(packet); } catch (IOException e) { if (!socket.isClosed()) { logHandler.info("Error while handling discovery request" + (packet.getAddress() != null ? " from " + packet.getAddress() : "") + ". Ignoring this request. --> " + e); } return null; } } private void refreshSocket() { if (socket.isClosed()) { logHandler.info(address + "<-- Socket closed, reopening it"); try { socket = MulticastUtil.newMulticastSocket(address, logHandler); } catch (IOException exp) { logHandler.error("Cannot reopen socket. Exiting multicast listener thread ...",exp); throw new SocketVerificationFailedException(exp); } } } private void handleQuery(DiscoveryIncomingMessage pMsg) { DiscoveryOutgoingMessage answer = new DiscoveryOutgoingMessage.Builder(RESPONSE) .respondTo(pMsg) .agentDetails(agentDetailsHolder.getAgentDetails()) .build(); logHandler.debug(address + "<-- Discovery request from " + pMsg.getSourceAddress() + ":" + pMsg.getSourcePort()); send(answer); } private void send(DiscoveryOutgoingMessage pAnswer) { byte[] message = pAnswer.getData(); final DatagramPacket packet = new DatagramPacket(message, message.length, pAnswer.getTargetAddress(),pAnswer.getTargetPort()); if (!socket.isClosed()) { try { socket.send(packet); } catch (IOException exp) { logHandler.info(address + "<-- Can not send discovery response to " + packet.getAddress()); } } } // Exception thrown when verification fails private static class SocketVerificationFailedException extends RuntimeException { public SocketVerificationFailedException(IOException e) { super(e); } } }