1 /* |
|
2 * Copyright (c) 2004, 2014, 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 |
|
26 package sun.jvmstat.perfdata.monitor.protocol.rmi; |
|
27 |
|
28 import sun.jvmstat.monitor.*; |
|
29 import sun.jvmstat.monitor.event.*; |
|
30 import sun.jvmstat.monitor.remote.*; |
|
31 import sun.jvmstat.perfdata.monitor.*; |
|
32 import java.util.*; |
|
33 import java.net.*; |
|
34 import java.io.*; |
|
35 import java.rmi.*; |
|
36 import java.util.HashMap; |
|
37 |
|
38 /** |
|
39 * Concrete implementation of the MonitoredHost interface for the |
|
40 * <em>rmi</em> protocol of the HotSpot PerfData monitoring implementation. |
|
41 * |
|
42 * @author Brian Doherty |
|
43 * @since 1.5 |
|
44 */ |
|
45 public class MonitoredHostProvider extends MonitoredHost { |
|
46 private static final String serverName = "/JStatRemoteHost"; |
|
47 private static final int DEFAULT_POLLING_INTERVAL = 1000; |
|
48 |
|
49 private ArrayList<HostListener> listeners; |
|
50 private NotifierTask task; |
|
51 private HashSet<Integer> activeVms; |
|
52 private RemoteVmManager vmManager; |
|
53 private RemoteHost remoteHost; |
|
54 private Timer timer; |
|
55 |
|
56 /** |
|
57 * Create a MonitoredHostProvider instance using the given HostIdentifier. |
|
58 * |
|
59 * @param hostId the host identifier for this MonitoredHost |
|
60 * @throws MonitorException Thrown on any error encountered while |
|
61 * communicating with the remote host. |
|
62 */ |
|
63 public MonitoredHostProvider(HostIdentifier hostId) |
|
64 throws MonitorException { |
|
65 this.hostId = hostId; |
|
66 this.listeners = new ArrayList<HostListener>(); |
|
67 this.interval = DEFAULT_POLLING_INTERVAL; |
|
68 this.activeVms = new HashSet<Integer>(); |
|
69 |
|
70 String rmiName; |
|
71 String sn = serverName; |
|
72 String path = hostId.getPath(); |
|
73 |
|
74 if ((path != null) && (path.length() > 0)) { |
|
75 sn = path; |
|
76 } |
|
77 |
|
78 if (hostId.getPort() != -1) { |
|
79 rmiName = "rmi://" + hostId.getHost() + ":" + hostId.getPort() + sn; |
|
80 } else { |
|
81 rmiName = "rmi://" + hostId.getHost() + sn; |
|
82 } |
|
83 |
|
84 try { |
|
85 remoteHost = (RemoteHost)Naming.lookup(rmiName); |
|
86 |
|
87 } catch (RemoteException e) { |
|
88 /* |
|
89 * rmi registry not available |
|
90 * |
|
91 * Access control exceptions, where the rmi server refuses a |
|
92 * connection based on policy file configuration, come through |
|
93 * here on the client side. Unfortunately, the RemoteException |
|
94 * doesn't contain enough information to determine the true cause |
|
95 * of the exception. So, we have to output a rather generic message. |
|
96 */ |
|
97 String message = "RMI Registry not available at " |
|
98 + hostId.getHost(); |
|
99 |
|
100 if (hostId.getPort() == -1) { |
|
101 message = message + ":" |
|
102 + java.rmi.registry.Registry.REGISTRY_PORT; |
|
103 } else { |
|
104 message = message + ":" + hostId.getPort(); |
|
105 } |
|
106 |
|
107 if (e.getMessage() != null) { |
|
108 throw new MonitorException(message + "\n" + e.getMessage(), e); |
|
109 } else { |
|
110 throw new MonitorException(message, e); |
|
111 } |
|
112 |
|
113 } catch (NotBoundException e) { |
|
114 // no server with given name |
|
115 String message = e.getMessage(); |
|
116 if (message == null) message = rmiName; |
|
117 throw new MonitorException("RMI Server " + message |
|
118 + " not available", e); |
|
119 } catch (MalformedURLException e) { |
|
120 // this is a programming problem |
|
121 e.printStackTrace(); |
|
122 throw new IllegalArgumentException("Malformed URL: " + rmiName); |
|
123 } |
|
124 this.vmManager = new RemoteVmManager(remoteHost); |
|
125 this.timer = new Timer(true); |
|
126 } |
|
127 |
|
128 /** |
|
129 * {@inheritDoc} |
|
130 */ |
|
131 public MonitoredVm getMonitoredVm(VmIdentifier vmid) |
|
132 throws MonitorException { |
|
133 return getMonitoredVm(vmid, DEFAULT_POLLING_INTERVAL); |
|
134 } |
|
135 |
|
136 /** |
|
137 * {@inheritDoc} |
|
138 */ |
|
139 public MonitoredVm getMonitoredVm(VmIdentifier vmid, int interval) |
|
140 throws MonitorException { |
|
141 VmIdentifier nvmid = null; |
|
142 try { |
|
143 nvmid = hostId.resolve(vmid); |
|
144 RemoteVm rvm = remoteHost.attachVm(vmid.getLocalVmId(), |
|
145 vmid.getMode()); |
|
146 RemoteMonitoredVm rmvm = new RemoteMonitoredVm(rvm, nvmid, timer, |
|
147 interval); |
|
148 rmvm.attach(); |
|
149 return rmvm; |
|
150 |
|
151 } catch (RemoteException e) { |
|
152 throw new MonitorException("Remote Exception attaching to " |
|
153 + nvmid.toString(), e); |
|
154 } catch (URISyntaxException e) { |
|
155 /* |
|
156 * the VmIdentifier is expected to be a valid and should resolve |
|
157 * easonably against the host identifier. A URISyntaxException |
|
158 * here is most likely a programming error. |
|
159 */ |
|
160 throw new IllegalArgumentException("Malformed URI: " |
|
161 + vmid.toString(), e); |
|
162 } |
|
163 } |
|
164 |
|
165 /** |
|
166 * {@inheritDoc} |
|
167 */ |
|
168 public void detach(MonitoredVm vm) throws MonitorException { |
|
169 RemoteMonitoredVm rmvm = (RemoteMonitoredVm)vm; |
|
170 rmvm.detach(); |
|
171 try { |
|
172 remoteHost.detachVm(rmvm.getRemoteVm()); |
|
173 |
|
174 } catch (RemoteException e) { |
|
175 throw new MonitorException("Remote Exception detaching from " |
|
176 + vm.getVmIdentifier().toString(), e); |
|
177 } |
|
178 } |
|
179 |
|
180 /** |
|
181 * {@inheritDoc} |
|
182 */ |
|
183 public void addHostListener(HostListener listener) { |
|
184 synchronized(listeners) { |
|
185 listeners.add(listener); |
|
186 if (task == null) { |
|
187 task = new NotifierTask(); |
|
188 timer.schedule(task, 0, interval); |
|
189 } |
|
190 } |
|
191 } |
|
192 |
|
193 /** |
|
194 * {@inheritDoc} |
|
195 */ |
|
196 public void removeHostListener(HostListener listener) { |
|
197 /* |
|
198 * XXX: if a disconnect method is added, make sure it calls |
|
199 * this method to unregister this object from the watcher. otherwise, |
|
200 * an unused MonitoredHostProvider instance may go uncollected. |
|
201 */ |
|
202 synchronized(listeners) { |
|
203 listeners.remove(listener); |
|
204 if (listeners.isEmpty() && (task != null)) { |
|
205 task.cancel(); |
|
206 task = null; |
|
207 } |
|
208 } |
|
209 } |
|
210 |
|
211 public void setInterval(int newInterval) { |
|
212 synchronized(listeners) { |
|
213 if (newInterval == interval) { |
|
214 return; |
|
215 } |
|
216 |
|
217 int oldInterval = interval; |
|
218 super.setInterval(newInterval); |
|
219 |
|
220 if (task != null) { |
|
221 task.cancel(); |
|
222 NotifierTask oldTask = task; |
|
223 task = new NotifierTask(); |
|
224 CountedTimerTaskUtils.reschedule(timer, oldTask, task, |
|
225 oldInterval, newInterval); |
|
226 } |
|
227 } |
|
228 } |
|
229 |
|
230 /** |
|
231 * {@inheritDoc} |
|
232 */ |
|
233 public Set<Integer> activeVms() throws MonitorException { |
|
234 return vmManager.activeVms(); |
|
235 } |
|
236 |
|
237 /** |
|
238 * Fire VmStatusChangeEvent events to HostListener objects |
|
239 * |
|
240 * @param active Set of Integer objects containing the local |
|
241 * Vm Identifiers of the active JVMs |
|
242 * @param started Set of Integer objects containing the local |
|
243 * Vm Identifiers of new JVMs started since last |
|
244 * interval. |
|
245 * @param terminated Set of Integer objects containing the local |
|
246 * Vm Identifiers of terminated JVMs since last |
|
247 * interval. |
|
248 */ |
|
249 @SuppressWarnings("unchecked") // Cast of result of clone |
|
250 private void fireVmStatusChangedEvents(Set<Integer> active, Set<Integer> started, |
|
251 Set<Integer> terminated) { |
|
252 ArrayList<HostListener> registered = null; |
|
253 VmStatusChangeEvent ev = null; |
|
254 |
|
255 synchronized(listeners) { |
|
256 registered = (ArrayList)listeners.clone(); |
|
257 } |
|
258 |
|
259 for (Iterator<HostListener> i = registered.iterator(); i.hasNext(); /* empty */) { |
|
260 HostListener l = i.next(); |
|
261 if (ev == null) { |
|
262 ev = new VmStatusChangeEvent(this, active, started, terminated); |
|
263 } |
|
264 l.vmStatusChanged(ev); |
|
265 } |
|
266 } |
|
267 |
|
268 /** |
|
269 * Fire hostDisconnectEvent events. |
|
270 */ |
|
271 @SuppressWarnings("unchecked") // Cast of result of clone |
|
272 void fireDisconnectedEvents() { |
|
273 ArrayList<HostListener> registered = null; |
|
274 HostEvent ev = null; |
|
275 |
|
276 synchronized(listeners) { |
|
277 registered = (ArrayList)listeners.clone(); |
|
278 } |
|
279 |
|
280 for (Iterator<HostListener> i = registered.iterator(); i.hasNext(); /* empty */) { |
|
281 HostListener l = i.next(); |
|
282 if (ev == null) { |
|
283 ev = new HostEvent(this); |
|
284 } |
|
285 l.disconnected(ev); |
|
286 } |
|
287 } |
|
288 |
|
289 /** |
|
290 * class to poll the remote machine and generate local event notifications. |
|
291 */ |
|
292 private class NotifierTask extends CountedTimerTask { |
|
293 public void run() { |
|
294 super.run(); |
|
295 |
|
296 // save the last set of active JVMs |
|
297 Set<Integer> lastActiveVms = activeVms; |
|
298 |
|
299 try { |
|
300 // get the current set of active JVMs |
|
301 activeVms = (HashSet<Integer>)vmManager.activeVms(); |
|
302 |
|
303 } catch (MonitorException e) { |
|
304 // XXX: use logging api |
|
305 System.err.println("MonitoredHostProvider: polling task " |
|
306 + "caught MonitorException:"); |
|
307 e.printStackTrace(); |
|
308 |
|
309 // mark the HostManager as errored and notify listeners |
|
310 setLastException(e); |
|
311 fireDisconnectedEvents(); |
|
312 } |
|
313 |
|
314 if (activeVms.isEmpty()) { |
|
315 return; |
|
316 } |
|
317 |
|
318 Set<Integer> startedVms = new HashSet<>(); |
|
319 Set<Integer> terminatedVms = new HashSet<>(); |
|
320 |
|
321 for (Iterator<Integer> i = activeVms.iterator(); i.hasNext(); /* empty */ ) { |
|
322 Integer vmid = i.next(); |
|
323 if (!lastActiveVms.contains(vmid)) { |
|
324 // a new file has been detected, add to set |
|
325 startedVms.add(vmid); |
|
326 } |
|
327 } |
|
328 |
|
329 for (Iterator<Integer> i = lastActiveVms.iterator(); i.hasNext(); |
|
330 /* empty */ ) { |
|
331 Integer o = i.next(); |
|
332 if (!activeVms.contains(o)) { |
|
333 // JVM has terminated, remove it from the active list |
|
334 terminatedVms.add(o); |
|
335 } |
|
336 } |
|
337 |
|
338 if (!startedVms.isEmpty() || !terminatedVms.isEmpty()) { |
|
339 fireVmStatusChangedEvents(activeVms, startedVms, terminatedVms); |
|
340 } |
|
341 } |
|
342 } |
|
343 } |
|