1 /* 2 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package sun.management.jdp; 26 27 import java.io.IOException; 28 import java.net.InetAddress; 29 import java.net.UnknownHostException; 30 import java.util.UUID; 31 32 import java.lang.management.ManagementFactory; 33 import java.lang.management.RuntimeMXBean; 34 import java.lang.reflect.Field; 35 import java.lang.reflect.Method; 36 import sun.management.VMManagement; 37 38 /** 39 * JdpController is responsible to create and manage a broadcast loop 40 * 41 * <p> Other part of code has no access to broadcast loop and have to use 42 * provided static methods 43 * {@link #startDiscoveryService(InetAddress,int,String,String) startDiscoveryService} 44 * and {@link #stopDiscoveryService() stopDiscoveryService}</p> 45 * <p>{@link #startDiscoveryService(InetAddress,int,String,String) startDiscoveryService} could be called multiple 46 * times as it stops the running service if it is necessary. Call to {@link #stopDiscoveryService() stopDiscoveryService} 47 * ignored if service isn't run</p> 48 * 49 * 50 * </p> 51 * 52 * <p> System properties below could be used to control broadcast loop behavior. 53 * Property below have to be set explicitly in command line. It's not possible to 54 * set it in management.config file. Careless changes of these properties could 55 * lead to security or network issues. 56 * <ul> 57 * <li>com.sun.management.jdp.ttl - set ttl for broadcast packet</li> 58 * <li>com.sun.management.jdp.pause - set broadcast interval in seconds</li> 59 * <li>com.sun.management.jdp.source_addr - an address of interface to use for broadcast</li> 60 * </ul> 61 </p> 62 * <p>null parameters values are filtered out on {@link JdpPacketWriter} level and 63 * corresponding keys are not placed to packet.</p> 64 */ 65 public final class JdpController { 66 67 private static class JDPControllerRunner implements Runnable { 68 69 private final JdpJmxPacket packet; 70 private final JdpBroadcaster bcast; 71 private final int pause; 72 private volatile boolean shutdown = false; 73 JDPControllerRunner(JdpBroadcaster bcast, JdpJmxPacket packet, int pause)74 private JDPControllerRunner(JdpBroadcaster bcast, JdpJmxPacket packet, int pause) { 75 this.bcast = bcast; 76 this.packet = packet; 77 this.pause = pause; 78 } 79 80 @Override run()81 public void run() { 82 try { 83 while (!shutdown) { 84 bcast.sendPacket(packet); 85 try { 86 Thread.sleep(this.pause); 87 } catch (InterruptedException e) { 88 // pass 89 } 90 } 91 92 } catch (IOException e) { 93 // pass; 94 } 95 96 // It's not possible to re-use controller, 97 // nevertheless reset shutdown variable 98 try { 99 stop(); 100 bcast.shutdown(); 101 } catch (IOException ex) { 102 // pass - ignore IOException during shutdown 103 } 104 } 105 stop()106 public void stop() { 107 shutdown = true; 108 } 109 } 110 private static JDPControllerRunner controller = null; 111 JdpController()112 private JdpController(){ 113 // Don't allow to instantiate this class. 114 } 115 116 // Utility to handle optional system properties 117 // Parse an integer from string or return default if provided string is null getInteger(String val, int dflt, String msg)118 private static int getInteger(String val, int dflt, String msg) throws JdpException { 119 try { 120 return (val == null) ? dflt : Integer.parseInt(val); 121 } catch (NumberFormatException ex) { 122 throw new JdpException(msg); 123 } 124 } 125 126 // Parse an inet address from string or return default if provided string is null getInetAddress(String val, InetAddress dflt, String msg)127 private static InetAddress getInetAddress(String val, InetAddress dflt, String msg) throws JdpException { 128 try { 129 return (val == null) ? dflt : InetAddress.getByName(val); 130 } catch (UnknownHostException ex) { 131 throw new JdpException(msg); 132 } 133 } 134 135 // Get the process id of the current running Java process getProcessId()136 private static Integer getProcessId() { 137 try { 138 // Get the current process id using a reflection hack 139 RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean(); 140 Field jvm = runtime.getClass().getDeclaredField("jvm"); 141 jvm.setAccessible(true); 142 143 VMManagement mgmt = (sun.management.VMManagement) jvm.get(runtime); 144 Method pid_method = mgmt.getClass().getDeclaredMethod("getProcessId"); 145 pid_method.setAccessible(true); 146 Integer pid = (Integer) pid_method.invoke(mgmt); 147 return pid; 148 } catch(Exception ex) { 149 return null; 150 } 151 } 152 153 154 /** 155 * Starts discovery service 156 * 157 * @param address - multicast group address 158 * @param port - udp port to use 159 * @param instanceName - name of running JVM instance 160 * @param url - JMX service url 161 * @throws IOException 162 */ startDiscoveryService(InetAddress address, int port, String instanceName, String url)163 public static synchronized void startDiscoveryService(InetAddress address, int port, String instanceName, String url) 164 throws IOException, JdpException { 165 166 // Limit packet to local subnet by default 167 int ttl = getInteger( 168 System.getProperty("com.sun.management.jdp.ttl"), 1, 169 "Invalid jdp packet ttl"); 170 171 // Broadcast once a 5 seconds by default 172 int pause = getInteger( 173 System.getProperty("com.sun.management.jdp.pause"), 5, 174 "Invalid jdp pause"); 175 176 // Converting seconds to milliseconds 177 pause = pause * 1000; 178 179 // Allow OS to choose broadcast source 180 InetAddress sourceAddress = getInetAddress( 181 System.getProperty("com.sun.management.jdp.source_addr"), null, 182 "Invalid source address provided"); 183 184 // Generate session id 185 UUID id = UUID.randomUUID(); 186 187 JdpJmxPacket packet = new JdpJmxPacket(id, url); 188 189 // Don't broadcast whole command line for security reason. 190 // Strip everything after first space 191 String javaCommand = System.getProperty("sun.java.command"); 192 if (javaCommand != null) { 193 String[] arr = javaCommand.split(" ", 2); 194 packet.setMainClass(arr[0]); 195 } 196 197 // Put optional explicit java instance name to packet, if user doesn't specify 198 // it the key is skipped. PacketWriter is responsible to skip keys having null value. 199 packet.setInstanceName(instanceName); 200 201 // Set rmi server hostname if it explicitly specified by user with 202 // java.rmi.server.hostname 203 String rmiHostname = System.getProperty("java.rmi.server.hostname"); 204 packet.setRmiHostname(rmiHostname); 205 206 // Set broadcast interval 207 packet.setBroadcastInterval(new Integer(pause).toString()); 208 209 // Set process id 210 Integer pid = getProcessId(); 211 if (pid != null) { 212 packet.setProcessId(pid.toString()); 213 } 214 215 JdpBroadcaster bcast = new JdpBroadcaster(address, sourceAddress, port, ttl); 216 217 // Stop discovery service if it's already running 218 stopDiscoveryService(); 219 220 controller = new JDPControllerRunner(bcast, packet, pause); 221 222 Thread t = new Thread(controller, "JDP broadcaster"); 223 t.setDaemon(true); 224 t.start(); 225 } 226 227 /** 228 * Stop running discovery service, 229 * it's safe to attempt to stop not started service 230 */ stopDiscoveryService()231 public static synchronized void stopDiscoveryService() { 232 if ( controller != null ){ 233 controller.stop(); 234 controller = null; 235 } 236 } 237 } 238