/*
 * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package javax.management.monitor;

import static com.sun.jmx.defaults.JmxProperties.MONITOR_LOGGER;
import com.sun.jmx.mbeanserver.GetPropertyAction;
import com.sun.jmx.mbeanserver.Introspector;
import java.io.IOException;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanRegistration;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
import javax.management.NotificationBroadcasterSupport;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import static javax.management.monitor.MonitorNotification.*;

Defines the part common to all monitor MBeans. A monitor MBean monitors values of an attribute common to a set of observed MBeans. The observed attribute is monitored at intervals specified by the granularity period. A gauge value (derived gauge) is derived from the values of the observed attribute.
Since:1.5
/** * Defines the part common to all monitor MBeans. * A monitor MBean monitors values of an attribute common to a set of observed * MBeans. The observed attribute is monitored at intervals specified by the * granularity period. A gauge value (derived gauge) is derived from the values * of the observed attribute. * * * @since 1.5 */
public abstract class Monitor extends NotificationBroadcasterSupport implements MonitorMBean, MBeanRegistration { /* * ------------------------------------------ * PACKAGE CLASSES * ------------------------------------------ */ static class ObservedObject { public ObservedObject(ObjectName observedObject) { this.observedObject = observedObject; } public final ObjectName getObservedObject() { return observedObject; } public final synchronized int getAlreadyNotified() { return alreadyNotified; } public final synchronized void setAlreadyNotified(int alreadyNotified) { this.alreadyNotified = alreadyNotified; } public final synchronized Object getDerivedGauge() { return derivedGauge; } public final synchronized void setDerivedGauge(Object derivedGauge) { this.derivedGauge = derivedGauge; } public final synchronized long getDerivedGaugeTimeStamp() { return derivedGaugeTimeStamp; } public final synchronized void setDerivedGaugeTimeStamp( long derivedGaugeTimeStamp) { this.derivedGaugeTimeStamp = derivedGaugeTimeStamp; } private final ObjectName observedObject; private int alreadyNotified; private Object derivedGauge; private long derivedGaugeTimeStamp; } /* * ------------------------------------------ * PRIVATE VARIABLES * ------------------------------------------ */
Attribute to observe.
/** * Attribute to observe. */
private String observedAttribute;
Monitor granularity period (in milliseconds). The default value is set to 10 seconds.
/** * Monitor granularity period (in milliseconds). * The default value is set to 10 seconds. */
private long granularityPeriod = 10000;
Monitor state. The default value is set to false.
/** * Monitor state. * The default value is set to <CODE>false</CODE>. */
private boolean isActive = false;
Monitor sequence number. The default value is set to 0.
/** * Monitor sequence number. * The default value is set to 0. */
private final AtomicLong sequenceNumber = new AtomicLong();
Complex type attribute flag. The default value is set to false.
/** * Complex type attribute flag. * The default value is set to <CODE>false</CODE>. */
private boolean isComplexTypeAttribute = false;
First attribute name extracted from complex type attribute name.
/** * First attribute name extracted from complex type attribute name. */
private String firstAttribute;
Remaining attribute names extracted from complex type attribute name.
/** * Remaining attribute names extracted from complex type attribute name. */
private final List<String> remainingAttributes = new CopyOnWriteArrayList<String>();
AccessControlContext of the Monitor.start() caller.
/** * AccessControlContext of the Monitor.start() caller. */
private static final AccessControlContext noPermissionsACC = new AccessControlContext( new ProtectionDomain[] {new ProtectionDomain(null, null)}); private volatile AccessControlContext acc = noPermissionsACC;
Scheduler Service.
/** * Scheduler Service. */
private static final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor( new DaemonThreadFactory("Scheduler"));
Map containing the thread pool executor per thread group.
/** * Map containing the thread pool executor per thread group. */
private static final Map<ThreadPoolExecutor, Void> executors = new WeakHashMap<ThreadPoolExecutor, Void>();
Lock for executors map.
/** * Lock for executors map. */
private static final Object executorsLock = new Object();
Maximum Pool Size
/** * Maximum Pool Size */
private static final int maximumPoolSize; static { final String maximumPoolSizeSysProp = "jmx.x.monitor.maximum.pool.size"; final String maximumPoolSizeStr = AccessController.doPrivileged( new GetPropertyAction(maximumPoolSizeSysProp)); if (maximumPoolSizeStr == null || maximumPoolSizeStr.trim().length() == 0) { maximumPoolSize = 10; } else { int maximumPoolSizeTmp = 10; try { maximumPoolSizeTmp = Integer.parseInt(maximumPoolSizeStr); } catch (NumberFormatException e) { if (MONITOR_LOGGER.isLoggable(Level.FINER)) { MONITOR_LOGGER.logp(Level.FINER, Monitor.class.getName(), "<static initializer>", "Wrong value for " + maximumPoolSizeSysProp + " system property", e); MONITOR_LOGGER.logp(Level.FINER, Monitor.class.getName(), "<static initializer>", maximumPoolSizeSysProp + " defaults to 10"); } maximumPoolSizeTmp = 10; } if (maximumPoolSizeTmp < 1) { maximumPoolSize = 1; } else { maximumPoolSize = maximumPoolSizeTmp; } } }
Future associated to the current monitor task.
/** * Future associated to the current monitor task. */
private Future<?> monitorFuture;
Scheduler task to be executed by the Scheduler Service.
/** * Scheduler task to be executed by the Scheduler Service. */
private final SchedulerTask schedulerTask = new SchedulerTask();
ScheduledFuture associated to the current scheduler task.
/** * ScheduledFuture associated to the current scheduler task. */
private ScheduledFuture<?> schedulerFuture; /* * ------------------------------------------ * PROTECTED VARIABLES * ------------------------------------------ */
The amount by which the capacity of the monitor arrays are automatically incremented when their size becomes greater than their capacity.
/** * The amount by which the capacity of the monitor arrays are * automatically incremented when their size becomes greater than * their capacity. */
protected final static int capacityIncrement = 16;
The number of valid components in the vector of observed objects.
/** * The number of valid components in the vector of observed objects. * */
protected int elementCount = 0;
Monitor errors that have already been notified.
Deprecated:equivalent to alreadyNotifieds[0].
/** * Monitor errors that have already been notified. * @deprecated equivalent to {@link #alreadyNotifieds}[0]. */
@Deprecated protected int alreadyNotified = 0;

Selected monitor errors that have already been notified.

Each element in this array corresponds to an observed object in the vector. It contains a bit mask of the flags OBSERVED_OBJECT_ERROR_NOTIFIED etc, indicating whether the corresponding notification has already been sent for the MBean being monitored.

/** * <p>Selected monitor errors that have already been notified.</p> * * <p>Each element in this array corresponds to an observed object * in the vector. It contains a bit mask of the flags {@link * #OBSERVED_OBJECT_ERROR_NOTIFIED} etc, indicating whether the * corresponding notification has already been sent for the MBean * being monitored.</p> * */
protected int alreadyNotifieds[] = new int[capacityIncrement];
Reference to the MBean server. This reference is null when the monitor MBean is not registered in an MBean server. This reference is initialized before the monitor MBean is registered in the MBean server.
See Also:
  • preRegister(MBeanServer server, ObjectName name)
/** * Reference to the MBean server. This reference is null when the * monitor MBean is not registered in an MBean server. This * reference is initialized before the monitor MBean is registered * in the MBean server. * @see #preRegister(MBeanServer server, ObjectName name) */
protected MBeanServer server; // Flags defining possible monitor errors. //
This flag is used to reset the alreadyNotifieds monitor attribute.
/** * This flag is used to reset the {@link #alreadyNotifieds * alreadyNotifieds} monitor attribute. */
protected static final int RESET_FLAGS_ALREADY_NOTIFIED = 0;
Flag denoting that a notification has occurred after changing the observed object. This flag is used to check that the new observed object is registered in the MBean server at the time of the first notification.
/** * Flag denoting that a notification has occurred after changing * the observed object. This flag is used to check that the new * observed object is registered in the MBean server at the time * of the first notification. */
protected static final int OBSERVED_OBJECT_ERROR_NOTIFIED = 1;
Flag denoting that a notification has occurred after changing the observed attribute. This flag is used to check that the new observed attribute belongs to the observed object at the time of the first notification.
/** * Flag denoting that a notification has occurred after changing * the observed attribute. This flag is used to check that the * new observed attribute belongs to the observed object at the * time of the first notification. */
protected static final int OBSERVED_ATTRIBUTE_ERROR_NOTIFIED = 2;
Flag denoting that a notification has occurred after changing the observed object or the observed attribute. This flag is used to check that the observed attribute type is correct (depending on the monitor in use) at the time of the first notification.
/** * Flag denoting that a notification has occurred after changing * the observed object or the observed attribute. This flag is * used to check that the observed attribute type is correct * (depending on the monitor in use) at the time of the first * notification. */
protected static final int OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED = 4;
Flag denoting that a notification has occurred after changing the observed object or the observed attribute. This flag is used to notify any exception (except the cases described above) when trying to get the value of the observed attribute at the time of the first notification.
/** * Flag denoting that a notification has occurred after changing * the observed object or the observed attribute. This flag is * used to notify any exception (except the cases described above) * when trying to get the value of the observed attribute at the * time of the first notification. */
protected static final int RUNTIME_ERROR_NOTIFIED = 8;
This field is retained for compatibility but should not be referenced.
Deprecated:No replacement.
/** * This field is retained for compatibility but should not be referenced. * * @deprecated No replacement. */
@Deprecated protected String dbgTag = Monitor.class.getName(); /* * ------------------------------------------ * PACKAGE VARIABLES * ------------------------------------------ */
List of ObservedObjects to which the attribute to observe belongs.
/** * List of ObservedObjects to which the attribute to observe belongs. */
final List<ObservedObject> observedObjects = new CopyOnWriteArrayList<ObservedObject>();
Flag denoting that a notification has occurred after changing the threshold. This flag is used to notify any exception related to invalid thresholds settings.
/** * Flag denoting that a notification has occurred after changing * the threshold. This flag is used to notify any exception * related to invalid thresholds settings. */
static final int THRESHOLD_ERROR_NOTIFIED = 16;
Enumeration used to keep trace of the derived gauge type in counter and gauge monitors.
/** * Enumeration used to keep trace of the derived gauge type * in counter and gauge monitors. */
enum NumericalType { BYTE, SHORT, INTEGER, LONG, FLOAT, DOUBLE };
Constant used to initialize all the numeric values.
/** * Constant used to initialize all the numeric values. */
static final Integer INTEGER_ZERO = 0; /* * ------------------------------------------ * PUBLIC METHODS * ------------------------------------------ */
Allows the monitor MBean to perform any operations it needs before being registered in the MBean server.

Initializes the reference to the MBean server.

Params:
  • server – The MBean server in which the monitor MBean will be registered.
  • name – The object name of the monitor MBean.
Throws:
Returns:The name of the monitor MBean registered.
/** * Allows the monitor MBean to perform any operations it needs * before being registered in the MBean server. * <P> * Initializes the reference to the MBean server. * * @param server The MBean server in which the monitor MBean will * be registered. * @param name The object name of the monitor MBean. * * @return The name of the monitor MBean registered. * * @exception Exception */
public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception { MONITOR_LOGGER.logp(Level.FINER, Monitor.class.getName(), "preRegister(MBeanServer, ObjectName)", "initialize the reference on the MBean server"); this.server = server; return name; }
Allows the monitor MBean to perform any operations needed after having been registered in the MBean server or after the registration has failed.

Not used in this context.

/** * Allows the monitor MBean to perform any operations needed after * having been registered in the MBean server or after the * registration has failed. * <P> * Not used in this context. */
public void postRegister(Boolean registrationDone) { }
Allows the monitor MBean to perform any operations it needs before being unregistered by the MBean server.

Stops the monitor.

Throws:
  • Exception –
/** * Allows the monitor MBean to perform any operations it needs * before being unregistered by the MBean server. * <P> * Stops the monitor. * * @exception Exception */
public void preDeregister() throws Exception { MONITOR_LOGGER.logp(Level.FINER, Monitor.class.getName(), "preDeregister()", "stop the monitor"); // Stop the Monitor. // stop(); }
Allows the monitor MBean to perform any operations needed after having been unregistered by the MBean server.

Not used in this context.

/** * Allows the monitor MBean to perform any operations needed after * having been unregistered by the MBean server. * <P> * Not used in this context. */
public void postDeregister() { }
Starts the monitor.
/** * Starts the monitor. */
public abstract void start();
Stops the monitor.
/** * Stops the monitor. */
public abstract void stop(); // GETTERS AND SETTERS //--------------------
Returns the object name of the first object in the set of observed MBeans, or null if there is no such object.
See Also:
Returns:The object being observed.
Deprecated:As of JMX 1.2, replaced by getObservedObjects
/** * Returns the object name of the first object in the set of observed * MBeans, or <code>null</code> if there is no such object. * * @return The object being observed. * * @see #setObservedObject(ObjectName) * * @deprecated As of JMX 1.2, replaced by {@link #getObservedObjects} */
@Deprecated public synchronized ObjectName getObservedObject() { if (observedObjects.isEmpty()) { return null; } else { return observedObjects.get(0).getObservedObject(); } }
Removes all objects from the set of observed objects, and then adds the specified object.
Params:
  • object – The object to observe.
Throws:
See Also:
Deprecated:As of JMX 1.2, replaced by addObservedObject
/** * Removes all objects from the set of observed objects, and then adds the * specified object. * * @param object The object to observe. * @exception IllegalArgumentException The specified * object is null. * * @see #getObservedObject() * * @deprecated As of JMX 1.2, replaced by {@link #addObservedObject} */
@Deprecated public synchronized void setObservedObject(ObjectName object) throws IllegalArgumentException { if (object == null) throw new IllegalArgumentException("Null observed object"); if (observedObjects.size() == 1 && containsObservedObject(object)) return; observedObjects.clear(); addObservedObject(object); }
Adds the specified object in the set of observed MBeans, if this object is not already present.
Params:
  • object – The object to observe.
Throws:
/** * Adds the specified object in the set of observed MBeans, if this object * is not already present. * * @param object The object to observe. * @exception IllegalArgumentException The specified object is null. * */
public synchronized void addObservedObject(ObjectName object) throws IllegalArgumentException { if (object == null) { throw new IllegalArgumentException("Null observed object"); } // Check that the specified object is not already contained. // if (containsObservedObject(object)) return; // Add the specified object in the list. // ObservedObject o = createObservedObject(object); o.setAlreadyNotified(RESET_FLAGS_ALREADY_NOTIFIED); o.setDerivedGauge(INTEGER_ZERO); o.setDerivedGaugeTimeStamp(System.currentTimeMillis()); observedObjects.add(o); // Update legacy protected stuff. // createAlreadyNotified(); }
Removes the specified object from the set of observed MBeans.
Params:
  • object – The object to remove.
/** * Removes the specified object from the set of observed MBeans. * * @param object The object to remove. * */
public synchronized void removeObservedObject(ObjectName object) { // Check for null object. // if (object == null) return; final ObservedObject o = getObservedObject(object); if (o != null) { // Remove the specified object from the list. // observedObjects.remove(o); // Update legacy protected stuff. // createAlreadyNotified(); } }
Tests whether the specified object is in the set of observed MBeans.
Params:
  • object – The object to check.
Returns:true if the specified object is present, false otherwise.
/** * Tests whether the specified object is in the set of observed MBeans. * * @param object The object to check. * @return <CODE>true</CODE> if the specified object is present, * <CODE>false</CODE> otherwise. * */
public synchronized boolean containsObservedObject(ObjectName object) { return getObservedObject(object) != null; }
Returns an array containing the objects being observed.
Returns:The objects being observed.
/** * Returns an array containing the objects being observed. * * @return The objects being observed. * */
public synchronized ObjectName[] getObservedObjects() { ObjectName[] names = new ObjectName[observedObjects.size()]; for (int i = 0; i < names.length; i++) names[i] = observedObjects.get(i).getObservedObject(); return names; }
Gets the attribute being observed.
The observed attribute is not initialized by default (set to null).
See Also:
Returns:The attribute being observed.
/** * Gets the attribute being observed. * <BR>The observed attribute is not initialized by default (set to null). * * @return The attribute being observed. * * @see #setObservedAttribute */
public synchronized String getObservedAttribute() { return observedAttribute; }
Sets the attribute to observe.
The observed attribute is not initialized by default (set to null).
Params:
  • attribute – The attribute to observe.
Throws:
See Also:
/** * Sets the attribute to observe. * <BR>The observed attribute is not initialized by default (set to null). * * @param attribute The attribute to observe. * @exception IllegalArgumentException The specified * attribute is null. * * @see #getObservedAttribute */
public void setObservedAttribute(String attribute) throws IllegalArgumentException { if (attribute == null) { throw new IllegalArgumentException("Null observed attribute"); } // Update alreadyNotified array. // synchronized (this) { if (observedAttribute != null && observedAttribute.equals(attribute)) return; observedAttribute = attribute; // Reset the complex type attribute information // such that it is recalculated again. // cleanupIsComplexTypeAttribute(); int index = 0; for (ObservedObject o : observedObjects) { resetAlreadyNotified(o, index++, OBSERVED_ATTRIBUTE_ERROR_NOTIFIED | OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED); } } }
Gets the granularity period (in milliseconds).
The default value of the granularity period is 10 seconds.
See Also:
Returns:The granularity period value.
/** * Gets the granularity period (in milliseconds). * <BR>The default value of the granularity period is 10 seconds. * * @return The granularity period value. * * @see #setGranularityPeriod */
public synchronized long getGranularityPeriod() { return granularityPeriod; }
Sets the granularity period (in milliseconds).
The default value of the granularity period is 10 seconds.
Params:
  • period – The granularity period value.
Throws:
See Also:
/** * Sets the granularity period (in milliseconds). * <BR>The default value of the granularity period is 10 seconds. * * @param period The granularity period value. * @exception IllegalArgumentException The granularity * period is less than or equal to zero. * * @see #getGranularityPeriod */
public synchronized void setGranularityPeriod(long period) throws IllegalArgumentException { if (period <= 0) { throw new IllegalArgumentException("Nonpositive granularity " + "period"); } if (granularityPeriod == period) return; granularityPeriod = period; // Reschedule the scheduler task if the monitor is active. // if (isActive()) { cleanupFutures(); schedulerFuture = scheduler.schedule(schedulerTask, period, TimeUnit.MILLISECONDS); } }
Tests whether the monitor MBean is active. A monitor MBean is marked active when the start method is called. It becomes inactive when the stop method is called.
Returns:true if the monitor MBean is active, false otherwise.
/** * Tests whether the monitor MBean is active. A monitor MBean is * marked active when the {@link #start start} method is called. * It becomes inactive when the {@link #stop stop} method is * called. * * @return <CODE>true</CODE> if the monitor MBean is active, * <CODE>false</CODE> otherwise. */
/* This method must be synchronized so that the monitoring thread will correctly see modifications to the isActive variable. See the MonitorTask action executed by the Scheduled Executor Service. */ public synchronized boolean isActive() { return isActive; } /* * ------------------------------------------ * PACKAGE METHODS * ------------------------------------------ */
Starts the monitor.
/** * Starts the monitor. */
void doStart() { MONITOR_LOGGER.logp(Level.FINER, Monitor.class.getName(), "doStart()", "start the monitor"); synchronized (this) { if (isActive()) { MONITOR_LOGGER.logp(Level.FINER, Monitor.class.getName(), "doStart()", "the monitor is already active"); return; } isActive = true; // Reset the complex type attribute information // such that it is recalculated again. // cleanupIsComplexTypeAttribute(); // Cache the AccessControlContext of the Monitor.start() caller. // The monitor tasks will be executed within this context. // acc = AccessController.getContext(); // Start the scheduler. // cleanupFutures(); schedulerTask.setMonitorTask(new MonitorTask()); schedulerFuture = scheduler.schedule(schedulerTask, getGranularityPeriod(), TimeUnit.MILLISECONDS); } }
Stops the monitor.
/** * Stops the monitor. */
void doStop() { MONITOR_LOGGER.logp(Level.FINER, Monitor.class.getName(), "doStop()", "stop the monitor"); synchronized (this) { if (!isActive()) { MONITOR_LOGGER.logp(Level.FINER, Monitor.class.getName(), "doStop()", "the monitor is not active"); return; } isActive = false; // Cancel the scheduler task associated with the // scheduler and its associated monitor task. // cleanupFutures(); // Reset the AccessControlContext. // acc = noPermissionsACC; // Reset the complex type attribute information // such that it is recalculated again. // cleanupIsComplexTypeAttribute(); } }
Gets the derived gauge of the specified object, if this object is contained in the set of observed MBeans, or null otherwise.
Params:
  • object – the name of the object whose derived gauge is to be returned.
Returns:The derived gauge of the specified object.
Since:1.6
/** * Gets the derived gauge of the specified object, if this object is * contained in the set of observed MBeans, or <code>null</code> otherwise. * * @param object the name of the object whose derived gauge is to * be returned. * * @return The derived gauge of the specified object. * * @since 1.6 */
synchronized Object getDerivedGauge(ObjectName object) { final ObservedObject o = getObservedObject(object); return o == null ? null : o.getDerivedGauge(); }
Gets the derived gauge timestamp of the specified object, if this object is contained in the set of observed MBeans, or 0 otherwise.
Params:
  • object – the name of the object whose derived gauge timestamp is to be returned.
Returns:The derived gauge timestamp of the specified object.
/** * Gets the derived gauge timestamp of the specified object, if * this object is contained in the set of observed MBeans, or * <code>0</code> otherwise. * * @param object the name of the object whose derived gauge * timestamp is to be returned. * * @return The derived gauge timestamp of the specified object. * */
synchronized long getDerivedGaugeTimeStamp(ObjectName object) { final ObservedObject o = getObservedObject(object); return o == null ? 0 : o.getDerivedGaugeTimeStamp(); } Object getAttribute(MBeanServerConnection mbsc, ObjectName object, String attribute) throws AttributeNotFoundException, InstanceNotFoundException, MBeanException, ReflectionException, IOException { // Check for "ObservedAttribute" replacement. // This could happen if a thread A called setObservedAttribute() // while other thread B was in the middle of the monitor() method // and received the old observed attribute value. // final boolean lookupMBeanInfo; synchronized (this) { if (!isActive()) throw new IllegalArgumentException( "The monitor has been stopped"); if (!attribute.equals(getObservedAttribute())) throw new IllegalArgumentException( "The observed attribute has been changed"); lookupMBeanInfo = (firstAttribute == null && attribute.indexOf('.') != -1); } // Look up MBeanInfo if needed // final MBeanInfo mbi; if (lookupMBeanInfo) { try { mbi = mbsc.getMBeanInfo(object); } catch (IntrospectionException e) { throw new IllegalArgumentException(e); } } else { mbi = null; } // Check for complex type attribute // final String fa; synchronized (this) { if (!isActive()) throw new IllegalArgumentException( "The monitor has been stopped"); if (!attribute.equals(getObservedAttribute())) throw new IllegalArgumentException( "The observed attribute has been changed"); if (firstAttribute == null) { if (attribute.indexOf('.') != -1) { MBeanAttributeInfo mbaiArray[] = mbi.getAttributes(); for (MBeanAttributeInfo mbai : mbaiArray) { if (attribute.equals(mbai.getName())) { firstAttribute = attribute; break; } } if (firstAttribute == null) { String tokens[] = attribute.split("\\.", -1); firstAttribute = tokens[0]; for (int i = 1; i < tokens.length; i++) remainingAttributes.add(tokens[i]); isComplexTypeAttribute = true; } } else { firstAttribute = attribute; } } fa = firstAttribute; } return mbsc.getAttribute(object, fa); } Comparable<?> getComparableFromAttribute(ObjectName object, String attribute, Object value) throws AttributeNotFoundException { if (isComplexTypeAttribute) { Object v = value; for (String attr : remainingAttributes) v = Introspector.elementFromComplex(v, attr); return (Comparable<?>) v; } else { return (Comparable<?>) value; } } boolean isComparableTypeValid(ObjectName object, String attribute, Comparable<?> value) { return true; } String buildErrorNotification(ObjectName object, String attribute, Comparable<?> value) { return null; } void onErrorNotification(MonitorNotification notification) { } Comparable<?> getDerivedGaugeFromComparable(ObjectName object, String attribute, Comparable<?> value) { return (Comparable<?>) value; } MonitorNotification buildAlarmNotification(ObjectName object, String attribute, Comparable<?> value){ return null; } boolean isThresholdTypeValid(ObjectName object, String attribute, Comparable<?> value) { return true; } static Class<? extends Number> classForType(NumericalType type) { switch (type) { case BYTE: return Byte.class; case SHORT: return Short.class; case INTEGER: return Integer.class; case LONG: return Long.class; case FLOAT: return Float.class; case DOUBLE: return Double.class; default: throw new IllegalArgumentException( "Unsupported numerical type"); } } static boolean isValidForType(Object value, Class<? extends Number> c) { return ((value == INTEGER_ZERO) || c.isInstance(value)); }
Get the specified ObservedObject if this object is contained in the set of observed MBeans, or null otherwise.
Params:
  • object – the name of the ObservedObject to retrieve.
Returns:The ObservedObject associated to the supplied ObjectName.
Since:1.6
/** * Get the specified {@code ObservedObject} if this object is * contained in the set of observed MBeans, or {@code null} * otherwise. * * @param object the name of the {@code ObservedObject} to retrieve. * * @return The {@code ObservedObject} associated to the supplied * {@code ObjectName}. * * @since 1.6 */
synchronized ObservedObject getObservedObject(ObjectName object) { for (ObservedObject o : observedObjects) if (o.getObservedObject().equals(object)) return o; return null; }
Factory method for ObservedObject creation.
Since:1.6
/** * Factory method for ObservedObject creation. * * @since 1.6 */
ObservedObject createObservedObject(ObjectName object) { return new ObservedObject(object); }
Create the alreadyNotified array from the ObservedObject array list.
/** * Create the {@link #alreadyNotified} array from * the {@code ObservedObject} array list. */
synchronized void createAlreadyNotified() { // Update elementCount. // elementCount = observedObjects.size(); // Update arrays. // alreadyNotifieds = new int[elementCount]; for (int i = 0; i < elementCount; i++) { alreadyNotifieds[i] = observedObjects.get(i).getAlreadyNotified(); } updateDeprecatedAlreadyNotified(); }
Update the deprecated alreadyNotified field.
/** * Update the deprecated {@link #alreadyNotified} field. */
synchronized void updateDeprecatedAlreadyNotified() { if (elementCount > 0) alreadyNotified = alreadyNotifieds[0]; else alreadyNotified = 0; }
Update the alreadyNotifieds array element at the given index with the already notified flag in the given ObservedObject. Ensure the deprecated alreadyNotified field is updated if appropriate.
/** * Update the {@link #alreadyNotifieds} array element at the given index * with the already notified flag in the given {@code ObservedObject}. * Ensure the deprecated {@link #alreadyNotified} field is updated * if appropriate. */
synchronized void updateAlreadyNotified(ObservedObject o, int index) { alreadyNotifieds[index] = o.getAlreadyNotified(); if (index == 0) updateDeprecatedAlreadyNotified(); }
Check if the given bits in the given element of alreadyNotifieds are set.
/** * Check if the given bits in the given element of {@link #alreadyNotifieds} * are set. */
synchronized boolean isAlreadyNotified(ObservedObject o, int mask) { return ((o.getAlreadyNotified() & mask) != 0); }
Set the given bits in the given element of alreadyNotifieds. Ensure the deprecated alreadyNotified field is updated if appropriate.
/** * Set the given bits in the given element of {@link #alreadyNotifieds}. * Ensure the deprecated {@link #alreadyNotified} field is updated * if appropriate. */
synchronized void setAlreadyNotified(ObservedObject o, int index, int mask, int an[]) { final int i = computeAlreadyNotifiedIndex(o, index, an); if (i == -1) return; o.setAlreadyNotified(o.getAlreadyNotified() | mask); updateAlreadyNotified(o, i); }
Reset the given bits in the given element of alreadyNotifieds. Ensure the deprecated alreadyNotified field is updated if appropriate.
/** * Reset the given bits in the given element of {@link #alreadyNotifieds}. * Ensure the deprecated {@link #alreadyNotified} field is updated * if appropriate. */
synchronized void resetAlreadyNotified(ObservedObject o, int index, int mask) { o.setAlreadyNotified(o.getAlreadyNotified() & ~mask); updateAlreadyNotified(o, index); }
Reset all bits in the given element of alreadyNotifieds. Ensure the deprecated alreadyNotified field is updated if appropriate.
/** * Reset all bits in the given element of {@link #alreadyNotifieds}. * Ensure the deprecated {@link #alreadyNotified} field is updated * if appropriate. */
synchronized void resetAllAlreadyNotified(ObservedObject o, int index, int an[]) { final int i = computeAlreadyNotifiedIndex(o, index, an); if (i == -1) return; o.setAlreadyNotified(RESET_FLAGS_ALREADY_NOTIFIED); updateAlreadyNotified(o, index); }
Check if the alreadyNotifieds array has been modified. If true recompute the index for the given observed object.
/** * Check if the {@link #alreadyNotifieds} array has been modified. * If true recompute the index for the given observed object. */
synchronized int computeAlreadyNotifiedIndex(ObservedObject o, int index, int an[]) { if (an == alreadyNotifieds) { return index; } else { return observedObjects.indexOf(o); } } /* * ------------------------------------------ * PRIVATE METHODS * ------------------------------------------ */
This method is used by the monitor MBean to create and send a monitor notification to all the listeners registered for this kind of notification.
Params:
  • type – The notification type.
  • timeStamp – The notification emission date.
  • msg – The notification message.
  • derGauge – The derived gauge.
  • trigger – The threshold/string (depending on the monitor type) that triggered off the notification.
  • object – The ObjectName of the observed object that triggered off the notification.
  • onError – Flag indicating if this monitor notification is an error notification or an alarm notification.
/** * This method is used by the monitor MBean to create and send a * monitor notification to all the listeners registered for this * kind of notification. * * @param type The notification type. * @param timeStamp The notification emission date. * @param msg The notification message. * @param derGauge The derived gauge. * @param trigger The threshold/string (depending on the monitor * type) that triggered off the notification. * @param object The ObjectName of the observed object that triggered * off the notification. * @param onError Flag indicating if this monitor notification is * an error notification or an alarm notification. */
private void sendNotification(String type, long timeStamp, String msg, Object derGauge, Object trigger, ObjectName object, boolean onError) { if (!isActive()) return; if (MONITOR_LOGGER.isLoggable(Level.FINER)) { MONITOR_LOGGER.logp(Level.FINER, Monitor.class.getName(), "sendNotification", "send notification: " + "\n\tNotification observed object = " + object + "\n\tNotification observed attribute = " + observedAttribute + "\n\tNotification derived gauge = " + derGauge); } long seqno = sequenceNumber.getAndIncrement(); MonitorNotification mn = new MonitorNotification(type, this, seqno, timeStamp, msg, object, observedAttribute, derGauge, trigger); if (onError) onErrorNotification(mn); sendNotification(mn); }
This method is called by the monitor each time the granularity period has been exceeded.
Params:
  • o – The observed object.
/** * This method is called by the monitor each time * the granularity period has been exceeded. * @param o The observed object. */
private void monitor(ObservedObject o, int index, int an[]) { String attribute; String notifType = null; String msg = null; Object derGauge = null; Object trigger = null; ObjectName object; Comparable<?> value = null; MonitorNotification alarm = null; if (!isActive()) return; // Check that neither the observed object nor the // observed attribute are null. If the observed // object or observed attribute is null, this means // that the monitor started before a complete // initialization and nothing is done. // synchronized (this) { object = o.getObservedObject(); attribute = getObservedAttribute(); if (object == null || attribute == null) { return; } } // Check that the observed object is registered in the // MBean server and that the observed attribute // belongs to the observed object. // Object attributeValue = null; try { attributeValue = getAttribute(server, object, attribute); if (attributeValue == null) if (isAlreadyNotified( o, OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED)) return; else { notifType = OBSERVED_ATTRIBUTE_TYPE_ERROR; setAlreadyNotified( o, index, OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED, an); msg = "The observed attribute value is null."; MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(), "monitor", msg); } } catch (NullPointerException np_ex) { if (isAlreadyNotified(o, RUNTIME_ERROR_NOTIFIED)) return; else { notifType = RUNTIME_ERROR; setAlreadyNotified(o, index, RUNTIME_ERROR_NOTIFIED, an); msg = "The monitor must be registered in the MBean " + "server or an MBeanServerConnection must be " + "explicitly supplied."; MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(), "monitor", msg); MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(), "monitor", np_ex.toString()); } } catch (InstanceNotFoundException inf_ex) { if (isAlreadyNotified(o, OBSERVED_OBJECT_ERROR_NOTIFIED)) return; else { notifType = OBSERVED_OBJECT_ERROR; setAlreadyNotified( o, index, OBSERVED_OBJECT_ERROR_NOTIFIED, an); msg = "The observed object must be accessible in " + "the MBeanServerConnection."; MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(), "monitor", msg); MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(), "monitor", inf_ex.toString()); } } catch (AttributeNotFoundException anf_ex) { if (isAlreadyNotified(o, OBSERVED_ATTRIBUTE_ERROR_NOTIFIED)) return; else { notifType = OBSERVED_ATTRIBUTE_ERROR; setAlreadyNotified( o, index, OBSERVED_ATTRIBUTE_ERROR_NOTIFIED, an); msg = "The observed attribute must be accessible in " + "the observed object."; MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(), "monitor", msg); MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(), "monitor", anf_ex.toString()); } } catch (MBeanException mb_ex) { if (isAlreadyNotified(o, RUNTIME_ERROR_NOTIFIED)) return; else { notifType = RUNTIME_ERROR; setAlreadyNotified(o, index, RUNTIME_ERROR_NOTIFIED, an); msg = mb_ex.getMessage() == null ? "" : mb_ex.getMessage(); MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(), "monitor", msg); MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(), "monitor", mb_ex.toString()); } } catch (ReflectionException ref_ex) { if (isAlreadyNotified(o, RUNTIME_ERROR_NOTIFIED)) { return; } else { notifType = RUNTIME_ERROR; setAlreadyNotified(o, index, RUNTIME_ERROR_NOTIFIED, an); msg = ref_ex.getMessage() == null ? "" : ref_ex.getMessage(); MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(), "monitor", msg); MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(), "monitor", ref_ex.toString()); } } catch (IOException io_ex) { if (isAlreadyNotified(o, RUNTIME_ERROR_NOTIFIED)) return; else { notifType = RUNTIME_ERROR; setAlreadyNotified(o, index, RUNTIME_ERROR_NOTIFIED, an); msg = io_ex.getMessage() == null ? "" : io_ex.getMessage(); MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(), "monitor", msg); MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(), "monitor", io_ex.toString()); } } catch (RuntimeException rt_ex) { if (isAlreadyNotified(o, RUNTIME_ERROR_NOTIFIED)) return; else { notifType = RUNTIME_ERROR; setAlreadyNotified(o, index, RUNTIME_ERROR_NOTIFIED, an); msg = rt_ex.getMessage() == null ? "" : rt_ex.getMessage(); MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(), "monitor", msg); MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(), "monitor", rt_ex.toString()); } } synchronized (this) { // Check if the monitor has been stopped. // if (!isActive()) return; // Check if the observed attribute has been changed. // // Avoid race condition where mbs.getAttribute() succeeded but // another thread replaced the observed attribute meanwhile. // // Avoid setting computed derived gauge on erroneous attribute. // if (!attribute.equals(getObservedAttribute())) return; // Derive a Comparable object from the ObservedAttribute value // if the type of the ObservedAttribute value is a complex type. // if (msg == null) { try { value = getComparableFromAttribute(object, attribute, attributeValue); } catch (ClassCastException e) { if (isAlreadyNotified( o, OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED)) return; else { notifType = OBSERVED_ATTRIBUTE_TYPE_ERROR; setAlreadyNotified(o, index, OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED, an); msg = "The observed attribute value does not " + "implement the Comparable interface."; MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(), "monitor", msg); MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(), "monitor", e.toString()); } } catch (AttributeNotFoundException e) { if (isAlreadyNotified(o, OBSERVED_ATTRIBUTE_ERROR_NOTIFIED)) return; else { notifType = OBSERVED_ATTRIBUTE_ERROR; setAlreadyNotified( o, index, OBSERVED_ATTRIBUTE_ERROR_NOTIFIED, an); msg = "The observed attribute must be accessible in " + "the observed object."; MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(), "monitor", msg); MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(), "monitor", e.toString()); } } catch (RuntimeException e) { if (isAlreadyNotified(o, RUNTIME_ERROR_NOTIFIED)) return; else { notifType = RUNTIME_ERROR; setAlreadyNotified(o, index, RUNTIME_ERROR_NOTIFIED, an); msg = e.getMessage() == null ? "" : e.getMessage(); MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(), "monitor", msg); MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(), "monitor", e.toString()); } } } // Check that the observed attribute type is supported by this // monitor. // if (msg == null) { if (!isComparableTypeValid(object, attribute, value)) { if (isAlreadyNotified( o, OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED)) return; else { notifType = OBSERVED_ATTRIBUTE_TYPE_ERROR; setAlreadyNotified(o, index, OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED, an); msg = "The observed attribute type is not valid."; MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(), "monitor", msg); } } } // Check that threshold type is supported by this monitor. // if (msg == null) { if (!isThresholdTypeValid(object, attribute, value)) { if (isAlreadyNotified(o, THRESHOLD_ERROR_NOTIFIED)) return; else { notifType = THRESHOLD_ERROR; setAlreadyNotified(o, index, THRESHOLD_ERROR_NOTIFIED, an); msg = "The threshold type is not valid."; MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(), "monitor", msg); } } } // Let someone subclassing the monitor to perform additional // monitor consistency checks and report errors if necessary. // if (msg == null) { msg = buildErrorNotification(object, attribute, value); if (msg != null) { if (isAlreadyNotified(o, RUNTIME_ERROR_NOTIFIED)) return; else { notifType = RUNTIME_ERROR; setAlreadyNotified(o, index, RUNTIME_ERROR_NOTIFIED, an); MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(), "monitor", msg); } } } // If no errors were found then clear all error flags and // let the monitor decide if a notification must be sent. // if (msg == null) { // Clear all already notified flags. // resetAllAlreadyNotified(o, index, an); // Get derived gauge from comparable value. // derGauge = getDerivedGaugeFromComparable(object, attribute, value); o.setDerivedGauge(derGauge); o.setDerivedGaugeTimeStamp(System.currentTimeMillis()); // Check if an alarm must be fired. // alarm = buildAlarmNotification(object, attribute, (Comparable<?>) derGauge); } } // Notify monitor errors // if (msg != null) sendNotification(notifType, System.currentTimeMillis(), msg, derGauge, trigger, object, true); // Notify monitor alarms // if (alarm != null && alarm.getType() != null) sendNotification(alarm.getType(), System.currentTimeMillis(), alarm.getMessage(), derGauge, alarm.getTrigger(), object, false); }
Cleanup the scheduler and monitor tasks futures.
/** * Cleanup the scheduler and monitor tasks futures. */
private synchronized void cleanupFutures() { if (schedulerFuture != null) { schedulerFuture.cancel(false); schedulerFuture = null; } if (monitorFuture != null) { monitorFuture.cancel(false); monitorFuture = null; } }
Cleanup the "is complex type attribute" info.
/** * Cleanup the "is complex type attribute" info. */
private synchronized void cleanupIsComplexTypeAttribute() { firstAttribute = null; remainingAttributes.clear(); isComplexTypeAttribute = false; }
SchedulerTask nested class: This class implements the Runnable interface. The SchedulerTask is executed periodically with a given fixed delay by the Scheduled Executor Service.
/** * SchedulerTask nested class: This class implements the Runnable interface. * * The SchedulerTask is executed periodically with a given fixed delay by * the Scheduled Executor Service. */
private class SchedulerTask implements Runnable { private MonitorTask task; /* * ------------------------------------------ * CONSTRUCTORS * ------------------------------------------ */ public SchedulerTask() { } /* * ------------------------------------------ * GETTERS/SETTERS * ------------------------------------------ */ public void setMonitorTask(MonitorTask task) { this.task = task; } /* * ------------------------------------------ * PUBLIC METHODS * ------------------------------------------ */ public void run() { synchronized (Monitor.this) { Monitor.this.monitorFuture = task.submit(); } } }
MonitorTask nested class: This class implements the Runnable interface. The MonitorTask is executed periodically with a given fixed delay by the Scheduled Executor Service.
/** * MonitorTask nested class: This class implements the Runnable interface. * * The MonitorTask is executed periodically with a given fixed delay by the * Scheduled Executor Service. */
private class MonitorTask implements Runnable { private ThreadPoolExecutor executor; /* * ------------------------------------------ * CONSTRUCTORS * ------------------------------------------ */ public MonitorTask() { // Find out if there's already an existing executor for the calling // thread and reuse it. Otherwise, create a new one and store it in // the executors map. If there is a SecurityManager, the group of // System.getSecurityManager() is used, else the group of the thread // instantiating this MonitorTask, i.e. the group of the thread that // calls "Monitor.start()". SecurityManager s = System.getSecurityManager(); ThreadGroup group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); synchronized (executorsLock) { for (ThreadPoolExecutor e : executors.keySet()) { DaemonThreadFactory tf = (DaemonThreadFactory) e.getThreadFactory(); ThreadGroup tg = tf.getThreadGroup(); if (tg == group) { executor = e; break; } } if (executor == null) { executor = new ThreadPoolExecutor( maximumPoolSize, maximumPoolSize, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new DaemonThreadFactory("ThreadGroup<" + group.getName() + "> Executor", group)); executor.allowCoreThreadTimeOut(true); executors.put(executor, null); } } } /* * ------------------------------------------ * PUBLIC METHODS * ------------------------------------------ */ public Future<?> submit() { return executor.submit(this); } public void run() { final ScheduledFuture<?> sf; final AccessControlContext ac; synchronized (Monitor.this) { sf = Monitor.this.schedulerFuture; ac = Monitor.this.acc; } PrivilegedAction<Void> action = new PrivilegedAction<Void>() { public Void run() { if (Monitor.this.isActive()) { final int an[] = alreadyNotifieds; int index = 0; for (ObservedObject o : Monitor.this.observedObjects) { if (Monitor.this.isActive()) { Monitor.this.monitor(o, index++, an); } } } return null; } }; if (ac == null) { throw new SecurityException("AccessControlContext cannot be null"); } AccessController.doPrivileged(action, ac); synchronized (Monitor.this) { if (Monitor.this.isActive() && Monitor.this.schedulerFuture == sf) { Monitor.this.monitorFuture = null; Monitor.this.schedulerFuture = scheduler.schedule(Monitor.this.schedulerTask, Monitor.this.getGranularityPeriod(), TimeUnit.MILLISECONDS); } } } }
Daemon thread factory used by the monitor executors.

This factory creates all new threads used by an Executor in the same ThreadGroup. If there is a SecurityManager, it uses the group of System.getSecurityManager(), else the group of the thread instantiating this DaemonThreadFactory. Each new thread is created as a daemon thread with priority Thread.NORM_PRIORITY. New threads have names accessible via Thread.getName() of "JMX Monitor <pool-name> Pool [Thread-M]", where M is the sequence number of the thread created by this factory.

/** * Daemon thread factory used by the monitor executors. * <P> * This factory creates all new threads used by an Executor in * the same ThreadGroup. If there is a SecurityManager, it uses * the group of System.getSecurityManager(), else the group of * the thread instantiating this DaemonThreadFactory. Each new * thread is created as a daemon thread with priority * Thread.NORM_PRIORITY. New threads have names accessible via * Thread.getName() of "{@literal JMX Monitor <pool-name> Pool [Thread-M]}", * where M is the sequence number of the thread created by this * factory. */
private static class DaemonThreadFactory implements ThreadFactory { final ThreadGroup group; final AtomicInteger threadNumber = new AtomicInteger(1); final String namePrefix; static final String nameSuffix = "]"; public DaemonThreadFactory(String poolName) { SecurityManager s = System.getSecurityManager(); group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); namePrefix = "JMX Monitor " + poolName + " Pool [Thread-"; } public DaemonThreadFactory(String poolName, ThreadGroup threadGroup) { group = threadGroup; namePrefix = "JMX Monitor " + poolName + " Pool [Thread-"; } public ThreadGroup getThreadGroup() { return group; } public Thread newThread(Runnable r) { Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement() + nameSuffix, 0); t.setDaemon(true); if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY); return t; } } }