1 /*
   2  * Copyright (c) 2004, 2008, 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.local;
  27 
  28 import sun.jvmstat.monitor.*;
  29 import sun.jvmstat.monitor.event.*;
  30 import sun.jvmstat.perfdata.monitor.*;
  31 import java.util.*;
  32 import java.net.*;
  33 
  34 /**
  35  * Concrete implementation of the MonitoredHost interface for the
  36  * <em>local</em> protocol of the HotSpot PerfData monitoring implementation.
  37  *
  38  * @author Brian Doherty
  39  * @since 1.5
  40  */
  41 public class MonitoredHostProvider extends MonitoredHost {
  42     private static final int DEFAULT_POLLING_INTERVAL = 1000;
  43 
  44     private ArrayList<HostListener> listeners;
  45     private NotifierTask task;
  46     private HashSet<Integer> activeVms;
  47     private LocalVmManager vmManager;
  48 
  49     /**
  50      * Create a MonitoredHostProvider instance using the given HostIdentifier.
  51      *
  52      * @param hostId the host identifier for this MonitoredHost
  53      */
  54     public MonitoredHostProvider(HostIdentifier hostId) {
  55         this.hostId = hostId;
  56         this.listeners = new ArrayList<HostListener>();
  57         this.interval = DEFAULT_POLLING_INTERVAL;
  58         this.activeVms = new HashSet<Integer>();
  59         this.vmManager = new LocalVmManager();
  60     }
  61 
  62     /**
  63      * {@inheritDoc}
  64      */
  65     public MonitoredVm getMonitoredVm(VmIdentifier vmid)
  66                        throws MonitorException {
  67         return getMonitoredVm(vmid, DEFAULT_POLLING_INTERVAL);
  68     }
  69 
  70     /**
  71      * {@inheritDoc}
  72      */
  73     public MonitoredVm getMonitoredVm(VmIdentifier vmid, int interval)
  74                        throws MonitorException {
  75         try {
  76             VmIdentifier nvmid = hostId.resolve(vmid);
  77             return new LocalMonitoredVm(nvmid, interval);
  78         } catch (URISyntaxException e) {
  79             /*
  80              * the VmIdentifier is expected to be a valid and it should
  81              * resolve reasonably against the host identifier. A
  82              * URISyntaxException here is most likely a programming error.
  83              */
  84             throw new IllegalArgumentException("Malformed URI: "
  85                                                + vmid.toString(), e);
  86         }
  87     }
  88 
  89     /**
  90      * {@inheritDoc}
  91      */
  92     public void detach(MonitoredVm vm) {
  93         vm.detach();
  94     }
  95 
  96     /**
  97      * {@inheritDoc}
  98      */
  99     public void addHostListener(HostListener listener) {
 100         synchronized(listeners) {
 101             listeners.add(listener);
 102             if (task == null) {
 103                 task = new NotifierTask();
 104                 LocalEventTimer timer = LocalEventTimer.getInstance();
 105                 timer.schedule(task, interval, interval);
 106             }
 107         }
 108     }
 109 
 110     /**
 111      * {@inheritDoc}
 112      */
 113     public void removeHostListener(HostListener listener) {
 114         synchronized(listeners) {
 115             listeners.remove(listener);
 116             if (listeners.isEmpty() && (task != null)) {
 117                 task.cancel();
 118                 task = null;
 119             }
 120         }
 121     }
 122 
 123     /**
 124      * {@inheritDoc}
 125      */
 126     public void setInterval(int newInterval) {
 127         synchronized(listeners) {
 128             if (newInterval == interval) {
 129                 return;
 130             }
 131 
 132             int oldInterval = interval;
 133             super.setInterval(newInterval);
 134 
 135             if (task != null) {
 136                 task.cancel();
 137                 NotifierTask oldTask = task;
 138                 task = new NotifierTask();
 139                 LocalEventTimer timer = LocalEventTimer.getInstance();
 140                 CountedTimerTaskUtils.reschedule(timer, oldTask, task,
 141                                                  oldInterval, newInterval);
 142             }
 143         }
 144     }
 145 
 146     /**
 147      * {@inheritDoc}
 148      */
 149     public Set<Integer> activeVms() {
 150         return vmManager.activeVms();
 151     }
 152 
 153     /**
 154      * Fire VmEvent events.
 155      *
 156      * @param active a set of Integer objects containing the vmid of
 157      *               the active Vms
 158      * @param started a set of Integer objects containing the vmid of
 159      *                new Vms started since last interval.
 160      * @param terminated a set of Integer objects containing the vmid of
 161      *                   terminated Vms since last interval.
 162      */
 163     private void fireVmStatusChangedEvents(Set active, Set started,
 164                                            Set terminated) {
 165         ArrayList registered = null;
 166         VmStatusChangeEvent ev = null;
 167 
 168         synchronized(listeners) {
 169             registered = (ArrayList)listeners.clone();
 170         }
 171 
 172         for (Iterator i = registered.iterator(); i.hasNext(); /* empty */) {
 173             HostListener l = (HostListener)i.next();
 174             if (ev == null) {
 175                 ev = new VmStatusChangeEvent(this, active, started, terminated);
 176             }
 177             l.vmStatusChanged(ev);
 178         }
 179     }
 180 
 181     /**
 182      * Class to poll the local system and generate event notifications.
 183      */
 184     private class NotifierTask extends CountedTimerTask {
 185         public void run() {
 186             super.run();
 187 
 188             // save the last set of active JVMs
 189             Set lastActiveVms = activeVms;
 190 
 191             // get the current set of active JVMs
 192             activeVms = (HashSet<Integer>)vmManager.activeVms();
 193 
 194             if (activeVms.isEmpty()) {
 195                 return;
 196             }
 197             Set<Integer> startedVms = new HashSet<Integer>();
 198             Set<Object> terminatedVms = new HashSet<Object>();
 199 
 200             for (Iterator i = activeVms.iterator(); i.hasNext(); /* empty */) {
 201                 Integer vmid = (Integer)i.next();
 202                 if (!lastActiveVms.contains(vmid)) {
 203                     // a new file has been detected, add to set
 204                     startedVms.add(vmid);
 205                 }
 206             }
 207 
 208             for (Iterator i = lastActiveVms.iterator(); i.hasNext();
 209                     /* empty */) {
 210                 Object o = i.next();
 211                 if (!activeVms.contains(o)) {
 212                     // JVM has terminated, remove it from the active list
 213                     terminatedVms.add(o);
 214                 }
 215             }
 216 
 217             if (!startedVms.isEmpty() || !terminatedVms.isEmpty()) {
 218                 fireVmStatusChangedEvents(activeVms, startedVms,
 219                                           terminatedVms);
 220             }
 221         }
 222     }
 223 }