1 /* 2 * Copyright (c) 2004, 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 }