6297N/A * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. 0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 0N/A * This code is free software; you can redistribute it and/or modify it 0N/A * under the terms of the GNU General Public License version 2 only, as 2362N/A * published by the Free Software Foundation. Oracle designates this 0N/A * particular file as subject to the "Classpath" exception as provided 2362N/A * by Oracle in the LICENSE file that accompanied this code. 0N/A * This code is distributed in the hope that it will be useful, but WITHOUT 0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 0N/A * version 2 for more details (a copy is included in the LICENSE file that 0N/A * accompanied this code). 0N/A * You should have received a copy of the GNU General Public License version 0N/A * 2 along with this work; if not, write to the Free Software Foundation, 0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2362N/A * or visit www.oracle.com if you need additional information or have any 0N/A/** A circular buffer of notifications received from an MBean server. */ 0N/A There is one instance of ArrayNotificationBuffer for every 0N/A MBeanServer object that has an attached ConnectorServer. Then, for 0N/A every ConnectorServer attached to a given MBeanServer, there is an 0N/A instance of the inner class ShareBuffer. So for example with two 0N/A ConnectorServers it looks like this: 0N/A ConnectorServer1 -> ShareBuffer1 -\ 0N/A }-> ArrayNotificationBuffer 0N/A ConnectorServer2 -> ShareBuffer2 -/ | 0N/A The ArrayNotificationBuffer has a circular buffer of 0N/A NamedNotification objects. Each ConnectorServer defines a 0N/A notification buffer size, and this size is recorded by the 0N/A corresponding ShareBuffer. The buffer size of the 0N/A ArrayNotificationBuffer is the maximum of all of its ShareBuffers. 0N/A When a ShareBuffer is added or removed, the ArrayNotificationBuffer 0N/A size is adjusted accordingly. 0N/A An ArrayNotificationBuffer also has a BufferListener (which is a 0N/A NotificationListener) registered on every NotificationBroadcaster 0N/A MBean in the MBeanServer to which it is attached. The cost of this 0N/A potentially large set of listeners is the principal motivation for 0N/A sharing the ArrayNotificationBuffer between ConnectorServers, and 0N/A also the reason that we are careful to discard the 0N/A ArrayNotificationBuffer (and its BufferListeners) when there are no 0N/A longer any ConnectorServers using it. 0N/A The synchronization of this class is inherently complex. In an attempt 0N/A to limit the complexity, we use just two locks: 0N/A - globalLock controls access to the mapping between an MBeanServer 0N/A and its ArrayNotificationBuffer and to the set of ShareBuffers for 0N/A each ArrayNotificationBuffer. 0N/A - the instance lock of each ArrayNotificationBuffer controls access 0N/A to the array of notifications, including its size, and to the 0N/A mechanism is used to indicate changes to the array. 0N/A If both locks are held at the same time, the globalLock must be 0N/A Since adding or removing a BufferListener to an MBean can involve 0N/A calling user code, we are careful not to hold any locks while it is 0N/A // FACTORY STUFF, INCLUDING SHARING 0N/A private static final 0N/A //Find out queue size 0N/A /* We avoid holding any locks while calling createListeners. 0N/A * This prevents possible deadlocks involving user code, but 0N/A * does mean that a second ConnectorServer created and started 0N/A * in this window will return before all the listeners are ready, 0N/A * which could lead to surprising behaviour. The alternative 0N/A * would be to block the second ConnectorServer until the first 0N/A * one has finished adding all the listeners, but that would then 0N/A * be subject to deadlock. 0N/A /* Ensure that this buffer is no longer the one that will be returned by 0N/A * getNotificationBuffer. This method is idempotent - calling it more 0N/A * than once has no effect beyond that of calling it once. 0N/A synchronized (
this) {
0N/A synchronized (
this) {
0N/A // Notify potential waiting fetchNotification call 0N/A // ARRAYNOTIFICATIONBUFFER IMPLEMENTATION 0N/A // We no longer support calling this method from outside. 0N/A // The JDK doesn't contain any such calls and users are not 0N/A // supposed to be accessing this class. 0N/A * <p>Fetch notifications that match the given listeners.</p> 0N/A * <p>The operation only considers notifications with a sequence 0N/A * number at least <code>startSequenceNumber</code>. It will take 0N/A * no longer than <code>timeout</code>, and will return no more 0N/A * than <code>maxNotifications</code> different notifications.</p> 0N/A * <p>If there are no notifications matching the criteria, the 0N/A * operation will block until one arrives, subject to the 0N/A * @param filter an object that will add notifications to a 0N/A * {@code List<TargetedNotification>} if they match the current 0N/A * listeners with their filters. 0N/A * @param startSequenceNumber the first sequence number to 0N/A * @param timeout the maximum time to wait. May be 0 to indicate 0N/A * not to wait if there are no notifications. 0N/A * @param maxNotifications the maximum number of notifications to 0N/A * return. May be 0 to indicate a wait for eligible notifications 0N/A * that will return a usable <code>nextSequenceNumber</code>. The 0N/A * {@link TargetedNotification} array in the returned {@link 0N/A * NotificationResult} may contain more than this number of 0N/A * elements but will not contain more than this number of 0N/A * different notifications. 0N/A synchronized(
this) {
0N/A // Check arg validity 0N/A /* Determine the end time corresponding to the timeout value. 0N/A Caller may legitimately supply Long.MAX_VALUE to indicate no 0N/A timeout. In that case the addition will overflow and produce 0N/A a negative end time. Set end time to Long.MAX_VALUE in that 0N/A case. We assume System.currentTimeMillis() is positive. */ 0N/A /* We set earliestSeq the first time through the loop. If we 0N/A set it here, notifications could be dropped before we 0N/A started examining them, so earliestSeq might not correspond 0N/A to the earliest notification we examined. */ 0N/A /* On exit from this loop, notifs, earliestSeq, and nextSeq must 0N/A all be correct values for the returned NotificationResult. */ 0N/A /* Get the next available notification regardless of filters, 0N/A or wait for one to arrive if there is none. */ 0N/A synchronized (
this) {
0N/A /* First time through. The current earliestSequenceNumber 0N/A is the first one we could have examined. */ 0N/A "nextSeq=earliestSeq");
0N/A /* If many notifications have been dropped since the 0N/A last time through, nextSeq could now be earlier 0N/A than the current earliest. If so, notifications 0N/A may have been lost and we return now so the caller 0N/A can see this next time it calls. */ 6297N/A // Skip security check if NotificationBufferFilter is not overloaded 0N/A /* nextSeq is the largest sequence number. If we 0N/A already got notifications, return them now. 0N/A Otherwise wait for some to arrive, with 0N/A "no more notifs but have some so don't wait");
0N/A /* dispose called */ 0N/A "dispose callled, no wait");
0N/A /* We have a candidate notification. See if it matches 0N/A our filters. We do this outside the synchronized block 0N/A so we don't hold up everyone accessing the buffer 0N/A (including notification senders) while we evaluate 0N/A potentially slow filters. */ 0N/A "applying filter to candidate");
0N/A /* We only check the max size now, so that our 0N/A returned nextSeq is as large as possible. This 0N/A prevents the caller from thinking it missed 0N/A interesting notifications when in fact we knew they 0N/A "reached maxNotifications");
0N/A /* Construct and return the result. */ 0N/A "dropped oldest notif, earliestSeq=" +
0N/A * Add our listener to every NotificationBroadcaster MBean 0N/A * currently in the MBean server and to every 0N/A * NotificationBroadcaster later created. 0N/A * It would be really nice if we could just do 0N/A * mbs.addNotificationListener(new ObjectName("*:*"), ...); 0N/A * Definitely something for the next version of JMX. 0N/A * There is a nasty race condition that we must handle. We 0N/A * first register for MBean-creation notifications so we can add 0N/A * listeners to new MBeans, then we query the existing MBeans to 0N/A * add listeners to them. The problem is that a new MBean could 0N/A * arrive after we register for creations but before the query has 0N/A * completed. Then we could see the MBean both in the query and 0N/A * in an MBean-creation notification, and we would end up 0N/A * registering our listener twice. 0N/A * To solve this problem, we arrange for new MBeans that arrive 0N/A * while the query is being done to be added to the Set createdDuringQuery 0N/A * and we do not add a listener immediately. When the query is done, 0N/A * we atomically turn off the addition of new names to createdDuringQuery 0N/A * and add all the names that were there to the result of the query. 0N/A * Since we are dealing with Sets, the result is the same whether or not 0N/A * the newly-created MBean was included in the query result. 0N/A * It is important not to hold any locks during the operation of adding 0N/A * listeners to MBeans. An MBean's addNotificationListener can be 0N/A * arbitrary user code, and this could deadlock with any locks we hold 0N/A * (see bug 6239400). The corollary is that we must not do any operations 0N/A * in this method or the methods it calls that require locks. 0N/A synchronized (
this) {
0N/A final String msg =
"Can't add listener to MBean server delegate: ";
0N/A /* Spec doesn't say whether Set returned by QueryNames can be modified 0N/A synchronized (
this) {
0N/A /* This can happen if the MBean was unregistered just 0N/A after the query. Or user NotificationBroadcaster might 0N/A throw unexpected exception. */ 0N/A /* This method must not be synchronized. See the comment on the 0N/A * createListeners method. 0N/A * The notification could arrive after our buffer has been destroyed 0N/A * or even during its destruction. So we always add our listener 0N/A * (without synchronization), then we check if the buffer has been 0N/A * destroyed and if so remove the listener we just added. 0N/A synchronized (
this) {
0N/A * Iterate until we extract the real exception 0N/A * from a stack of PrivilegedActionExceptions. 0N/A "ArrayNotificationBuffer");