1 /*
   2  * Copyright (c) 2004, 2015, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 4984057
  27  * @key randomness
  28  * @summary Test that monitors can sample a large number of attributes
  29  * @author Eamonn McManus
  30  *
  31  * @run clean MultiMonitorTest
  32  * @run build MultiMonitorTest
  33  * @run main MultiMonitorTest
  34  */
  35 
  36 import java.util.*;
  37 import javax.management.*;
  38 import javax.management.monitor.*;
  39 
  40 /* We create N MBeans and three monitors, one for each different
  41    monitor type.  Each monitor monitors a single attribute in each of
  42    the N MBeans.  We arrange for the trigger condition to be
  43    satisfied, so the listener we register on each monitor should get N
  44    notifications.  */
  45 public class MultiMonitorTest {
  46     static final int N = 100;
  47     static final ObjectName[] mbeanNames = new ObjectName[N];
  48     static final Monitored[] monitored = new Monitored[N];
  49     static final int COUNTER_THRESHOLD = 1000;
  50     static final int OVER_COUNTER_THRESHOLD = 2000;
  51     static final double GAUGE_THRESHOLD = 1000.0;
  52     static final double OVER_GAUGE_THRESHOLD = 2000.0;
  53     static final String STRING_TO_COMPARE = "chou";
  54     static final String DIFFERENT_STRING = "chevre";
  55 
  56     public static void main(String[] args) throws Exception {
  57         System.out.println("Test that monitors can sample a large " +
  58                            "number of attributes");
  59 
  60         final MBeanServer mbs = MBeanServerFactory.createMBeanServer();
  61         for (int i = 0; i < N; i++) {
  62             mbeanNames[i] = new ObjectName(":type=Monitored,instance=" + i);
  63             monitored[i] = new Monitored();
  64             mbs.registerMBean(monitored[i], mbeanNames[i]);
  65         }
  66         final ObjectName counterMonitor =
  67             new ObjectName(":type=CounterMonitor");
  68         final ObjectName gaugeMonitor =
  69             new ObjectName(":type=GaugeMonitor");
  70         final ObjectName stringMonitor =
  71             new ObjectName(":type=StringMonitor");
  72         final ObjectName[] monitorNames =
  73             new ObjectName[] {counterMonitor, gaugeMonitor, stringMonitor};
  74         final String[] attrNames =
  75             new String[] {"CounterValue", "GaugeValue", "StringValue"};
  76         mbs.createMBean(CounterMonitor.class.getName(), counterMonitor);
  77         mbs.createMBean(GaugeMonitor.class.getName(), gaugeMonitor);
  78         mbs.createMBean(StringMonitor.class.getName(), stringMonitor);
  79         final CounterMonitorMBean counterProxy = (CounterMonitorMBean)
  80             MBeanServerInvocationHandler
  81             .newProxyInstance(mbs, counterMonitor, CounterMonitorMBean.class,
  82                               false);
  83         final GaugeMonitorMBean gaugeProxy = (GaugeMonitorMBean)
  84             MBeanServerInvocationHandler
  85             .newProxyInstance(mbs, gaugeMonitor, GaugeMonitorMBean.class,
  86                               false);
  87         final StringMonitorMBean stringProxy = (StringMonitorMBean)
  88             MBeanServerInvocationHandler
  89             .newProxyInstance(mbs, stringMonitor, StringMonitorMBean.class,
  90                               false);
  91         final MonitorMBean[] proxies = new MonitorMBean[] {
  92             counterProxy, gaugeProxy, stringProxy,
  93         };
  94         for (int i = 0; i < 3; i++) {
  95             proxies[i].setGranularityPeriod(1);
  96             proxies[i].setObservedAttribute(attrNames[i]);
  97             for (int j = 0; j < N; j++)
  98                 proxies[i].addObservedObject(mbeanNames[j]);
  99         }
 100 
 101         final CountListener[] listeners = new CountListener[] {
 102             new CountListener(), new CountListener(), new CountListener()
 103         };
 104         for (int i = 0; i < 3; i++) {
 105             mbs.addNotificationListener(monitorNames[i], listeners[i],
 106                                         null, null);
 107         }
 108 
 109         counterProxy.setInitThreshold(new Integer(COUNTER_THRESHOLD));
 110         counterProxy.setNotify(true);
 111         gaugeProxy.setThresholds(new Double(GAUGE_THRESHOLD), new Double(0.0));
 112         gaugeProxy.setNotifyHigh(true);
 113         stringProxy.setStringToCompare(STRING_TO_COMPARE);
 114         stringProxy.setNotifyDiffer(true);
 115 
 116         // A couple of granularity periods to detect bad behaviour
 117         Thread.sleep(2);
 118 
 119         if (!listenersAreAll(0, listeners)) {
 120             System.out.println("TEST FAILED: listeners not all 0");
 121             System.exit(1);
 122         }
 123 
 124         for (int i = 0; i < 3; i++)
 125             proxies[i].start();
 126 
 127         long startTime = System.currentTimeMillis();
 128         while (!listenersAreAll(N, listeners)
 129                && System.currentTimeMillis() < startTime + 5000)
 130             Thread.sleep(1);
 131 
 132         // More time for bad behaviour
 133         Thread.sleep(1000);
 134 
 135         if (!listenersAreAll(N, listeners)) {
 136             System.out.print("TEST FAILED: listener counts wrong:");
 137             for (int i = 0; i < listeners.length; i++)
 138                 System.out.print(" " + listeners[i].getCount());
 139             System.out.println();
 140             System.exit(1);
 141         }
 142 
 143         for (int i = 0; i < 3; i++) {
 144             proxies[i].stop();
 145             for (int j = 0; j < N; j++)
 146                 proxies[i].removeObservedObject(mbeanNames[j]);
 147             ObjectName[] observed = proxies[i].getObservedObjects();
 148             if (observed.length != 0) {
 149                 System.out.println("TEST FAILED: not all observed objects " +
 150                                    "removed: " + Arrays.asList(observed));
 151                 System.exit(1);
 152             }
 153         }
 154 
 155         System.out.println("Test passed");
 156     }
 157 
 158     public static interface MonitoredMBean {
 159         public int getCounterValue();
 160         public double getGaugeValue();
 161         public String getStringValue();
 162     }
 163 
 164     public static class Monitored implements MonitoredMBean {
 165         /* We give a small random number of normal readings (possibly
 166            zero) before giving a reading that provokes a
 167            notification.  */
 168         private int okCounter = randomInt(5);
 169         private int okGauge = randomInt(5);
 170         private int okString = randomInt(5);
 171 
 172         public synchronized int getCounterValue() {
 173             if (--okCounter >= 0)
 174                 return 0;
 175             else
 176                 return OVER_COUNTER_THRESHOLD;
 177         }
 178 
 179         public synchronized double getGaugeValue() {
 180             if (--okGauge >= 0)
 181                 return 0.0;
 182             else
 183                 return OVER_GAUGE_THRESHOLD;
 184         }
 185 
 186         public synchronized String getStringValue() {
 187             if (--okString >= 0)
 188                 return STRING_TO_COMPARE;
 189             else
 190                 return DIFFERENT_STRING;
 191         }
 192     }
 193 
 194     public static class CountListener implements NotificationListener {
 195         private int count;
 196 
 197         public synchronized void handleNotification(Notification n, Object h) {
 198             if (!(n instanceof MonitorNotification)) {
 199                 System.out.println("TEST FAILED: bad notif: " +
 200                                    n.getClass().getName());
 201                 System.exit(1);
 202             }
 203             if (n.getType().indexOf("error") >= 0) {
 204                 System.out.println("TEST FAILED: error notif: " + n.getType());
 205                 System.exit(1);
 206             }
 207             count++;
 208         }
 209 
 210         public synchronized int getCount() {
 211             return count;
 212         }
 213     }
 214 
 215     private static boolean listenersAreAll(int n, CountListener[] listeners) {
 216         for (int i = 0; i < listeners.length; i++) {
 217             if (listeners[i].getCount() != n)
 218                 return false;
 219         }
 220         return true;
 221     }
 222 
 223     private static final Random random = new Random();
 224     static synchronized int randomInt(int n) {
 225         return random.nextInt(n);
 226     }
 227 }