Class SlowQueryReportJmx

All Implemented Interfaces:
InvocationHandler, NotificationBroadcaster, NotificationEmitter, SlowQueryReportJmxMBean

public class SlowQueryReportJmx extends SlowQueryReport implements NotificationEmitter, SlowQueryReportJmxMBean
JMX-enabled slow query report interceptor. Publishes slow and failed query statistics to JMX and provides notifications when slow or failed queries are detected.

The MBean and the notification broadcaster are published for the pool name: the first interceptor instance to borrow a connection registers them, and every other instance sharing the name - the other connections of that pool, the second interceptor of the recommended two-thresholds configuration, or a second pool configured with the same name - reports through them. They stay published until the last live pool registered under the name closes; closing one of several same-named pools leaves the registration with the survivors. A pool that starts under the name after that last close has decided its release - racing it - adopts the registration still published under the name, and the pending release notices the takeover and leaves the registration with the new pool.

A JMX client's listener subscription belongs to the registration it was added through. JMX cannot carry a subscription across an unregistration, so after the last pool of a name has closed, a client must attach its listeners again to receive notifications from a pool started under that name anew.

  • Field Details

    • SLOW_QUERY_NOTIFICATION

      public static final String SLOW_QUERY_NOTIFICATION
      Notification type for slow query events.
      See Also:
    • FAILED_QUERY_NOTIFICATION

      public static final String FAILED_QUERY_NOTIFICATION
      Notification type for failed query events.
      See Also:
    • objectNameAttribute

      public static final String objectNameAttribute
      Property key for overriding the JMX object name.
      See Also:
    • SLOW_QUERY_TYPE

      protected static volatile CompositeType SLOW_QUERY_TYPE
      Composite type used for JMX query statistics data.
    • mbeans

      protected static final ConcurrentHashMap<String, SlowQueryReportJmxMBean> mbeans
      Registry of JMX MBeans for each connection pool.
    • notifiers

      protected static final ConcurrentHashMap<String, NotificationBroadcasterSupport> notifiers
      Notification broadcasters for each connection pool. Interceptor instances are created per pooled connection, but JMX clients attach their listeners to the single MBean registered per pool, so notifications raised by any instance of a pool must reach the broadcaster of the pool rather than a broadcaster private to the instance.
    • registrationFailuresLogged

      protected static final ConcurrentHashMap<String, ObjectName> registrationFailuresLogged
      Registry of the first registration failure seen for each pool name, holding the ObjectName that failed. Registration is retried by every borrow of such a pool, but only the first failure of a name is logged with its exception, so a persistent refusal - a foreign MBean holding the name, a security restriction - is not reported with a stack trace once per borrow. The marker is dropped when a registration under the name succeeds and when the name's release runs through deregisterJmx(), so a later failure cycle is reported loudly once again.
    • registeredUnder

      protected volatile ObjectName registeredUnder
      The ObjectName this instance published itself under. registerJmx() records it on the instance that took the registry slot, before the publish, and clears it again when the MBeanServer refused the MBean. The release in deregisterJmx() unregisters exactly the name recorded on the registration the registry held, whatever class and objectName property the releasing instance carries - a closing pool delivers a freshly instantiated interceptor whose definitions need not match those of the pool that registered.
    • notifier

      protected volatile NotificationBroadcasterSupport notifier
      Notification broadcaster used before the pool name is known; once the pool name is known, getNotifier() returns the broadcaster shared by all interceptor instances of that pool.
    • poolName

      protected String poolName
      The name of the connection pool.
    • notifySequence

      protected static final AtomicLong notifySequence
      Atomic sequence counter for notifications.
    • notifyPool

      protected boolean notifyPool
      Whether to send notifications to the pool.
    • pool

      protected ConnectionPool pool
      The connection pool reference.
  • Constructor Details

    • SlowQueryReportJmx

      public SlowQueryReportJmx()
      Creates a new SlowQueryReportJmx instance.
  • Method Details

    • getNotifier

      protected NotificationBroadcasterSupport getNotifier()
      Returns the notification broadcaster that all interceptor instances of the current pool share, so that notifications raised on any connection reach the listeners registered with the pool's MBean.
      Returns:
      the shared broadcaster for this interceptor's pool
    • addNotificationListener

      public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws IllegalArgumentException
      Adds a notification listener.
      Specified by:
      addNotificationListener in interface NotificationBroadcaster
      Parameters:
      listener - the listener to add
      filter - the notification filter
      handback - the handback object
      Throws:
      IllegalArgumentException - if the listener is null
    • getNotificationInfo

      public MBeanNotificationInfo[] getNotificationInfo()
      Returns information about the notifications this MBean can send.
      Specified by:
      getNotificationInfo in interface NotificationBroadcaster
      Returns:
      an array of MBeanNotificationInfo objects
    • removeNotificationListener

      public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException
      Removes a notification listener.
      Specified by:
      removeNotificationListener in interface NotificationBroadcaster
      Parameters:
      listener - the listener to remove
      Throws:
      ListenerNotFoundException - if the listener was not previously added
    • removeNotificationListener

      public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException
      Removes a notification listener with the given filter and handback.
      Specified by:
      removeNotificationListener in interface NotificationEmitter
      Parameters:
      listener - the listener to remove
      filter - the notification filter
      handback - the handback object
      Throws:
      ListenerNotFoundException - if the listener was not previously added
    • getCompositeType

      protected static CompositeType getCompositeType()
      Returns the composite type used for JMX query statistics.
      Returns:
      the composite type, or null if initialization failed
    • reset

      public void reset(ConnectionPool parent, PooledConnection con)
      Resets this interceptor for a new connection, registering JMX if applicable.
      Overrides:
      reset in class SlowQueryReport
      Parameters:
      parent - the connection pool
      con - the pooled connection
    • poolClosed

      public void poolClosed(ConnectionPool pool)
      Called when the connection pool is closed; releases the JMX registration of the pool's name - the MBean and the broadcaster its clients' listeners are attached to - only once that pool was the last live pool registered under the name. Both belong to the name and are shared by every live pool of that name, so taking them down while a survivor is still running would orphan the survivor's monitoring; releasing them on every close but a decided last one would unregister a shared registration that other closing or live pools still rely on. Whether this close is the last one is decided atomically by SlowQueryReport.releaseStatistics(ConnectionPool) on the way through super.poolClosed, so concurrent closes of same-named pools cannot all see a survivor and leave the name registered for ever.

      The decision and the release are separate steps, and a pool that starts under the name between them has taken the name - and the registration still published under it - back; releaseJmxRegistration(ConnectionPool) therefore re-verifies the name before anything is taken down.

      Overrides:
      poolClosed in class SlowQueryReport
      Parameters:
      pool - the connection pool being closed
    • releaseJmxRegistration

      protected void releaseJmxRegistration(ConnectionPool releasedPool)
      Completes the release of the current pool name whose statistics the given pool has just freed: drops the MBean registration with the name's broadcaster and registration-failure marker through deregisterJmx() - unless a live pool is registered under the name again that does not include the releasing one. A pool that started under the name after the release was decided has taken the name over, adopting the registration still published under it, and it - not this close - owns the name's JMX state from then on; the last live pool of the name is the one to release it.

      The statistics are read the plain way here, without a lock, deliberately: a check computed inside a ConcurrentHashMap.compute on the name would be atomic against a start but would also make that start wait behind the release, and a release must never block a pool that is coming up. Without the lock the check cannot see a start that begins after it has read - the deregisterJmx() that follows then still takes down only the registration the registry held at its own read, never a fresher one, and only the name's broadcaster and failure marker can be hit early by a start landing inside that window; see deregisterJmx(). The one case the value guard does not spare is a start whose fresh registration lands in the slot between the read here and the read in deregisterJmx(): that release takes down the value it read, leaving the newcomer to republish on its next borrow - a window narrower than the one that existed before this guard, measured in the gap between two map reads. The guard likewise proceeds when the statistics still hold the very pool that is closing, which only happens when the same pool instance is restarted while this close is still in flight; the restarted generation then republishes its registration on its first borrow, like any pool whose registration was released.

      Parameters:
      releasedPool - the pool whose statistics release decided this release of the name
    • poolStarted

      public void poolStarted(ConnectionPool pool)
      Called when the connection pool is started.
      Overrides:
      poolStarted in class SlowQueryReport
      Parameters:
      pool - the connection pool being started
    • reportFailedQuery

      protected String reportFailedQuery(String query, Object[] args, String name, long start, long delta, Throwable t)
      Reports a failed query and sends a JMX notification if logging is enabled.
      Overrides:
      reportFailedQuery in class SlowQueryReport
      Parameters:
      query - the SQL query
      args - the query arguments
      name - the name of the method used to execute
      start - the wall clock time the query execution started
      delta - the time the execution took before it failed, measured with the monotonic clock
      t - the throwable that caused the failure
      Returns:
      the formatted query string
    • notifyJmx

      protected void notifyJmx(String query, String type)
      Sends a JMX notification for the given query and notification type.
      Parameters:
      query - the SQL query string
      type - the notification type (slow or failed query)
    • getNotificationSource

      protected Object getNotificationSource()
      Returns the source this MBean's notifications are sent with.

      Notification marshals its source, so a listener outside this JVM - one reached through a JMX connector, or one that hands the notification on to another process - only receives the notification at all if the source is serializable. This MBean is not, and neither is the pool it reports on, so the source is the ObjectName the MBean is registered under, which names the pool and is serializable. Interceptor instances are created per pooled connection and only one of them is the registered MBean's resource, so an instance cannot name itself: every instance of a pool name sends the very name its registration was published under. An instance whose name cannot be formed - a malformed objectName property or a pool name that cannot be quoted into an ObjectName - was never registered at all, so no listener reached through a connector can exist for it; the source is then a string of the default name's shape, which still names the pool for the local listeners the broadcaster serves.

      Returns:
      the ObjectName the registration of this interceptor's pool name is published under, or, when that name cannot be formed, a string of the default name's shape
    • reportSlowQuery

      protected String reportSlowQuery(String query, Object[] args, String name, long start, long delta)
      Reports a slow query and sends a JMX notification if logging is enabled.
      Overrides:
      reportSlowQuery in class SlowQueryReport
      Parameters:
      query - the SQL query
      args - the query arguments
      name - the connection pool name
      start - the start time in milliseconds
      delta - the elapsed time in milliseconds
      Returns:
      the formatted query string
    • getPoolNames

      public String[] getPoolNames()
      JMX operation - return the names of all the pools
      Specified by:
      getPoolNames in interface SlowQueryReportJmxMBean
      Returns:
      - all the names of pools that we have stored data for, which is a view of the JVM-wide statistics registry rather than of this pool alone
    • getPoolName

      public String getPoolName()
      JMX operation - return the name of the pool
      Specified by:
      getPoolName in interface SlowQueryReportJmxMBean
      Returns:
      the name of the pool, unique within the JVM, or null before this instance is bound to a pool
    • isNotifyPool

      public boolean isNotifyPool()
      Returns whether notifications are sent to the pool.
      Returns:
      true if notifications are sent to the pool
    • setNotifyPool

      public void setNotifyPool(boolean notifyPool)
      Sets whether notifications are sent to the pool. Configuration entry point only, applied to every interceptor instance of a pool by setProperties while its connection chain is built: it is not part of the JMX management interface, because writing it through the registered MBean would reach only the one instance that owns the registration and leave the pool's other connections routing as before. See SlowQueryReportJmxMBean.
      Parameters:
      notifyPool - true if notifications should be sent to the pool
    • resetStats

      public void resetStats()
      JMX operation - remove all stats for this connection pool. The statistics are keyed by pool name and shared by every live pool configured with that name, so clearing them through one pool's MBean clears them for all of those pools. Only the recorded content goes: the statistics map stays registered with the name - every live instance holds that very map and keeps accumulating into it.
      Specified by:
      resetStats in interface SlowQueryReportJmxMBean
    • getSlowQueriesCD

      public CompositeData[] getSlowQueriesCD() throws OpenDataException
      JMX operation - returns all the queries we have collected.
      Specified by:
      getSlowQueriesCD in interface SlowQueryReportJmxMBean
      Returns:
      - the slow query report as composite data; an empty array when the pool name holds no statistics map - none was ever registered under it, or the last live pool of the name has closed and freed it
      Throws:
      OpenDataException - if the composite data cannot be created; a JMX client gets it wrapped in an MBeanException
    • deregisterJmx

      protected void deregisterJmx()
      Releases the MBean registration and the shared broadcaster held for the current pool name - even when the pool name is still owned by live pools. Normal pool shutdown releases through super.poolClosed plus SlowQueryReport.lastPoolOfNameClosed in poolClosed(ConnectionPool), which keeps the registration for any surviving same-named pool and skips the release when a new pool has taken the name back (releaseJmxRegistration(ConnectionPool)).

      The registry slot is dropped only while it still holds the registration the registry was read at: a registration a pool installing itself behind this release puts into the slot belongs to that pool and keeps its MBean published. The unregistration uses the ObjectName that registration recorded when it was published, so it never depends on the class or the objectName property of the instance running this release - which a closing pool delivers freshly, and which need not share either with the registration it releases.

      The broadcaster and the registration-failure marker belong to the name as much as the MBean does, so the release drops them even when this pool never held the registry slot: a pool whose every registration attempt was refused still reported through the broadcaster, and its throttle marker must not outlive the name and hush the first failure of a later failure cycle under the name. Both entries are created lazily - by getNotifier() and by listeners a client adds through interceptor handles it still holds - so a client subscribing to the released name concurrently with this release can have its late subscription re-insert an entry nothing registered under the name uses any more; a fresh registration under the name re-uses or replaces it.

    • registrationObjectName

      protected ObjectName registrationObjectName(SlowQueryReportJmxMBean registered) throws MalformedObjectNameException
      Returns the ObjectName the given registry registration published itself under, as the registry owner recorded it when registerJmx() took the slot. Unregistering this - rather than a name recomputed from the class and properties of the instance running the release - is what keeps the unregistration on the name the MBean actually occupies: two pools configured with one name may carry interceptor definitions that compute different ObjectNames, and the release run by one of them must not give up on the name the other one's registration was published under and leave it registered for ever.
      Parameters:
      registered - the registry value this release took out of the registry
      Returns:
      the ObjectName to unregister
      Throws:
      MalformedObjectNameException - if a fallback ObjectName cannot be formed
    • getObjectName

      public ObjectName getObjectName(Class<?> clazz, String poolName) throws MalformedObjectNameException
      Returns the JMX ObjectName for the given class and pool name.
      Parameters:
      clazz - the interceptor class
      poolName - the name of the connection pool
      Returns:
      the JMX ObjectName
      Throws:
      MalformedObjectNameException - if the object name is malformed
    • registerJmx

      protected void registerJmx()
      Registers this interceptor with JMX.

      The registration is attempted under the protection of the per-name registry slot: the slot is taken with putIfAbsent so only one instance per pool name registers, and it is released again when the MBeanServer refuses the MBean. A failed publish must not keep the slot, because the slot is the only registration gate: every later borrow would find it occupied and skip the registration, so the pool would claim an MBean for its whole life that was never published - and would not re-register even after whatever made the MBeanServer refuse it is gone. Releasing the slot lets a later borrow retry; JmxUtil.registerJmx cannot be used for the attempt because it swallows every exception and returns a name whatever happened, so the failure of its own call is unobservable to the caller. Only the first refusal of a name is logged with its exception, so a persistent refusal is not reported once per borrow.

    • setProperties

      public void setProperties(Map<String, PoolProperties.InterceptorProperty> properties)
      Sets the interceptor properties, including the notifyPool flag.
      Overrides:
      setProperties in class SlowQueryReport
      Parameters:
      properties - the interceptor properties