/* * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.management.jdp; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.UUID; /** * JdpController is responsible to create and manage a broadcast loop * * <p> Other part of code has no access to broadcast loop and have to use * provided static methods * {@link #startDiscoveryService(InetAddress,int,String,String) startDiscoveryService} * and {@link #stopDiscoveryService() stopDiscoveryService}</p> * <p>{@link #startDiscoveryService(InetAddress,int,String,String) startDiscoveryService} could be called multiple * times as it stops the running service if it is necessary. Call to {@link #stopDiscoveryService() stopDiscoveryService} * ignored if service isn't run</p> * * * </p> * * <p> System properties below could be used to control broadcast loop behavior. * Property below have to be set explicitly in command line. It's not possible to * set it in management.config file. Careless changes of these properties could * lead to security or network issues. * <ul> * <li>com.sun.management.jdp.ttl - set ttl for broadcast packet</li> * <li>com.sun.management.jdp.pause - set broadcast interval in seconds</li> * <li>com.sun.management.jdp.source_addr - an address of interface to use for broadcast</li> * </ul> </p> * <p>null parameters values are filtered out on {@link JdpPacketWriter} level and * corresponding keys are not placed to packet.</p> */ public final class JdpController { private static class JDPControllerRunner implements Runnable { private final JdpJmxPacket packet; private final JdpBroadcaster bcast; private final int pause; private volatile boolean shutdown = false; private JDPControllerRunner(JdpBroadcaster bcast, JdpJmxPacket packet, int pause) { this.bcast = bcast; this.packet = packet; this.pause = pause; } @Override public void run() { try { while (!shutdown) { bcast.sendPacket(packet); try { Thread.sleep(this.pause); } catch (InterruptedException e) { // pass } } } catch (IOException e) { // pass; } // It's not possible to re-use controller, // nevertheless reset shutdown variable try { stop(); bcast.shutdown(); } catch (IOException ex) { // pass - ignore IOException during shutdown } } public void stop() { shutdown = true; } } private static JDPControllerRunner controller = null; private JdpController(){ // Don't allow to instantiate this class. } // Utility to handle optional system properties // Parse an integer from string or return default if provided string is null private static int getInteger(String val, int dflt, String msg) throws JdpException { try { return (val == null) ? dflt : Integer.parseInt(val); } catch (NumberFormatException ex) { throw new JdpException(msg); } } // Parse an inet address from string or return default if provided string is null private static InetAddress getInetAddress(String val, InetAddress dflt, String msg) throws JdpException { try { return (val == null) ? dflt : InetAddress.getByName(val); } catch (UnknownHostException ex) { throw new JdpException(msg); } } /** * Starts discovery service * * @param address - multicast group address * @param port - udp port to use * @param instanceName - name of running JVM instance * @param url - JMX service url * @throws IOException */ public static synchronized void startDiscoveryService(InetAddress address, int port, String instanceName, String url) throws IOException, JdpException { // Limit packet to local subnet by default int ttl = getInteger( System.getProperty("com.sun.management.jdp.ttl"), 1, "Invalid jdp packet ttl"); // Broadcast once a 5 seconds by default int pause = getInteger( System.getProperty("com.sun.management.jdp.pause"), 5, "Invalid jdp pause"); // Converting seconds to milliseconds pause = pause * 1000; // Allow OS to choose broadcast source InetAddress sourceAddress = getInetAddress( System.getProperty("com.sun.management.jdp.source_addr"), null, "Invalid source address provided"); // Generate session id UUID id = UUID.randomUUID(); JdpJmxPacket packet = new JdpJmxPacket(id, url); // Don't broadcast whole command line for security reason. // Strip everything after first space String javaCommand = System.getProperty("sun.java.command"); if (javaCommand != null) { String[] arr = javaCommand.split(" ", 2); packet.setMainClass(arr[0]); } // Put optional explicit java instance name to packet, if user doesn't specify // it the key is skipped. PacketWriter is responsible to skip keys having null value. packet.setInstanceName(instanceName); JdpBroadcaster bcast = new JdpBroadcaster(address, sourceAddress, port, ttl); // Stop discovery service if it's already running stopDiscoveryService(); controller = new JDPControllerRunner(bcast, packet, pause); Thread t = new Thread(controller, "JDP broadcaster"); t.setDaemon(true); t.start(); } /** * Stop running discovery service, * it's safe to attempt to stop not started service */ public static synchronized void stopDiscoveryService() { if ( controller != null ){ controller.stop(); controller = null; } } }