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 }