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 import sun.misc.ManagedLocalsThread; 38 39 /** 40 * JdpController is responsible to create and manage a broadcast loop. 41 * 42 * <p> Other part of code has no access to broadcast loop and have to use 43 * provided static methods 44 * {@link #startDiscoveryService(InetAddress,int,String,String) startDiscoveryService} 45 * and {@link #stopDiscoveryService() stopDiscoveryService} 46 * <p>{@link #startDiscoveryService(InetAddress,int,String,String) startDiscoveryService} could be called multiple 47 * times as it stops the running service if it is necessary. 48 * Call to {@link #stopDiscoveryService() stopDiscoveryService} 49 * ignored if service isn't run. 50 * 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 * 62 * <p>null parameters values are filtered out on {@link JdpPacketWriter} level and 63 * corresponding keys are not placed to packet. 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 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 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 106 public void stop() { 107 shutdown = true; 108 } 109 } 110 private static JDPControllerRunner controller = null; 111 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 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 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 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 */ 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(Integer.toString(pause)); 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 ManagedLocalsThread(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 */ 231 public static synchronized void stopDiscoveryService() { 232 if ( controller != null ){ 233 controller.stop(); 234 controller = null; 235 } 236 } 237 }