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.lang.reflect.*; 33 import java.util.*; 34 import java.io.*; 35 import java.nio.ByteBuffer; 36 import java.rmi.*; 37 38 /** 39 * Concrete implementation of the AbstractMonitoredVm class for the 40 * <em>rmi:</em> protocol for the HotSpot PerfData monitoring implementation. 41 * <p> 42 * This class provides the ability to acquire to the instrumentation buffer 43 * of a live, remote target Java Virtual Machine through an RMI server. 44 * 45 * @author Brian Doherty 46 * @since 1.5 47 */ 48 public class RemoteMonitoredVm extends AbstractMonitoredVm { 49 50 private ArrayList<VmListener> listeners; 51 private NotifierTask notifierTask; 52 private SamplerTask samplerTask; 53 private Timer timer; 54 55 private RemoteVm rvm; 56 private ByteBuffer updateBuffer; 57 58 /** 59 * Create a RemoteMonitoredVm instance. 60 * 61 * @param rvm the proxy to the remote MonitoredVm instance. 62 * @param vmid the vm identifier specifying the remot target JVM 63 * @param timer the timer used to run polling tasks 64 * @param interval the sampling interval 65 */ 66 public RemoteMonitoredVm(RemoteVm rvm, VmIdentifier vmid, 67 Timer timer, int interval) 68 throws MonitorException { 69 super(vmid, interval); 70 this.rvm = rvm; 71 pdb = new PerfDataBuffer(rvm, vmid.getLocalVmId()); 72 this.listeners = new ArrayList<VmListener>(); 73 this.timer = timer; 74 } 75 76 /** 77 * Method to attach to the remote MonitoredVm. 78 */ 79 public void attach() throws MonitorException { 80 updateBuffer = pdb.getByteBuffer().duplicate(); 81 82 // if continuous sampling is requested, register with the sampler thread 83 if (interval > 0) { 84 samplerTask = new SamplerTask(); 85 timer.schedule(samplerTask, 0, interval); 86 } 87 } 88 89 /** 90 * {@inheritDoc} 91 */ 92 public void detach() { 93 try { 94 if (interval > 0) { 95 if (samplerTask != null) { 96 samplerTask.cancel(); 97 samplerTask = null; 98 } 99 if (notifierTask != null) { 100 notifierTask.cancel(); 101 notifierTask = null; 102 } 103 sample(); 104 } 105 } catch (RemoteException e) { 106 // XXX: - use logging api? throw an exception instead? 107 System.err.println("Could not read data for remote JVM " + vmid); 108 e.printStackTrace(); 109 110 } finally { 111 super.detach(); 112 } 113 } 114 115 /** 116 * Get a copy of the remote instrumentation buffer. 117 *<p> 118 * The data in the remote instrumentation buffer is copied into 119 * a local byte buffer. 120 * 121 * @throws RemoteException Thrown on any communications errors with 122 * the remote system. 123 */ 124 public void sample() throws RemoteException { 125 assert updateBuffer != null; 126 ((PerfDataBuffer)pdb).sample(updateBuffer); 127 } 128 129 /** 130 * Get the proxy to the remote MonitoredVm. 131 * 132 * @return RemoteVm - the proxy to the remote MonitoredVm. 133 */ 134 public RemoteVm getRemoteVm() { 135 return rvm; 136 } 137 138 /** 139 * {@inheritDoc} 140 */ 141 public void addVmListener(VmListener l) { 142 synchronized(listeners) { 143 listeners.add(l); 144 if (notifierTask == null) { 145 notifierTask = new NotifierTask(); 146 timer.schedule(notifierTask, 0, interval); 147 } 148 } 149 } 150 151 /** 152 * {@inheritDoc} 153 */ 154 public void removeVmListener(VmListener l) { 155 synchronized(listeners) { 156 listeners.remove(l); 157 if (listeners.isEmpty() && (notifierTask != null)) { 158 notifierTask.cancel(); 159 notifierTask = null; 160 } 161 } 162 } 163 164 /** 165 * {@inheritDoc} 166 */ 167 public void setInterval(int newInterval) { 168 synchronized(listeners) { 169 if (newInterval == interval) { 170 return; 171 } 172 173 int oldInterval = interval; 174 super.setInterval(newInterval); 175 176 if (samplerTask != null) { 177 samplerTask.cancel(); 178 SamplerTask oldSamplerTask = samplerTask; 179 samplerTask = new SamplerTask(); 180 CountedTimerTaskUtils.reschedule(timer, oldSamplerTask, 181 samplerTask, oldInterval, 182 newInterval); 183 } 184 if (notifierTask != null) { 185 notifierTask.cancel(); 186 NotifierTask oldNotifierTask = notifierTask; 187 notifierTask = new NotifierTask(); 188 CountedTimerTaskUtils.reschedule(timer, oldNotifierTask, 189 notifierTask, oldInterval, 190 newInterval); 191 } 192 } 193 } 194 195 /** 196 * Fire MonitoredVmStructureChanged events. 197 * 198 * @param inserted List of Monitor objects inserted. 199 * @param removed List of Monitor objects removed. 200 */ 201 @SuppressWarnings("unchecked") // Cast of result of clone 202 void fireMonitorStatusChangedEvents(List<Monitor> inserted, List<Monitor> removed) { 203 ArrayList<VmListener> registered = null; 204 MonitorStatusChangeEvent ev = null; 205 206 synchronized(listeners) { 207 registered = (ArrayList)listeners.clone(); 208 } 209 210 for (Iterator<VmListener> i = registered.iterator(); i.hasNext(); /* empty */) { 211 VmListener l = i.next(); 212 if (ev == null) { 213 ev = new MonitorStatusChangeEvent(this, inserted, removed); 214 } 215 l.monitorStatusChanged(ev); 216 } 217 } 218 219 /** 220 * Fire MonitoredVmStructureChanged events. 221 */ 222 @SuppressWarnings("unchecked") // Cast of result of clone 223 void fireMonitorsUpdatedEvents() { 224 ArrayList<VmListener> registered = null; 225 VmEvent ev = null; 226 227 synchronized(listeners) { 228 registered = (ArrayList)listeners.clone(); 229 } 230 231 for (Iterator<VmListener> i = registered.iterator(); i.hasNext(); /* empty */) { 232 VmListener l = i.next(); 233 if (ev == null) { 234 ev = new VmEvent(this); 235 } 236 l.monitorsUpdated(ev); 237 } 238 } 239 240 /* 241 * Timer Tasks. There are two separate timer tasks here. The SamplerTask 242 * is active whenever we are attached to the remote JVM with a periodic 243 * sampling interval > 0. The NotifierTask is only active if a VmListener 244 * has registered with this RemoteMonitoredVm instance. Also, in the future 245 * we may want to run these tasks at different intervals. Currently, 246 * they run at the same interval and some significant work may 247 * need to be done to complete the separation of these two intervals. 248 */ 249 250 /** 251 * Class to periodically check the state of the defined monitors 252 * for the remote MonitoredVm instance and to notify listeners of 253 * any detected changes. 254 */ 255 private class NotifierTask extends CountedTimerTask { 256 public void run() { 257 super.run(); 258 try { 259 MonitorStatus status = getMonitorStatus(); 260 261 List<Monitor> inserted = status.getInserted(); 262 List<Monitor> removed = status.getRemoved(); 263 264 if (!inserted.isEmpty() || !removed.isEmpty()) { 265 fireMonitorStatusChangedEvents(inserted, removed); 266 } 267 } catch (MonitorException e) { 268 // XXX: use logging api? fire disconnect events? mark errored? 269 // fireDisconnectedEvents(); 270 System.err.println("Exception updating monitors for " 271 + getVmIdentifier()); 272 e.printStackTrace(); 273 // XXX: should we cancle the notifierTask here? 274 // this.cancel(); 275 } 276 } 277 } 278 279 /** 280 * Class to periodically sample the remote instrumentation byte buffer 281 * and refresh the local copy. Registered listeners are notified of 282 * the completion of a sampling event. 283 */ 284 private class SamplerTask extends CountedTimerTask { 285 public void run() { 286 super.run(); 287 try { 288 sample(); 289 fireMonitorsUpdatedEvents(); 290 291 } catch (RemoteException e) { 292 // XXX: use logging api, mark vm as errored. 293 System.err.println("Exception taking sample for " 294 + getVmIdentifier()); 295 e.printStackTrace(); 296 this.cancel(); 297 } 298 } 299 } 300 }