📄 arraynotificationbuffer.java
字号:
* listeners to new MBeans, then we query the existing MBeans to * add listeners to them. The problem is that a new MBean could * arrive after we register for creations but before the query has * completed. Then we could see the MBean both in the query and * in an MBean-creation notification, and we would end up * registering our listener twice. * * To solve this problem, we arrange for new MBeans that arrive * while the query is being done to be added to the Set createdDuringQuery * and we do not add a listener immediately. When the query is done, * we atomically turn off the addition of new names to createdDuringQuery * and add all the names that were there to the result of the query. * Since we are dealing with Sets, the result is the same whether or not * the newly-created MBean was included in the query result. * * It is important not to hold any locks during the operation of adding * listeners to MBeans. An MBean's addNotificationListener can be * arbitrary user code, and this could deadlock with any locks we hold * (see bug 6239400). The corollary is that we must not do any operations * in this method or the methods it calls that require locks. */ private void createListeners() { logger.debug("createListeners", "starts"); synchronized (this) { createdDuringQuery = new HashSet<ObjectName>(); } try { addNotificationListener(MBeanServerDelegate.DELEGATE_NAME, creationListener, creationFilter, null); logger.debug("createListeners", "added creationListener"); } catch (Exception e) { final String msg = "Can't add listener to MBean server delegate: "; RuntimeException re = new IllegalArgumentException(msg + e); EnvHelp.initCause(re, e); logger.fine("createListeners", msg + e); logger.debug("createListeners", e); throw re; } /* Spec doesn't say whether Set returned by QueryNames can be modified so we clone it. */ Set<ObjectName> names = queryNames(null, broadcasterQuery); names = new HashSet<ObjectName>(names); synchronized (this) { names.addAll(createdDuringQuery); createdDuringQuery = null; } for (ObjectName name : names) addBufferListener(name); logger.debug("createListeners", "ends"); } private void addBufferListener(ObjectName name) { checkNoLocks(); if (logger.debugOn()) logger.debug("addBufferListener", name.toString()); try { addNotificationListener(name, bufferListener, null, name); } catch (Exception e) { logger.trace("addBufferListener", e); /* This can happen if the MBean was unregistered just after the query. Or user NotificationBroadcaster might throw unexpected exception. */ } } private void removeBufferListener(ObjectName name) { checkNoLocks(); if (logger.debugOn()) logger.debug("removeBufferListener", name.toString()); try { removeNotificationListener(name, bufferListener); } catch (Exception e) { logger.trace("removeBufferListener", e); } } private void addNotificationListener(final ObjectName name, final NotificationListener listener, final NotificationFilter filter, final Object handback) throws Exception { try { AccessController.doPrivileged(new PrivilegedExceptionAction() { public Object run() throws InstanceNotFoundException { mBeanServer.addNotificationListener(name, listener, filter, handback); return null; } }); } catch (Exception e) { throw extractException(e); } } private void removeNotificationListener(final ObjectName name, final NotificationListener listener) throws Exception { try { AccessController.doPrivileged(new PrivilegedExceptionAction() { public Object run() throws Exception { mBeanServer.removeNotificationListener(name, listener); return null; } }); } catch (Exception e) { throw extractException(e); } } private Set<ObjectName> queryNames(final ObjectName name, final QueryExp query) { PrivilegedAction<Set<ObjectName>> act = new PrivilegedAction<Set<ObjectName>>() { public Set<ObjectName> run() { return mBeanServer.queryNames(name, query); } }; try { return AccessController.doPrivileged(act); } catch (RuntimeException e) { logger.fine("queryNames", "Failed to query names: " + e); logger.debug("queryNames", e); throw e; } } private static boolean isInstanceOf(final MBeanServer mbs, final ObjectName name, final String className) { PrivilegedExceptionAction<Boolean> act = new PrivilegedExceptionAction<Boolean>() { public Boolean run() throws InstanceNotFoundException { return mbs.isInstanceOf(name, className); } }; try { return AccessController.doPrivileged(act); } catch (Exception e) { logger.fine("isInstanceOf", "failed: " + e); logger.debug("isInstanceOf", e); return false; } } /* This method must not be synchronized. See the comment on the * createListeners method. * * The notification could arrive after our buffer has been destroyed * or even during its destruction. So we always add our listener * (without synchronization), then we check if the buffer has been * destroyed and if so remove the listener we just added. */ private void createdNotification(MBeanServerNotification n) { final String shouldEqual = MBeanServerNotification.REGISTRATION_NOTIFICATION; if (!n.getType().equals(shouldEqual)) { logger.warning("createNotification", "bad type: " + n.getType()); return; } ObjectName name = n.getMBeanName(); if (logger.debugOn()) logger.debug("createdNotification", "for: " + name); synchronized (this) { if (createdDuringQuery != null) { createdDuringQuery.add(name); return; } } if (isInstanceOf(mBeanServer, name, broadcasterClass)) { addBufferListener(name); if (isDisposed()) removeBufferListener(name); } } private class BufferListener implements NotificationListener { public void handleNotification(Notification notif, Object handback) { if (logger.debugOn()) { logger.debug("BufferListener.handleNotification", "notif=" + notif + "; handback=" + handback); } ObjectName name = (ObjectName) handback; addNotification(new NamedNotification(name, notif)); } } private final NotificationListener bufferListener = new BufferListener(); private static class BroadcasterQuery extends QueryEval implements QueryExp { public boolean apply(final ObjectName name) { final MBeanServer mbs = QueryEval.getMBeanServer(); return isInstanceOf(mbs, name, broadcasterClass); } } private static final QueryExp broadcasterQuery = new BroadcasterQuery(); private static final NotificationFilter creationFilter; static { NotificationFilterSupport nfs = new NotificationFilterSupport(); nfs.enableType(MBeanServerNotification.REGISTRATION_NOTIFICATION); creationFilter = nfs; } private final NotificationListener creationListener = new NotificationListener() { public void handleNotification(Notification notif, Object handback) { logger.debug("creationListener", "handleNotification called"); createdNotification((MBeanServerNotification) notif); } }; private void destroyListeners() { checkNoLocks(); logger.debug("destroyListeners", "starts"); try { removeNotificationListener(MBeanServerDelegate.DELEGATE_NAME, creationListener); } catch (Exception e) { logger.warning("remove listener from MBeanServer delegate", e); } Set<ObjectName> names = queryNames(null, broadcasterQuery); for (final ObjectName name : names) { if (logger.debugOn()) logger.debug("destroyListeners", "remove listener from " + name); removeBufferListener(name); } logger.debug("destroyListeners", "ends"); } private void checkNoLocks() { if (Thread.holdsLock(this) || Thread.holdsLock(globalLock)) logger.warning("checkNoLocks", "lock protocol violation"); } /** * Iterate until we extract the real exception * from a stack of PrivilegedActionExceptions. */ private static Exception extractException(Exception e) { while (e instanceof PrivilegedActionException) { e = ((PrivilegedActionException)e).getException(); } return e; } private static final ClassLogger logger = new ClassLogger("javax.management.remote.misc", "ArrayNotificationBuffer"); private final MBeanServer mBeanServer; private final ArrayQueue<NamedNotification> queue; private int queueSize; private long earliestSequenceNumber; private long nextSequenceNumber; private Set<ObjectName> createdDuringQuery; static final String broadcasterClass = NotificationBroadcaster.class.getName();}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -