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} |
|
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. |
|
47 * Call to {@link #stopDiscoveryService() stopDiscoveryService} |
|
48 * ignored if service isn't run. |
|
49 * |
|
50 * |
|
51 * <p> System properties below could be used to control broadcast loop behavior. |
|
52 * Property below have to be set explicitly in command line. It's not possible to |
|
53 * set it in management.config file. Careless changes of these properties could |
|
54 * lead to security or network issues. |
|
55 * <ul> |
|
56 * <li>com.sun.management.jdp.ttl - set ttl for broadcast packet</li> |
|
57 * <li>com.sun.management.jdp.pause - set broadcast interval in seconds</li> |
|
58 * <li>com.sun.management.jdp.source_addr - an address of interface to use for broadcast</li> |
|
59 * </ul> |
|
60 * |
|
61 * <p>null parameters values are filtered out on {@link JdpPacketWriter} level and |
|
62 * corresponding keys are not placed to packet. |
|
63 */ |
|
64 public final class JdpController { |
|
65 |
|
66 private static class JDPControllerRunner implements Runnable { |
|
67 |
|
68 private final JdpJmxPacket packet; |
|
69 private final JdpBroadcaster bcast; |
|
70 private final int pause; |
|
71 private volatile boolean shutdown = false; |
|
72 |
|
73 private JDPControllerRunner(JdpBroadcaster bcast, JdpJmxPacket packet, int pause) { |
|
74 this.bcast = bcast; |
|
75 this.packet = packet; |
|
76 this.pause = pause; |
|
77 } |
|
78 |
|
79 @Override |
|
80 public void run() { |
|
81 try { |
|
82 while (!shutdown) { |
|
83 bcast.sendPacket(packet); |
|
84 try { |
|
85 Thread.sleep(this.pause); |
|
86 } catch (InterruptedException e) { |
|
87 // pass |
|
88 } |
|
89 } |
|
90 |
|
91 } catch (IOException e) { |
|
92 // pass; |
|
93 } |
|
94 |
|
95 // It's not possible to re-use controller, |
|
96 // nevertheless reset shutdown variable |
|
97 try { |
|
98 stop(); |
|
99 bcast.shutdown(); |
|
100 } catch (IOException ex) { |
|
101 // pass - ignore IOException during shutdown |
|
102 } |
|
103 } |
|
104 |
|
105 public void stop() { |
|
106 shutdown = true; |
|
107 } |
|
108 } |
|
109 private static JDPControllerRunner controller = null; |
|
110 |
|
111 private JdpController(){ |
|
112 // Don't allow to instantiate this class. |
|
113 } |
|
114 |
|
115 // Utility to handle optional system properties |
|
116 // Parse an integer from string or return default if provided string is null |
|
117 private static int getInteger(String val, int dflt, String msg) throws JdpException { |
|
118 try { |
|
119 return (val == null) ? dflt : Integer.parseInt(val); |
|
120 } catch (NumberFormatException ex) { |
|
121 throw new JdpException(msg); |
|
122 } |
|
123 } |
|
124 |
|
125 // Parse an inet address from string or return default if provided string is null |
|
126 private static InetAddress getInetAddress(String val, InetAddress dflt, String msg) throws JdpException { |
|
127 try { |
|
128 return (val == null) ? dflt : InetAddress.getByName(val); |
|
129 } catch (UnknownHostException ex) { |
|
130 throw new JdpException(msg); |
|
131 } |
|
132 } |
|
133 |
|
134 // Get the process id of the current running Java process |
|
135 private static Integer getProcessId() { |
|
136 try { |
|
137 // Get the current process id using a reflection hack |
|
138 RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean(); |
|
139 Field jvm = runtime.getClass().getDeclaredField("jvm"); |
|
140 jvm.setAccessible(true); |
|
141 |
|
142 VMManagement mgmt = (sun.management.VMManagement) jvm.get(runtime); |
|
143 Method pid_method = mgmt.getClass().getDeclaredMethod("getProcessId"); |
|
144 pid_method.setAccessible(true); |
|
145 Integer pid = (Integer) pid_method.invoke(mgmt); |
|
146 return pid; |
|
147 } catch(Exception ex) { |
|
148 return null; |
|
149 } |
|
150 } |
|
151 |
|
152 |
|
153 /** |
|
154 * Starts discovery service |
|
155 * |
|
156 * @param address - multicast group address |
|
157 * @param port - udp port to use |
|
158 * @param instanceName - name of running JVM instance |
|
159 * @param url - JMX service url |
|
160 * @throws IOException |
|
161 */ |
|
162 public static synchronized void startDiscoveryService(InetAddress address, int port, String instanceName, String url) |
|
163 throws IOException, JdpException { |
|
164 |
|
165 // Limit packet to local subnet by default |
|
166 int ttl = getInteger( |
|
167 System.getProperty("com.sun.management.jdp.ttl"), 1, |
|
168 "Invalid jdp packet ttl"); |
|
169 |
|
170 // Broadcast once a 5 seconds by default |
|
171 int pause = getInteger( |
|
172 System.getProperty("com.sun.management.jdp.pause"), 5, |
|
173 "Invalid jdp pause"); |
|
174 |
|
175 // Converting seconds to milliseconds |
|
176 pause = pause * 1000; |
|
177 |
|
178 // Allow OS to choose broadcast source |
|
179 InetAddress sourceAddress = getInetAddress( |
|
180 System.getProperty("com.sun.management.jdp.source_addr"), null, |
|
181 "Invalid source address provided"); |
|
182 |
|
183 // Generate session id |
|
184 UUID id = UUID.randomUUID(); |
|
185 |
|
186 JdpJmxPacket packet = new JdpJmxPacket(id, url); |
|
187 |
|
188 // Don't broadcast whole command line for security reason. |
|
189 // Strip everything after first space |
|
190 String javaCommand = System.getProperty("sun.java.command"); |
|
191 if (javaCommand != null) { |
|
192 String[] arr = javaCommand.split(" ", 2); |
|
193 packet.setMainClass(arr[0]); |
|
194 } |
|
195 |
|
196 // Put optional explicit java instance name to packet, if user doesn't specify |
|
197 // it the key is skipped. PacketWriter is responsible to skip keys having null value. |
|
198 packet.setInstanceName(instanceName); |
|
199 |
|
200 // Set rmi server hostname if it explicitly specified by user with |
|
201 // java.rmi.server.hostname |
|
202 String rmiHostname = System.getProperty("java.rmi.server.hostname"); |
|
203 packet.setRmiHostname(rmiHostname); |
|
204 |
|
205 // Set broadcast interval |
|
206 packet.setBroadcastInterval(Integer.toString(pause)); |
|
207 |
|
208 // Set process id |
|
209 Integer pid = getProcessId(); |
|
210 if (pid != null) { |
|
211 packet.setProcessId(pid.toString()); |
|
212 } |
|
213 |
|
214 JdpBroadcaster bcast = new JdpBroadcaster(address, sourceAddress, port, ttl); |
|
215 |
|
216 // Stop discovery service if it's already running |
|
217 stopDiscoveryService(); |
|
218 |
|
219 controller = new JDPControllerRunner(bcast, packet, pause); |
|
220 |
|
221 Thread t = new Thread(null, controller, "JDP broadcaster", 0, false); |
|
222 t.setDaemon(true); |
|
223 t.start(); |
|
224 } |
|
225 |
|
226 /** |
|
227 * Stop running discovery service, |
|
228 * it's safe to attempt to stop not started service |
|
229 */ |
|
230 public static synchronized void stopDiscoveryService() { |
|
231 if ( controller != null ){ |
|
232 controller.stop(); |
|
233 controller = null; |
|
234 } |
|
235 } |
|
236 } |
|