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
26package javax.management.monitor;
27
28import static com.sun.jmx.defaults.JmxProperties.MONITOR_LOGGER;
29import java.lang.System.Logger.Level;
30import javax.management.MBeanNotificationInfo;
31import javax.management.ObjectName;
32import static javax.management.monitor.Monitor.NumericalType.*;
33import static javax.management.monitor.MonitorNotification.*;
34
35/**
36 * Defines a monitor MBean designed to observe the values of a gauge attribute.
37 *
38 * <P> A gauge monitor observes an attribute that is continuously
39 * variable with time. A gauge monitor sends notifications as
40 * follows:
41 *
42 * <UL>
43 *
44 * <LI> if the attribute value is increasing and becomes equal to or
45 * greater than the high threshold value, a {@link
46 * MonitorNotification#THRESHOLD_HIGH_VALUE_EXCEEDED threshold high
47 * notification} is sent. The notify high flag must be set to
48 * <CODE>true</CODE>.
49 *
50 * <BR>Subsequent crossings of the high threshold value do not cause
51 * further notifications unless the attribute value becomes equal to
52 * or less than the low threshold value.</LI>
53 *
54 * <LI> if the attribute value is decreasing and becomes equal to or
55 * less than the low threshold value, a {@link
56 * MonitorNotification#THRESHOLD_LOW_VALUE_EXCEEDED threshold low
57 * notification} is sent. The notify low flag must be set to
58 * <CODE>true</CODE>.
59 *
60 * <BR>Subsequent crossings of the low threshold value do not cause
61 * further notifications unless the attribute value becomes equal to
62 * or greater than the high threshold value.</LI>
63 *
64 * </UL>
65 *
66 * This provides a hysteresis mechanism to avoid repeated triggering
67 * of notifications when the attribute value makes small oscillations
68 * around the high or low threshold value.
69 *
70 * <P> If the gauge difference mode is used, the value of the derived
71 * gauge is calculated as the difference between the observed gauge
72 * values for two successive observations.
73 *
74 * <BR>The derived gauge value (V[t]) is calculated using the following method:
75 * <UL>
76 * <LI>V[t] = gauge[t] - gauge[t-GP]</LI>
77 * </UL>
78 *
79 * This implementation of the gauge monitor requires the observed
80 * attribute to be of the type integer or floating-point
81 * (<CODE>Byte</CODE>, <CODE>Integer</CODE>, <CODE>Short</CODE>,
82 * <CODE>Long</CODE>, <CODE>Float</CODE>, <CODE>Double</CODE>).
83 *
84 *
85 * @since 1.5
86 */
87public class GaugeMonitor extends Monitor implements GaugeMonitorMBean {
88
89    /*
90     * ------------------------------------------
91     *  PACKAGE CLASSES
92     * ------------------------------------------
93     */
94
95    static class GaugeMonitorObservedObject extends ObservedObject {
96
97        public GaugeMonitorObservedObject(ObjectName observedObject) {
98            super(observedObject);
99        }
100
101        public final synchronized boolean getDerivedGaugeValid() {
102            return derivedGaugeValid;
103        }
104        public final synchronized void setDerivedGaugeValid(
105                                                 boolean derivedGaugeValid) {
106            this.derivedGaugeValid = derivedGaugeValid;
107        }
108        public final synchronized NumericalType getType() {
109            return type;
110        }
111        public final synchronized void setType(NumericalType type) {
112            this.type = type;
113        }
114        public final synchronized Number getPreviousScanGauge() {
115            return previousScanGauge;
116        }
117        public final synchronized void setPreviousScanGauge(
118                                                  Number previousScanGauge) {
119            this.previousScanGauge = previousScanGauge;
120        }
121        public final synchronized int getStatus() {
122            return status;
123        }
124        public final synchronized void setStatus(int status) {
125            this.status = status;
126        }
127
128        private boolean derivedGaugeValid;
129        private NumericalType type;
130        private Number previousScanGauge;
131        private int status;
132    }
133
134    /*
135     * ------------------------------------------
136     *  PRIVATE VARIABLES
137     * ------------------------------------------
138     */
139
140    /**
141     * Gauge high threshold.
142     *
143     * <BR>The default value is a null Integer object.
144     */
145    private Number highThreshold = INTEGER_ZERO;
146
147    /**
148     * Gauge low threshold.
149     *
150     * <BR>The default value is a null Integer object.
151     */
152    private Number lowThreshold = INTEGER_ZERO;
153
154    /**
155     * Flag indicating if the gauge monitor notifies when exceeding
156     * the high threshold.
157     *
158     * <BR>The default value is <CODE>false</CODE>.
159     */
160    private boolean notifyHigh = false;
161
162    /**
163     * Flag indicating if the gauge monitor notifies when exceeding
164     * the low threshold.
165     *
166     * <BR>The default value is <CODE>false</CODE>.
167     */
168    private boolean notifyLow = false;
169
170    /**
171     * Flag indicating if the gauge difference mode is used.  If the
172     * gauge difference mode is used, the derived gauge is the
173     * difference between two consecutive observed values.  Otherwise,
174     * the derived gauge is directly the value of the observed
175     * attribute.
176     *
177     * <BR>The default value is set to <CODE>false</CODE>.
178     */
179    private boolean differenceMode = false;
180
181    private static final String[] types = {
182        RUNTIME_ERROR,
183        OBSERVED_OBJECT_ERROR,
184        OBSERVED_ATTRIBUTE_ERROR,
185        OBSERVED_ATTRIBUTE_TYPE_ERROR,
186        THRESHOLD_ERROR,
187        THRESHOLD_HIGH_VALUE_EXCEEDED,
188        THRESHOLD_LOW_VALUE_EXCEEDED
189    };
190
191    private static final MBeanNotificationInfo[] notifsInfo = {
192        new MBeanNotificationInfo(
193            types,
194            "javax.management.monitor.MonitorNotification",
195            "Notifications sent by the GaugeMonitor MBean")
196    };
197
198    // Flags needed to implement the hysteresis mechanism.
199    //
200    private static final int RISING             = 0;
201    private static final int FALLING            = 1;
202    private static final int RISING_OR_FALLING  = 2;
203
204    /*
205     * ------------------------------------------
206     *  CONSTRUCTORS
207     * ------------------------------------------
208     */
209
210    /**
211     * Default constructor.
212     */
213    public GaugeMonitor() {
214    }
215
216    /*
217     * ------------------------------------------
218     *  PUBLIC METHODS
219     * ------------------------------------------
220     */
221
222    /**
223     * Starts the gauge monitor.
224     */
225    public synchronized void start() {
226        if (isActive()) {
227            MONITOR_LOGGER.log(Level.TRACE, "the monitor is already active");
228            return;
229        }
230        // Reset values.
231        //
232        for (ObservedObject o : observedObjects) {
233            final GaugeMonitorObservedObject gmo =
234                (GaugeMonitorObservedObject) o;
235            gmo.setStatus(RISING_OR_FALLING);
236            gmo.setPreviousScanGauge(null);
237        }
238        doStart();
239    }
240
241    /**
242     * Stops the gauge monitor.
243     */
244    public synchronized void stop() {
245        doStop();
246    }
247
248    // GETTERS AND SETTERS
249    //--------------------
250
251    /**
252     * Gets the derived gauge of the specified object, if this object is
253     * contained in the set of observed MBeans, or <code>null</code> otherwise.
254     *
255     * @param object the name of the MBean.
256     *
257     * @return The derived gauge of the specified object.
258     *
259     */
260    @Override
261    public synchronized Number getDerivedGauge(ObjectName object) {
262        return (Number) super.getDerivedGauge(object);
263    }
264
265    /**
266     * Gets the derived gauge timestamp of the specified object, if
267     * this object is contained in the set of observed MBeans, or
268     * <code>0</code> otherwise.
269     *
270     * @param object the name of the object whose derived gauge
271     * timestamp is to be returned.
272     *
273     * @return The derived gauge timestamp of the specified object.
274     *
275     */
276    @Override
277    public synchronized long getDerivedGaugeTimeStamp(ObjectName object) {
278        return super.getDerivedGaugeTimeStamp(object);
279    }
280
281    /**
282     * Returns the derived gauge of the first object in the set of
283     * observed MBeans.
284     *
285     * @return The derived gauge.
286     *
287     * @deprecated As of JMX 1.2, replaced by
288     * {@link #getDerivedGauge(ObjectName)}
289     */
290    @Deprecated
291    public synchronized Number getDerivedGauge() {
292        if (observedObjects.isEmpty()) {
293            return null;
294        } else {
295            return (Number) observedObjects.get(0).getDerivedGauge();
296        }
297    }
298
299    /**
300     * Gets the derived gauge timestamp of the first object in the set
301     * of observed MBeans.
302     *
303     * @return The derived gauge timestamp.
304     *
305     * @deprecated As of JMX 1.2, replaced by
306     * {@link #getDerivedGaugeTimeStamp(ObjectName)}
307     */
308    @Deprecated
309    public synchronized long getDerivedGaugeTimeStamp() {
310        if (observedObjects.isEmpty()) {
311            return 0;
312        } else {
313            return observedObjects.get(0).getDerivedGaugeTimeStamp();
314        }
315    }
316
317    /**
318     * Gets the high threshold value common to all observed MBeans.
319     *
320     * @return The high threshold value.
321     *
322     * @see #setThresholds
323     */
324    public synchronized Number getHighThreshold() {
325        return highThreshold;
326    }
327
328    /**
329     * Gets the low threshold value common to all observed MBeans.
330     *
331     * @return The low threshold value.
332     *
333     * @see #setThresholds
334     */
335    public synchronized Number getLowThreshold() {
336        return lowThreshold;
337    }
338
339    /**
340     * Sets the high and the low threshold values common to all
341     * observed MBeans.
342     *
343     * @param highValue The high threshold value.
344     * @param lowValue The low threshold value.
345     *
346     * @exception IllegalArgumentException The specified high/low
347     * threshold is null or the low threshold is greater than the high
348     * threshold or the high threshold and the low threshold are not
349     * of the same type.
350     *
351     * @see #getHighThreshold
352     * @see #getLowThreshold
353     */
354    public synchronized void setThresholds(Number highValue, Number lowValue)
355        throws IllegalArgumentException {
356
357        if ((highValue == null) || (lowValue == null)) {
358            throw new IllegalArgumentException("Null threshold value");
359        }
360
361        if (highValue.getClass() != lowValue.getClass()) {
362            throw new IllegalArgumentException("Different type " +
363                                               "threshold values");
364        }
365
366        if (isFirstStrictlyGreaterThanLast(lowValue, highValue,
367                                           highValue.getClass().getName())) {
368            throw new IllegalArgumentException("High threshold less than " +
369                                               "low threshold");
370        }
371
372        if (highThreshold.equals(highValue) && lowThreshold.equals(lowValue))
373            return;
374        highThreshold = highValue;
375        lowThreshold = lowValue;
376
377        // Reset values.
378        //
379        int index = 0;
380        for (ObservedObject o : observedObjects) {
381            resetAlreadyNotified(o, index++, THRESHOLD_ERROR_NOTIFIED);
382            final GaugeMonitorObservedObject gmo =
383                (GaugeMonitorObservedObject) o;
384            gmo.setStatus(RISING_OR_FALLING);
385        }
386    }
387
388    /**
389     * Gets the high notification's on/off switch value common to all
390     * observed MBeans.
391     *
392     * @return <CODE>true</CODE> if the gauge monitor notifies when
393     * exceeding the high threshold, <CODE>false</CODE> otherwise.
394     *
395     * @see #setNotifyHigh
396     */
397    public synchronized boolean getNotifyHigh() {
398        return notifyHigh;
399    }
400
401    /**
402     * Sets the high notification's on/off switch value common to all
403     * observed MBeans.
404     *
405     * @param value The high notification's on/off switch value.
406     *
407     * @see #getNotifyHigh
408     */
409    public synchronized void setNotifyHigh(boolean value) {
410        if (notifyHigh == value)
411            return;
412        notifyHigh = value;
413    }
414
415    /**
416     * Gets the low notification's on/off switch value common to all
417     * observed MBeans.
418     *
419     * @return <CODE>true</CODE> if the gauge monitor notifies when
420     * exceeding the low threshold, <CODE>false</CODE> otherwise.
421     *
422     * @see #setNotifyLow
423     */
424    public synchronized boolean getNotifyLow() {
425        return notifyLow;
426    }
427
428    /**
429     * Sets the low notification's on/off switch value common to all
430     * observed MBeans.
431     *
432     * @param value The low notification's on/off switch value.
433     *
434     * @see #getNotifyLow
435     */
436    public synchronized void setNotifyLow(boolean value) {
437        if (notifyLow == value)
438            return;
439        notifyLow = value;
440    }
441
442    /**
443     * Gets the difference mode flag value common to all observed MBeans.
444     *
445     * @return <CODE>true</CODE> if the difference mode is used,
446     * <CODE>false</CODE> otherwise.
447     *
448     * @see #setDifferenceMode
449     */
450    public synchronized boolean getDifferenceMode() {
451        return differenceMode;
452    }
453
454    /**
455     * Sets the difference mode flag value common to all observed MBeans.
456     *
457     * @param value The difference mode flag value.
458     *
459     * @see #getDifferenceMode
460     */
461    public synchronized void setDifferenceMode(boolean value) {
462        if (differenceMode == value)
463            return;
464        differenceMode = value;
465
466        // Reset values.
467        //
468        for (ObservedObject o : observedObjects) {
469            final GaugeMonitorObservedObject gmo =
470                (GaugeMonitorObservedObject) o;
471            gmo.setStatus(RISING_OR_FALLING);
472            gmo.setPreviousScanGauge(null);
473        }
474    }
475
476   /**
477     * Returns a <CODE>NotificationInfo</CODE> object containing the
478     * name of the Java class of the notification and the notification
479     * types sent by the gauge monitor.
480     */
481    @Override
482    public MBeanNotificationInfo[] getNotificationInfo() {
483        return notifsInfo.clone();
484    }
485
486    /*
487     * ------------------------------------------
488     *  PRIVATE METHODS
489     * ------------------------------------------
490     */
491
492    /**
493     * Updates the derived gauge attribute of the observed object.
494     *
495     * @param scanGauge The value of the observed attribute.
496     * @param o The observed object.
497     * @return <CODE>true</CODE> if the derived gauge value is valid,
498     * <CODE>false</CODE> otherwise.  The derived gauge value is
499     * invalid when the differenceMode flag is set to
500     * <CODE>true</CODE> and it is the first notification (so we
501     * haven't 2 consecutive values to update the derived gauge).
502     */
503    private synchronized boolean updateDerivedGauge(
504        Object scanGauge, GaugeMonitorObservedObject o) {
505
506        boolean is_derived_gauge_valid;
507
508        // The gauge difference mode is used.
509        //
510        if (differenceMode) {
511
512            // The previous scan gauge has been initialized.
513            //
514            if (o.getPreviousScanGauge() != null) {
515                setDerivedGaugeWithDifference((Number)scanGauge, o);
516                is_derived_gauge_valid = true;
517            }
518            // The previous scan gauge has not been initialized.
519            // We cannot update the derived gauge...
520            //
521            else {
522                is_derived_gauge_valid = false;
523            }
524            o.setPreviousScanGauge((Number)scanGauge);
525        }
526        // The gauge difference mode is not used.
527        //
528        else {
529            o.setDerivedGauge((Number)scanGauge);
530            is_derived_gauge_valid = true;
531        }
532
533        return is_derived_gauge_valid;
534    }
535
536    /**
537     * Updates the notification attribute of the observed object
538     * and notifies the listeners only once if the notify flag
539     * is set to <CODE>true</CODE>.
540     * @param o The observed object.
541     */
542    private synchronized MonitorNotification updateNotifications(
543        GaugeMonitorObservedObject o) {
544
545        MonitorNotification n = null;
546
547        // Send high notification if notifyHigh is true.
548        // Send low notification if notifyLow is true.
549        //
550        if (o.getStatus() == RISING_OR_FALLING) {
551            if (isFirstGreaterThanLast((Number)o.getDerivedGauge(),
552                                       highThreshold,
553                                       o.getType())) {
554                if (notifyHigh) {
555                    n = new MonitorNotification(
556                            THRESHOLD_HIGH_VALUE_EXCEEDED,
557                            this,
558                            0,
559                            0,
560                            "",
561                            null,
562                            null,
563                            null,
564                            highThreshold);
565                }
566                o.setStatus(FALLING);
567            } else if (isFirstGreaterThanLast(lowThreshold,
568                                              (Number)o.getDerivedGauge(),
569                                              o.getType())) {
570                if (notifyLow) {
571                    n = new MonitorNotification(
572                            THRESHOLD_LOW_VALUE_EXCEEDED,
573                            this,
574                            0,
575                            0,
576                            "",
577                            null,
578                            null,
579                            null,
580                            lowThreshold);
581                }
582                o.setStatus(RISING);
583            }
584        } else {
585            if (o.getStatus() == RISING) {
586                if (isFirstGreaterThanLast((Number)o.getDerivedGauge(),
587                                           highThreshold,
588                                           o.getType())) {
589                    if (notifyHigh) {
590                        n = new MonitorNotification(
591                                THRESHOLD_HIGH_VALUE_EXCEEDED,
592                                this,
593                                0,
594                                0,
595                                "",
596                                null,
597                                null,
598                                null,
599                                highThreshold);
600                    }
601                    o.setStatus(FALLING);
602                }
603            } else if (o.getStatus() == FALLING) {
604                if (isFirstGreaterThanLast(lowThreshold,
605                                           (Number)o.getDerivedGauge(),
606                                           o.getType())) {
607                    if (notifyLow) {
608                        n = new MonitorNotification(
609                                THRESHOLD_LOW_VALUE_EXCEEDED,
610                                this,
611                                0,
612                                0,
613                                "",
614                                null,
615                                null,
616                                null,
617                                lowThreshold);
618                    }
619                    o.setStatus(RISING);
620                }
621            }
622        }
623
624        return n;
625    }
626
627    /**
628     * Sets the derived gauge when the differenceMode flag is set to
629     * <CODE>true</CODE>.  Both integer and floating-point types are
630     * allowed.
631     *
632     * @param scanGauge The value of the observed attribute.
633     * @param o The observed object.
634     */
635    private synchronized void setDerivedGaugeWithDifference(
636        Number scanGauge, GaugeMonitorObservedObject o) {
637        Number prev = o.getPreviousScanGauge();
638        Number der;
639        switch (o.getType()) {
640        case INTEGER:
641            der = Integer.valueOf(((Integer)scanGauge).intValue() -
642                                  ((Integer)prev).intValue());
643            break;
644        case BYTE:
645            der = Byte.valueOf((byte)(((Byte)scanGauge).byteValue() -
646                                      ((Byte)prev).byteValue()));
647            break;
648        case SHORT:
649            der = Short.valueOf((short)(((Short)scanGauge).shortValue() -
650                                        ((Short)prev).shortValue()));
651            break;
652        case LONG:
653            der = Long.valueOf(((Long)scanGauge).longValue() -
654                               ((Long)prev).longValue());
655            break;
656        case FLOAT:
657            der = Float.valueOf(((Float)scanGauge).floatValue() -
658                                ((Float)prev).floatValue());
659            break;
660        case DOUBLE:
661            der = Double.valueOf(((Double)scanGauge).doubleValue() -
662                                 ((Double)prev).doubleValue());
663            break;
664        default:
665            // Should never occur...
666            MONITOR_LOGGER.log(Level.TRACE,
667                    "the threshold type is invalid");
668            return;
669        }
670        o.setDerivedGauge(der);
671    }
672
673    /**
674     * Tests if the first specified Number is greater than or equal to
675     * the last.  Both integer and floating-point types are allowed.
676     *
677     * @param greater The first Number to compare with the second.
678     * @param less The second Number to compare with the first.
679     * @param type The number type.
680     * @return <CODE>true</CODE> if the first specified Number is
681     * greater than or equal to the last, <CODE>false</CODE>
682     * otherwise.
683     */
684    private boolean isFirstGreaterThanLast(Number greater,
685                                           Number less,
686                                           NumericalType type) {
687
688        switch (type) {
689        case INTEGER:
690        case BYTE:
691        case SHORT:
692        case LONG:
693            return (greater.longValue() >= less.longValue());
694        case FLOAT:
695        case DOUBLE:
696            return (greater.doubleValue() >= less.doubleValue());
697        default:
698            // Should never occur...
699            MONITOR_LOGGER.log(Level.TRACE,
700                    "the threshold type is invalid");
701            return false;
702        }
703    }
704
705    /**
706     * Tests if the first specified Number is strictly greater than the last.
707     * Both integer and floating-point types are allowed.
708     *
709     * @param greater The first Number to compare with the second.
710     * @param less The second Number to compare with the first.
711     * @param className The number class name.
712     * @return <CODE>true</CODE> if the first specified Number is
713     * strictly greater than the last, <CODE>false</CODE> otherwise.
714     */
715    private boolean isFirstStrictlyGreaterThanLast(Number greater,
716                                                   Number less,
717                                                   String className) {
718
719        if (className.equals("java.lang.Integer") ||
720            className.equals("java.lang.Byte") ||
721            className.equals("java.lang.Short") ||
722            className.equals("java.lang.Long")) {
723
724            return (greater.longValue() > less.longValue());
725        }
726        else if (className.equals("java.lang.Float") ||
727                 className.equals("java.lang.Double")) {
728
729            return (greater.doubleValue() > less.doubleValue());
730        }
731        else {
732            // Should never occur...
733            MONITOR_LOGGER.log(Level.TRACE,
734                    "the threshold type is invalid");
735            return false;
736        }
737    }
738
739    /*
740     * ------------------------------------------
741     *  PACKAGE METHODS
742     * ------------------------------------------
743     */
744
745    /**
746     * Factory method for ObservedObject creation.
747     *
748     * @since 1.6
749     */
750    @Override
751    ObservedObject createObservedObject(ObjectName object) {
752        final GaugeMonitorObservedObject gmo =
753            new GaugeMonitorObservedObject(object);
754        gmo.setStatus(RISING_OR_FALLING);
755        gmo.setPreviousScanGauge(null);
756        return gmo;
757    }
758
759    /**
760     * This method globally sets the derived gauge type for the given
761     * "object" and "attribute" after checking that the type of the
762     * supplied observed attribute value is one of the value types
763     * supported by this monitor.
764     */
765    @Override
766    synchronized boolean isComparableTypeValid(ObjectName object,
767                                               String attribute,
768                                               Comparable<?> value) {
769        final GaugeMonitorObservedObject o =
770            (GaugeMonitorObservedObject) getObservedObject(object);
771        if (o == null)
772            return false;
773
774        // Check that the observed attribute is either of type
775        // "Integer" or "Float".
776        //
777        if (value instanceof Integer) {
778            o.setType(INTEGER);
779        } else if (value instanceof Byte) {
780            o.setType(BYTE);
781        } else if (value instanceof Short) {
782            o.setType(SHORT);
783        } else if (value instanceof Long) {
784            o.setType(LONG);
785        } else if (value instanceof Float) {
786            o.setType(FLOAT);
787        } else if (value instanceof Double) {
788            o.setType(DOUBLE);
789        } else {
790            return false;
791        }
792        return true;
793    }
794
795    @Override
796    synchronized Comparable<?> getDerivedGaugeFromComparable(
797                                                  ObjectName object,
798                                                  String attribute,
799                                                  Comparable<?> value) {
800        final GaugeMonitorObservedObject o =
801            (GaugeMonitorObservedObject) getObservedObject(object);
802        if (o == null)
803            return null;
804
805        // Update the derived gauge attributes and check the
806        // validity of the new value. The derived gauge value
807        // is invalid when the differenceMode flag is set to
808        // true and it is the first notification, i.e. we
809        // haven't got 2 consecutive values to update the
810        // derived gauge.
811        //
812        o.setDerivedGaugeValid(updateDerivedGauge(value, o));
813
814        return (Comparable<?>) o.getDerivedGauge();
815    }
816
817    @Override
818    synchronized void onErrorNotification(MonitorNotification notification) {
819        final GaugeMonitorObservedObject o = (GaugeMonitorObservedObject)
820            getObservedObject(notification.getObservedObject());
821        if (o == null)
822            return;
823
824        // Reset values.
825        //
826        o.setStatus(RISING_OR_FALLING);
827        o.setPreviousScanGauge(null);
828    }
829
830    @Override
831    synchronized MonitorNotification buildAlarmNotification(
832                                               ObjectName object,
833                                               String attribute,
834                                               Comparable<?> value) {
835        final GaugeMonitorObservedObject o =
836            (GaugeMonitorObservedObject) getObservedObject(object);
837        if (o == null)
838            return null;
839
840        // Notify the listeners if the updated derived
841        // gauge value is valid.
842        //
843        final MonitorNotification alarm;
844        if (o.getDerivedGaugeValid())
845            alarm = updateNotifications(o);
846        else
847            alarm = null;
848        return alarm;
849    }
850
851    /**
852     * Tests if the threshold high and threshold low are both of the
853     * same type as the gauge.  Both integer and floating-point types
854     * are allowed.
855     *
856     * Note:
857     *   If the optional lowThreshold or highThreshold have not been
858     *   initialized, their default value is an Integer object with
859     *   a value equal to zero.
860     *
861     * @param object The observed object.
862     * @param attribute The observed attribute.
863     * @param value The sample value.
864     * @return <CODE>true</CODE> if type is the same,
865     * <CODE>false</CODE> otherwise.
866     */
867    @Override
868    synchronized boolean isThresholdTypeValid(ObjectName object,
869                                              String attribute,
870                                              Comparable<?> value) {
871        final GaugeMonitorObservedObject o =
872            (GaugeMonitorObservedObject) getObservedObject(object);
873        if (o == null)
874            return false;
875
876        Class<? extends Number> c = classForType(o.getType());
877        return (isValidForType(highThreshold, c) &&
878                isValidForType(lowThreshold, c));
879    }
880}
881