blob: 2c11b7b4c02315e85d294143238a62a279f2c7f1 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2004 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.jvmstat.perfdata.monitor.protocol.rmi;
27
28import sun.jvmstat.monitor.*;
29import sun.jvmstat.monitor.event.*;
30import sun.jvmstat.monitor.remote.*;
31import sun.jvmstat.perfdata.monitor.*;
32import java.util.*;
33import java.net.*;
34import java.io.*;
35import java.rmi.*;
36import 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 */
45public 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 private void fireVmStatusChangedEvents(Set active, Set started,
250 Set terminated) {
251 ArrayList registered = null;
252 VmStatusChangeEvent ev = null;
253
254 synchronized(listeners) {
255 registered = (ArrayList)listeners.clone();
256 }
257
258 for (Iterator i = registered.iterator(); i.hasNext(); /* empty */) {
259 HostListener l = (HostListener)i.next();
260 if (ev == null) {
261 ev = new VmStatusChangeEvent(this, active, started, terminated);
262 }
263 l.vmStatusChanged(ev);
264 }
265 }
266
267 /**
268 * Fire hostDisconnectEvent events.
269 */
270 void fireDisconnectedEvents() {
271 ArrayList registered = null;
272 HostEvent ev = null;
273
274 synchronized(listeners) {
275 registered = (ArrayList)listeners.clone();
276 }
277
278 for (Iterator i = registered.iterator(); i.hasNext(); /* empty */) {
279 HostListener l = (HostListener)i.next();
280 if (ev == null) {
281 ev = new HostEvent(this);
282 }
283 l.disconnected(ev);
284 }
285 }
286
287 /**
288 * class to poll the remote machine and generate local event notifications.
289 */
290 private class NotifierTask extends CountedTimerTask {
291 public void run() {
292 super.run();
293
294 // save the last set of active JVMs
295 Set lastActiveVms = activeVms;
296
297 try {
298 // get the current set of active JVMs
299 activeVms = (HashSet<Integer>)vmManager.activeVms();
300
301 } catch (MonitorException e) {
302 // XXX: use logging api
303 System.err.println("MonitoredHostProvider: polling task "
304 + "caught MonitorException:");
305 e.printStackTrace();
306
307 // mark the HostManager as errored and notify listeners
308 setLastException(e);
309 fireDisconnectedEvents();
310 }
311
312 if (activeVms.isEmpty()) {
313 return;
314 }
315
316 Set<Integer> startedVms = new HashSet<Integer>();
317 Set<Object> terminatedVms = new HashSet<Object>();
318
319 for (Iterator i = activeVms.iterator(); i.hasNext(); /* empty */ ) {
320 Integer vmid = (Integer)i.next();
321 if (!lastActiveVms.contains(vmid)) {
322 // a new file has been detected, add to set
323 startedVms.add(vmid);
324 }
325 }
326
327 for (Iterator i = lastActiveVms.iterator(); i.hasNext();
328 /* empty */ ) {
329 Object o = i.next();
330 if (!activeVms.contains(o)) {
331 // JVM has terminated, remove it from the active list
332 terminatedVms.add(o);
333 }
334 }
335
336 if (!startedVms.isEmpty() || !terminatedVms.isEmpty()) {
337 fireVmStatusChangedEvents(activeVms, startedVms, terminatedVms);
338 }
339 }
340 }
341}