1 /*
2  * Copyright (c) 1999, 2017, 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 javax.management.monitor;
27 
28 import static com.sun.jmx.defaults.JmxProperties.MONITOR_LOGGER;
29 import java.lang.System.Logger.Level;
30 import javax.management.ObjectName;
31 import javax.management.MBeanNotificationInfo;
32 import static javax.management.monitor.Monitor.NumericalType.*;
33 import static javax.management.monitor.MonitorNotification.*;
34 
35 /**
36  * Defines a monitor MBean designed to observe the values of a counter
37  * attribute.
38  *
39  * <P> A counter monitor sends a {@link
40  * MonitorNotification#THRESHOLD_VALUE_EXCEEDED threshold
41  * notification} when the value of the counter reaches or exceeds a
42  * threshold known as the comparison level.  The notify flag must be
43  * set to <CODE>true</CODE>.
44  *
45  * <P> In addition, an offset mechanism enables particular counting
46  * intervals to be detected.  If the offset value is not zero,
47  * whenever the threshold is triggered by the counter value reaching a
48  * comparison level, that comparison level is incremented by the
49  * offset value.  This is regarded as taking place instantaneously,
50  * that is, before the count is incremented.  Thus, for each level,
51  * the threshold triggers an event notification every time the count
52  * increases by an interval equal to the offset value.
53  *
54  * <P> If the counter can wrap around its maximum value, the modulus
55  * needs to be specified.  The modulus is the value at which the
56  * counter is reset to zero.
57  *
58  * <P> If the counter difference mode is used, the value of the
59  * derived gauge is calculated as the difference between the observed
60  * counter values for two successive observations.  If this difference
61  * is negative, the value of the derived gauge is incremented by the
62  * value of the modulus.  The derived gauge value (V[t]) is calculated
63  * using the following method:
64  *
65  * <UL>
66  * <LI>if (counter[t] - counter[t-GP]) is positive then
67  * V[t] = counter[t] - counter[t-GP]
68  * <LI>if (counter[t] - counter[t-GP]) is negative then
69  * V[t] = counter[t] - counter[t-GP] + MODULUS
70  * </UL>
71  *
72  * This implementation of the counter monitor requires the observed
73  * attribute to be of the type integer (<CODE>Byte</CODE>,
74  * <CODE>Integer</CODE>, <CODE>Short</CODE>, <CODE>Long</CODE>).
75  *
76  *
77  * @since 1.5
78  */
79 public class CounterMonitor extends Monitor implements CounterMonitorMBean {
80 
81     /*
82      * ------------------------------------------
83      *  PACKAGE CLASSES
84      * ------------------------------------------
85      */
86 
87     static class CounterMonitorObservedObject extends ObservedObject {
88 
CounterMonitorObservedObject(ObjectName observedObject)89         public CounterMonitorObservedObject(ObjectName observedObject) {
90             super(observedObject);
91         }
92 
getThreshold()93         public final synchronized Number getThreshold() {
94             return threshold;
95         }
setThreshold(Number threshold)96         public final synchronized void setThreshold(Number threshold) {
97             this.threshold = threshold;
98         }
getPreviousScanCounter()99         public final synchronized Number getPreviousScanCounter() {
100             return previousScanCounter;
101         }
setPreviousScanCounter( Number previousScanCounter)102         public final synchronized void setPreviousScanCounter(
103                                                   Number previousScanCounter) {
104             this.previousScanCounter = previousScanCounter;
105         }
getModulusExceeded()106         public final synchronized boolean getModulusExceeded() {
107             return modulusExceeded;
108         }
setModulusExceeded( boolean modulusExceeded)109         public final synchronized void setModulusExceeded(
110                                                  boolean modulusExceeded) {
111             this.modulusExceeded = modulusExceeded;
112         }
getDerivedGaugeExceeded()113         public final synchronized Number getDerivedGaugeExceeded() {
114             return derivedGaugeExceeded;
115         }
setDerivedGaugeExceeded( Number derivedGaugeExceeded)116         public final synchronized void setDerivedGaugeExceeded(
117                                                  Number derivedGaugeExceeded) {
118             this.derivedGaugeExceeded = derivedGaugeExceeded;
119         }
getDerivedGaugeValid()120         public final synchronized boolean getDerivedGaugeValid() {
121             return derivedGaugeValid;
122         }
setDerivedGaugeValid( boolean derivedGaugeValid)123         public final synchronized void setDerivedGaugeValid(
124                                                  boolean derivedGaugeValid) {
125             this.derivedGaugeValid = derivedGaugeValid;
126         }
getEventAlreadyNotified()127         public final synchronized boolean getEventAlreadyNotified() {
128             return eventAlreadyNotified;
129         }
setEventAlreadyNotified( boolean eventAlreadyNotified)130         public final synchronized void setEventAlreadyNotified(
131                                                boolean eventAlreadyNotified) {
132             this.eventAlreadyNotified = eventAlreadyNotified;
133         }
getType()134         public final synchronized NumericalType getType() {
135             return type;
136         }
setType(NumericalType type)137         public final synchronized void setType(NumericalType type) {
138             this.type = type;
139         }
140 
141         private Number threshold;
142         private Number previousScanCounter;
143         private boolean modulusExceeded;
144         private Number derivedGaugeExceeded;
145         private boolean derivedGaugeValid;
146         private boolean eventAlreadyNotified;
147         private NumericalType type;
148     }
149 
150     /*
151      * ------------------------------------------
152      *  PRIVATE VARIABLES
153      * ------------------------------------------
154      */
155 
156     /**
157      * Counter modulus.
158      * <BR>The default value is a null Integer object.
159      */
160     private Number modulus = INTEGER_ZERO;
161 
162     /**
163      * Counter offset.
164      * <BR>The default value is a null Integer object.
165      */
166     private Number offset = INTEGER_ZERO;
167 
168     /**
169      * Flag indicating if the counter monitor notifies when exceeding
170      * the threshold.  The default value is set to
171      * <CODE>false</CODE>.
172      */
173     private boolean notify = false;
174 
175     /**
176      * Flag indicating if the counter difference mode is used.  If the
177      * counter difference mode is used, the derived gauge is the
178      * difference between two consecutive observed values.  Otherwise,
179      * the derived gauge is directly the value of the observed
180      * attribute.  The default value is set to <CODE>false</CODE>.
181      */
182     private boolean differenceMode = false;
183 
184     /**
185      * Initial counter threshold.  This value is used to initialize
186      * the threshold when a new object is added to the list and reset
187      * the threshold to its initial value each time the counter
188      * resets.
189      */
190     private Number initThreshold = INTEGER_ZERO;
191 
192     private static final String[] types = {
193         RUNTIME_ERROR,
194         OBSERVED_OBJECT_ERROR,
195         OBSERVED_ATTRIBUTE_ERROR,
196         OBSERVED_ATTRIBUTE_TYPE_ERROR,
197         THRESHOLD_ERROR,
198         THRESHOLD_VALUE_EXCEEDED
199     };
200 
201     private static final MBeanNotificationInfo[] notifsInfo = {
202         new MBeanNotificationInfo(
203             types,
204             "javax.management.monitor.MonitorNotification",
205             "Notifications sent by the CounterMonitor MBean")
206     };
207 
208     /*
209      * ------------------------------------------
210      *  CONSTRUCTORS
211      * ------------------------------------------
212      */
213 
214     /**
215      * Default constructor.
216      */
CounterMonitor()217     public CounterMonitor() {
218     }
219 
220     /*
221      * ------------------------------------------
222      *  PUBLIC METHODS
223      * ------------------------------------------
224      */
225 
226     /**
227      * Starts the counter monitor.
228      */
start()229     public synchronized void start() {
230         if (isActive()) {
231             MONITOR_LOGGER.log(Level.TRACE, "the monitor is already active");
232             return;
233         }
234         // Reset values.
235         //
236         for (ObservedObject o : observedObjects) {
237             final CounterMonitorObservedObject cmo =
238                 (CounterMonitorObservedObject) o;
239             cmo.setThreshold(initThreshold);
240             cmo.setModulusExceeded(false);
241             cmo.setEventAlreadyNotified(false);
242             cmo.setPreviousScanCounter(null);
243         }
244         doStart();
245     }
246 
247     /**
248      * Stops the counter monitor.
249      */
stop()250     public synchronized void stop() {
251         doStop();
252     }
253 
254     // GETTERS AND SETTERS
255     //--------------------
256 
257     /**
258      * Gets the derived gauge of the specified object, if this object is
259      * contained in the set of observed MBeans, or <code>null</code> otherwise.
260      *
261      * @param object the name of the object whose derived gauge is to
262      * be returned.
263      *
264      * @return The derived gauge of the specified object.
265      *
266      */
267     @Override
getDerivedGauge(ObjectName object)268     public synchronized Number getDerivedGauge(ObjectName object) {
269         return (Number) super.getDerivedGauge(object);
270     }
271 
272     /**
273      * Gets the derived gauge timestamp of the specified object, if
274      * this object is contained in the set of observed MBeans, or
275      * <code>0</code> otherwise.
276      *
277      * @param object the name of the object whose derived gauge
278      * timestamp is to be returned.
279      *
280      * @return The derived gauge timestamp of the specified object.
281      *
282      */
283     @Override
getDerivedGaugeTimeStamp(ObjectName object)284     public synchronized long getDerivedGaugeTimeStamp(ObjectName object) {
285         return super.getDerivedGaugeTimeStamp(object);
286     }
287 
288     /**
289      * Gets the current threshold value of the specified object, if
290      * this object is contained in the set of observed MBeans, or
291      * <code>null</code> otherwise.
292      *
293      * @param object the name of the object whose threshold is to be
294      * returned.
295      *
296      * @return The threshold value of the specified object.
297      *
298      */
getThreshold(ObjectName object)299     public synchronized Number getThreshold(ObjectName object) {
300         final CounterMonitorObservedObject o =
301             (CounterMonitorObservedObject) getObservedObject(object);
302         if (o == null)
303             return null;
304 
305         // If the counter that is monitored rolls over when it reaches a
306         // maximum value, then the modulus value needs to be set to that
307         // maximum value. The threshold will then also roll over whenever
308         // it strictly exceeds the modulus value. When the threshold rolls
309         // over, it is reset to the value that was specified through the
310         // latest call to the monitor's setInitThreshold method, before
311         // any offsets were applied.
312         //
313         if (offset.longValue() > 0L &&
314             modulus.longValue() > 0L &&
315             o.getThreshold().longValue() > modulus.longValue()) {
316             return initThreshold;
317         } else {
318             return o.getThreshold();
319         }
320     }
321 
322     /**
323      * Gets the initial threshold value common to all observed objects.
324      *
325      * @return The initial threshold.
326      *
327      * @see #setInitThreshold
328      *
329      */
getInitThreshold()330     public synchronized Number getInitThreshold() {
331         return initThreshold;
332     }
333 
334     /**
335      * Sets the initial threshold value common to all observed objects.
336      *
337      * <BR>The current threshold of every object in the set of
338      * observed MBeans is updated consequently.
339      *
340      * @param value The initial threshold value.
341      *
342      * @exception IllegalArgumentException The specified
343      * threshold is null or the threshold value is less than zero.
344      *
345      * @see #getInitThreshold
346      *
347      */
setInitThreshold(Number value)348     public synchronized void setInitThreshold(Number value)
349         throws IllegalArgumentException {
350 
351         if (value == null) {
352             throw new IllegalArgumentException("Null threshold");
353         }
354         if (value.longValue() < 0L) {
355             throw new IllegalArgumentException("Negative threshold");
356         }
357 
358         if (initThreshold.equals(value))
359             return;
360         initThreshold = value;
361 
362         // Reset values.
363         //
364         int index = 0;
365         for (ObservedObject o : observedObjects) {
366             resetAlreadyNotified(o, index++, THRESHOLD_ERROR_NOTIFIED);
367             final CounterMonitorObservedObject cmo =
368                 (CounterMonitorObservedObject) o;
369             cmo.setThreshold(value);
370             cmo.setModulusExceeded(false);
371             cmo.setEventAlreadyNotified(false);
372         }
373     }
374 
375     /**
376      * Returns the derived gauge of the first object in the set of
377      * observed MBeans.
378      *
379      * @return The derived gauge.
380      *
381      * @deprecated As of JMX 1.2, replaced by
382      * {@link #getDerivedGauge(ObjectName)}
383      */
384     @Deprecated
getDerivedGauge()385     public synchronized Number getDerivedGauge() {
386         if (observedObjects.isEmpty()) {
387             return null;
388         } else {
389             return (Number) observedObjects.get(0).getDerivedGauge();
390         }
391     }
392 
393     /**
394      * Gets the derived gauge timestamp of the first object in the set
395      * of observed MBeans.
396      *
397      * @return The derived gauge timestamp.
398      *
399      * @deprecated As of JMX 1.2, replaced by
400      * {@link #getDerivedGaugeTimeStamp(ObjectName)}
401      */
402     @Deprecated
getDerivedGaugeTimeStamp()403     public synchronized long getDerivedGaugeTimeStamp() {
404         if (observedObjects.isEmpty()) {
405             return 0;
406         } else {
407             return observedObjects.get(0).getDerivedGaugeTimeStamp();
408         }
409     }
410 
411     /**
412      * Gets the threshold value of the first object in the set of
413      * observed MBeans.
414      *
415      * @return The threshold value.
416      *
417      * @see #setThreshold
418      *
419      * @deprecated As of JMX 1.2, replaced by {@link #getThreshold(ObjectName)}
420      */
421     @Deprecated
getThreshold()422     public synchronized Number getThreshold() {
423         return getThreshold(getObservedObject());
424     }
425 
426     /**
427      * Sets the initial threshold value.
428      *
429      * @param value The initial threshold value.
430      *
431      * @exception IllegalArgumentException The specified threshold is
432      * null or the threshold value is less than zero.
433      *
434      * @see #getThreshold()
435      *
436      * @deprecated As of JMX 1.2, replaced by {@link #setInitThreshold}
437      */
438     @Deprecated
setThreshold(Number value)439     public synchronized void setThreshold(Number value)
440         throws IllegalArgumentException {
441         setInitThreshold(value);
442     }
443 
444     /**
445      * Gets the offset value common to all observed MBeans.
446      *
447      * @return The offset value.
448      *
449      * @see #setOffset
450      */
getOffset()451     public synchronized Number getOffset() {
452         return offset;
453     }
454 
455     /**
456      * Sets the offset value common to all observed MBeans.
457      *
458      * @param value The offset value.
459      *
460      * @exception IllegalArgumentException The specified
461      * offset is null or the offset value is less than zero.
462      *
463      * @see #getOffset
464      */
setOffset(Number value)465     public synchronized void setOffset(Number value)
466         throws IllegalArgumentException {
467 
468         if (value == null) {
469             throw new IllegalArgumentException("Null offset");
470         }
471         if (value.longValue() < 0L) {
472             throw new IllegalArgumentException("Negative offset");
473         }
474 
475         if (offset.equals(value))
476             return;
477         offset = value;
478 
479         int index = 0;
480         for (ObservedObject o : observedObjects) {
481             resetAlreadyNotified(o, index++, THRESHOLD_ERROR_NOTIFIED);
482         }
483     }
484 
485     /**
486      * Gets the modulus value common to all observed MBeans.
487      *
488      * @see #setModulus
489      *
490      * @return The modulus value.
491      */
getModulus()492     public synchronized Number getModulus() {
493         return modulus;
494     }
495 
496     /**
497      * Sets the modulus value common to all observed MBeans.
498      *
499      * @param value The modulus value.
500      *
501      * @exception IllegalArgumentException The specified
502      * modulus is null or the modulus value is less than zero.
503      *
504      * @see #getModulus
505      */
setModulus(Number value)506     public synchronized void setModulus(Number value)
507         throws IllegalArgumentException {
508 
509         if (value == null) {
510             throw new IllegalArgumentException("Null modulus");
511         }
512         if (value.longValue() < 0L) {
513             throw new IllegalArgumentException("Negative modulus");
514         }
515 
516         if (modulus.equals(value))
517             return;
518         modulus = value;
519 
520         // Reset values.
521         //
522         int index = 0;
523         for (ObservedObject o : observedObjects) {
524             resetAlreadyNotified(o, index++, THRESHOLD_ERROR_NOTIFIED);
525             final CounterMonitorObservedObject cmo =
526                 (CounterMonitorObservedObject) o;
527             cmo.setModulusExceeded(false);
528         }
529     }
530 
531     /**
532      * Gets the notification's on/off switch value common to all
533      * observed MBeans.
534      *
535      * @return <CODE>true</CODE> if the counter monitor notifies when
536      * exceeding the threshold, <CODE>false</CODE> otherwise.
537      *
538      * @see #setNotify
539      */
getNotify()540     public synchronized boolean getNotify() {
541         return notify;
542     }
543 
544     /**
545      * Sets the notification's on/off switch value common to all
546      * observed MBeans.
547      *
548      * @param value The notification's on/off switch value.
549      *
550      * @see #getNotify
551      */
setNotify(boolean value)552     public synchronized void setNotify(boolean value) {
553         if (notify == value)
554             return;
555         notify = value;
556     }
557 
558     /**
559      * Gets the difference mode flag value common to all observed MBeans.
560      *
561      * @return <CODE>true</CODE> if the difference mode is used,
562      * <CODE>false</CODE> otherwise.
563      *
564      * @see #setDifferenceMode
565      */
getDifferenceMode()566     public synchronized boolean getDifferenceMode() {
567         return differenceMode;
568     }
569 
570     /**
571      * Sets the difference mode flag value common to all observed MBeans.
572      *
573      * @param value The difference mode flag value.
574      *
575      * @see #getDifferenceMode
576      */
setDifferenceMode(boolean value)577     public synchronized void setDifferenceMode(boolean value) {
578         if (differenceMode == value)
579             return;
580         differenceMode = value;
581 
582         // Reset values.
583         //
584         for (ObservedObject o : observedObjects) {
585             final CounterMonitorObservedObject cmo =
586                 (CounterMonitorObservedObject) o;
587             cmo.setThreshold(initThreshold);
588             cmo.setModulusExceeded(false);
589             cmo.setEventAlreadyNotified(false);
590             cmo.setPreviousScanCounter(null);
591         }
592     }
593 
594     /**
595      * Returns a <CODE>NotificationInfo</CODE> object containing the
596      * name of the Java class of the notification and the notification
597      * types sent by the counter monitor.
598      */
599     @Override
getNotificationInfo()600     public MBeanNotificationInfo[] getNotificationInfo() {
601         return notifsInfo.clone();
602     }
603 
604     /*
605      * ------------------------------------------
606      *  PRIVATE METHODS
607      * ------------------------------------------
608      */
609 
610     /**
611      * Updates the derived gauge attribute of the observed object.
612      *
613      * @param scanCounter The value of the observed attribute.
614      * @param o The observed object.
615      * @return <CODE>true</CODE> if the derived gauge value is valid,
616      * <CODE>false</CODE> otherwise.  The derived gauge value is
617      * invalid when the differenceMode flag is set to
618      * <CODE>true</CODE> and it is the first notification (so we
619      * haven't 2 consecutive values to update the derived gauge).
620      */
updateDerivedGauge( Object scanCounter, CounterMonitorObservedObject o)621     private synchronized boolean updateDerivedGauge(
622         Object scanCounter, CounterMonitorObservedObject o) {
623 
624         boolean is_derived_gauge_valid;
625 
626         // The counter difference mode is used.
627         //
628         if (differenceMode) {
629 
630             // The previous scan counter has been initialized.
631             //
632             if (o.getPreviousScanCounter() != null) {
633                 setDerivedGaugeWithDifference((Number)scanCounter, null, o);
634 
635                 // If derived gauge is negative it means that the
636                 // counter has wrapped around and the value of the
637                 // threshold needs to be reset to its initial value.
638                 //
639                 if (((Number)o.getDerivedGauge()).longValue() < 0L) {
640                     if (modulus.longValue() > 0L) {
641                         setDerivedGaugeWithDifference((Number)scanCounter,
642                                                       modulus, o);
643                     }
644                     o.setThreshold(initThreshold);
645                     o.setEventAlreadyNotified(false);
646                 }
647                 is_derived_gauge_valid = true;
648             }
649             // The previous scan counter has not been initialized.
650             // We cannot update the derived gauge...
651             //
652             else {
653                 is_derived_gauge_valid = false;
654             }
655             o.setPreviousScanCounter((Number)scanCounter);
656         }
657         // The counter difference mode is not used.
658         //
659         else {
660             o.setDerivedGauge((Number)scanCounter);
661             is_derived_gauge_valid = true;
662         }
663         return is_derived_gauge_valid;
664     }
665 
666     /**
667      * Updates the notification attribute of the observed object
668      * and notifies the listeners only once if the notify flag
669      * is set to <CODE>true</CODE>.
670      * @param o The observed object.
671      */
updateNotifications( CounterMonitorObservedObject o)672     private synchronized MonitorNotification updateNotifications(
673         CounterMonitorObservedObject o) {
674 
675         MonitorNotification n = null;
676 
677         // Send notification if notify is true.
678         //
679         if (!o.getEventAlreadyNotified()) {
680             if (((Number)o.getDerivedGauge()).longValue() >=
681                 o.getThreshold().longValue()) {
682                 if (notify) {
683                     n = new MonitorNotification(THRESHOLD_VALUE_EXCEEDED,
684                                                 this,
685                                                 0,
686                                                 0,
687                                                 "",
688                                                 null,
689                                                 null,
690                                                 null,
691                                                 o.getThreshold());
692                 }
693                 if (!differenceMode) {
694                     o.setEventAlreadyNotified(true);
695                 }
696             }
697         } else {
698             if (MONITOR_LOGGER.isLoggable(Level.TRACE)) {
699                 final StringBuilder strb = new StringBuilder()
700                 .append("The notification:")
701                 .append("\n\tNotification observed object = ")
702                 .append(o.getObservedObject())
703                 .append("\n\tNotification observed attribute = ")
704                 .append(getObservedAttribute())
705                 .append("\n\tNotification threshold level = ")
706                 .append(o.getThreshold())
707                 .append("\n\tNotification derived gauge = ")
708                 .append(o.getDerivedGauge())
709                 .append("\nhas already been sent");
710                 MONITOR_LOGGER.log(Level.TRACE, strb::toString);
711             }
712         }
713 
714         return n;
715     }
716 
717     /**
718      * Updates the threshold attribute of the observed object.
719      * @param o The observed object.
720      */
updateThreshold(CounterMonitorObservedObject o)721     private synchronized void updateThreshold(CounterMonitorObservedObject o) {
722 
723         // Calculate the new threshold value if the threshold has been
724         // exceeded and if the offset value is greater than zero.
725         //
726         if (((Number)o.getDerivedGauge()).longValue() >=
727             o.getThreshold().longValue()) {
728 
729             if (offset.longValue() > 0L) {
730 
731                 // Increment the threshold until its value is greater
732                 // than the one for the current derived gauge.
733                 //
734                 long threshold_value = o.getThreshold().longValue();
735                 while (((Number)o.getDerivedGauge()).longValue() >=
736                        threshold_value) {
737                     threshold_value += offset.longValue();
738                 }
739 
740                 // Set threshold attribute.
741                 //
742                 switch (o.getType()) {
743                     case INTEGER:
744                         o.setThreshold(Integer.valueOf((int)threshold_value));
745                         break;
746                     case BYTE:
747                         o.setThreshold(Byte.valueOf((byte)threshold_value));
748                         break;
749                     case SHORT:
750                         o.setThreshold(Short.valueOf((short)threshold_value));
751                         break;
752                     case LONG:
753                         o.setThreshold(Long.valueOf(threshold_value));
754                         break;
755                     default:
756                         // Should never occur...
757                         MONITOR_LOGGER.log(Level.TRACE,
758                                 "the threshold type is invalid");
759                         break;
760                 }
761 
762                 // If the counter can wrap around when it reaches
763                 // its maximum and we are not dealing with counter
764                 // differences then we need to reset the threshold
765                 // to its initial value too.
766                 //
767                 if (!differenceMode) {
768                     if (modulus.longValue() > 0L) {
769                         if (o.getThreshold().longValue() >
770                             modulus.longValue()) {
771                             o.setModulusExceeded(true);
772                             o.setDerivedGaugeExceeded(
773                                 (Number) o.getDerivedGauge());
774                         }
775                     }
776                 }
777 
778                 // Threshold value has been modified so we can notify again.
779                 //
780                 o.setEventAlreadyNotified(false);
781             } else {
782                 o.setModulusExceeded(true);
783                 o.setDerivedGaugeExceeded((Number) o.getDerivedGauge());
784             }
785         }
786     }
787 
788     /**
789      * Sets the derived gauge of the specified observed object when the
790      * differenceMode flag is set to <CODE>true</CODE>.  Integer types
791      * only are allowed.
792      *
793      * @param scanCounter The value of the observed attribute.
794      * @param mod The counter modulus value.
795      * @param o The observed object.
796      */
setDerivedGaugeWithDifference( Number scanCounter, Number mod, CounterMonitorObservedObject o)797     private synchronized void setDerivedGaugeWithDifference(
798         Number scanCounter, Number mod, CounterMonitorObservedObject o) {
799         /* We do the arithmetic using longs here even though the
800            result may end up in a smaller type.  Since
801            l == (byte)l (mod 256) for any long l,
802            (byte) ((byte)l1 + (byte)l2) == (byte) (l1 + l2),
803            and likewise for subtraction.  So it's the same as if
804            we had done the arithmetic in the smaller type.*/
805 
806         long derived =
807             scanCounter.longValue() - o.getPreviousScanCounter().longValue();
808         if (mod != null)
809             derived += modulus.longValue();
810 
811         switch (o.getType()) {
812         case INTEGER: o.setDerivedGauge(Integer.valueOf((int) derived)); break;
813         case BYTE: o.setDerivedGauge(Byte.valueOf((byte) derived)); break;
814         case SHORT: o.setDerivedGauge(Short.valueOf((short) derived)); break;
815         case LONG: o.setDerivedGauge(Long.valueOf(derived)); break;
816         default:
817             // Should never occur...
818             MONITOR_LOGGER.log(Level.TRACE,
819                     "the threshold type is invalid");
820             break;
821         }
822     }
823 
824     /*
825      * ------------------------------------------
826      *  PACKAGE METHODS
827      * ------------------------------------------
828      */
829 
830     /**
831      * Factory method for ObservedObject creation.
832      *
833      * @since 1.6
834      */
835     @Override
createObservedObject(ObjectName object)836     ObservedObject createObservedObject(ObjectName object) {
837         final CounterMonitorObservedObject cmo =
838             new CounterMonitorObservedObject(object);
839         cmo.setThreshold(initThreshold);
840         cmo.setModulusExceeded(false);
841         cmo.setEventAlreadyNotified(false);
842         cmo.setPreviousScanCounter(null);
843         return cmo;
844     }
845 
846     /**
847      * This method globally sets the derived gauge type for the given
848      * "object" and "attribute" after checking that the type of the
849      * supplied observed attribute value is one of the value types
850      * supported by this monitor.
851      */
852     @Override
isComparableTypeValid(ObjectName object, String attribute, Comparable<?> value)853     synchronized boolean isComparableTypeValid(ObjectName object,
854                                                String attribute,
855                                                Comparable<?> value) {
856         final CounterMonitorObservedObject o =
857             (CounterMonitorObservedObject) getObservedObject(object);
858         if (o == null)
859             return false;
860 
861         // Check that the observed attribute is of type "Integer".
862         //
863         if (value instanceof Integer) {
864             o.setType(INTEGER);
865         } else if (value instanceof Byte) {
866             o.setType(BYTE);
867         } else if (value instanceof Short) {
868             o.setType(SHORT);
869         } else if (value instanceof Long) {
870             o.setType(LONG);
871         } else {
872             return false;
873         }
874         return true;
875     }
876 
877     @Override
getDerivedGaugeFromComparable( ObjectName object, String attribute, Comparable<?> value)878     synchronized Comparable<?> getDerivedGaugeFromComparable(
879                                                   ObjectName object,
880                                                   String attribute,
881                                                   Comparable<?> value) {
882         final CounterMonitorObservedObject o =
883             (CounterMonitorObservedObject) getObservedObject(object);
884         if (o == null)
885             return null;
886 
887         // Check if counter has wrapped around.
888         //
889         if (o.getModulusExceeded()) {
890             if (((Number)o.getDerivedGauge()).longValue() <
891                 o.getDerivedGaugeExceeded().longValue()) {
892                     o.setThreshold(initThreshold);
893                     o.setModulusExceeded(false);
894                     o.setEventAlreadyNotified(false);
895             }
896         }
897 
898         // Update the derived gauge attributes and check the
899         // validity of the new value. The derived gauge value
900         // is invalid when the differenceMode flag is set to
901         // true and it is the first notification, i.e. we
902         // haven't got 2 consecutive values to update the
903         // derived gauge.
904         //
905         o.setDerivedGaugeValid(updateDerivedGauge(value, o));
906 
907         return (Comparable<?>) o.getDerivedGauge();
908     }
909 
910     @Override
onErrorNotification(MonitorNotification notification)911     synchronized void onErrorNotification(MonitorNotification notification) {
912         final CounterMonitorObservedObject o = (CounterMonitorObservedObject)
913             getObservedObject(notification.getObservedObject());
914         if (o == null)
915             return;
916 
917         // Reset values.
918         //
919         o.setModulusExceeded(false);
920         o.setEventAlreadyNotified(false);
921         o.setPreviousScanCounter(null);
922     }
923 
924     @Override
buildAlarmNotification( ObjectName object, String attribute, Comparable<?> value)925     synchronized MonitorNotification buildAlarmNotification(
926                                                ObjectName object,
927                                                String attribute,
928                                                Comparable<?> value) {
929         final CounterMonitorObservedObject o =
930             (CounterMonitorObservedObject) getObservedObject(object);
931         if (o == null)
932             return null;
933 
934         // Notify the listeners and update the threshold if
935         // the updated derived gauge value is valid.
936         //
937         final MonitorNotification alarm;
938         if (o.getDerivedGaugeValid()) {
939             alarm = updateNotifications(o);
940             updateThreshold(o);
941         } else {
942             alarm = null;
943         }
944         return alarm;
945     }
946 
947     /**
948      * Tests if the threshold, offset and modulus of the specified observed
949      * object are of the same type as the counter. Only integer types are
950      * allowed.
951      *
952      * Note:
953      *   If the optional offset or modulus have not been initialized, their
954      *   default value is an Integer object with a value equal to zero.
955      *
956      * @param object The observed object.
957      * @param attribute The observed attribute.
958      * @param value The sample value.
959      * @return <CODE>true</CODE> if type is the same,
960      * <CODE>false</CODE> otherwise.
961      */
962     @Override
isThresholdTypeValid(ObjectName object, String attribute, Comparable<?> value)963     synchronized boolean isThresholdTypeValid(ObjectName object,
964                                               String attribute,
965                                               Comparable<?> value) {
966         final CounterMonitorObservedObject o =
967             (CounterMonitorObservedObject) getObservedObject(object);
968         if (o == null)
969             return false;
970 
971         Class<? extends Number> c = classForType(o.getType());
972         return (c.isInstance(o.getThreshold()) &&
973                 isValidForType(offset, c) &&
974                 isValidForType(modulus, c));
975     }
976 }
977