feat(jdk8): move files to new folder to avoid resources compiled.

This commit is contained in:
2025-09-07 15:25:52 +08:00
parent 3f0047bf6f
commit 8c35cfb1c0
17415 changed files with 217 additions and 213 deletions

View File

@@ -0,0 +1,852 @@
/*
* Copyright (c) 2003, 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 com.sun.jmx.remote.internal;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.HashMap;
import java.util.Map;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanServer;
import javax.management.MBeanServerDelegate;
import javax.management.MBeanServerNotification;
import javax.management.Notification;
import javax.management.NotificationBroadcaster;
import javax.management.NotificationFilter;
import javax.management.NotificationFilterSupport;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.QueryEval;
import javax.management.QueryExp;
import javax.management.remote.NotificationResult;
import javax.management.remote.TargetedNotification;
import com.sun.jmx.remote.util.EnvHelp;
import com.sun.jmx.remote.util.ClassLogger;
/** A circular buffer of notifications received from an MBean server. */
/*
There is one instance of ArrayNotificationBuffer for every
MBeanServer object that has an attached ConnectorServer. Then, for
every ConnectorServer attached to a given MBeanServer, there is an
instance of the inner class ShareBuffer. So for example with two
ConnectorServers it looks like this:
ConnectorServer1 -> ShareBuffer1 -\
}-> ArrayNotificationBuffer
ConnectorServer2 -> ShareBuffer2 -/ |
|
v
MBeanServer
The ArrayNotificationBuffer has a circular buffer of
NamedNotification objects. Each ConnectorServer defines a
notification buffer size, and this size is recorded by the
corresponding ShareBuffer. The buffer size of the
ArrayNotificationBuffer is the maximum of all of its ShareBuffers.
When a ShareBuffer is added or removed, the ArrayNotificationBuffer
size is adjusted accordingly.
An ArrayNotificationBuffer also has a BufferListener (which is a
NotificationListener) registered on every NotificationBroadcaster
MBean in the MBeanServer to which it is attached. The cost of this
potentially large set of listeners is the principal motivation for
sharing the ArrayNotificationBuffer between ConnectorServers, and
also the reason that we are careful to discard the
ArrayNotificationBuffer (and its BufferListeners) when there are no
longer any ConnectorServers using it.
The synchronization of this class is inherently complex. In an attempt
to limit the complexity, we use just two locks:
- globalLock controls access to the mapping between an MBeanServer
and its ArrayNotificationBuffer and to the set of ShareBuffers for
each ArrayNotificationBuffer.
- the instance lock of each ArrayNotificationBuffer controls access
to the array of notifications, including its size, and to the
dispose flag of the ArrayNotificationBuffer. The wait/notify
mechanism is used to indicate changes to the array.
If both locks are held at the same time, the globalLock must be
taken first.
Since adding or removing a BufferListener to an MBean can involve
calling user code, we are careful not to hold any locks while it is
done.
*/
public class ArrayNotificationBuffer implements NotificationBuffer {
private boolean disposed = false;
// FACTORY STUFF, INCLUDING SHARING
private static final Object globalLock = new Object();
private static final
HashMap<MBeanServer,ArrayNotificationBuffer> mbsToBuffer =
new HashMap<MBeanServer,ArrayNotificationBuffer>(1);
private final Collection<ShareBuffer> sharers = new HashSet<ShareBuffer>(1);
public static NotificationBuffer getNotificationBuffer(
MBeanServer mbs, Map<String, ?> env) {
if (env == null)
env = Collections.emptyMap();
//Find out queue size
int queueSize = EnvHelp.getNotifBufferSize(env);
ArrayNotificationBuffer buf;
boolean create;
NotificationBuffer sharer;
synchronized (globalLock) {
buf = mbsToBuffer.get(mbs);
create = (buf == null);
if (create) {
buf = new ArrayNotificationBuffer(mbs, queueSize);
mbsToBuffer.put(mbs, buf);
}
sharer = buf.new ShareBuffer(queueSize);
}
/* We avoid holding any locks while calling createListeners.
* This prevents possible deadlocks involving user code, but
* does mean that a second ConnectorServer created and started
* in this window will return before all the listeners are ready,
* which could lead to surprising behaviour. The alternative
* would be to block the second ConnectorServer until the first
* one has finished adding all the listeners, but that would then
* be subject to deadlock.
*/
if (create)
buf.createListeners();
return sharer;
}
/* Ensure that this buffer is no longer the one that will be returned by
* getNotificationBuffer. This method is idempotent - calling it more
* than once has no effect beyond that of calling it once.
*/
static void removeNotificationBuffer(MBeanServer mbs) {
synchronized (globalLock) {
mbsToBuffer.remove(mbs);
}
}
void addSharer(ShareBuffer sharer) {
synchronized (globalLock) {
synchronized (this) {
if (sharer.getSize() > queueSize)
resize(sharer.getSize());
}
sharers.add(sharer);
}
}
private void removeSharer(ShareBuffer sharer) {
boolean empty;
synchronized (globalLock) {
sharers.remove(sharer);
empty = sharers.isEmpty();
if (empty)
removeNotificationBuffer(mBeanServer);
else {
int max = 0;
for (ShareBuffer buf : sharers) {
int bufsize = buf.getSize();
if (bufsize > max)
max = bufsize;
}
if (max < queueSize)
resize(max);
}
}
if (empty) {
synchronized (this) {
disposed = true;
// Notify potential waiting fetchNotification call
notifyAll();
}
destroyListeners();
}
}
private synchronized void resize(int newSize) {
if (newSize == queueSize)
return;
while (queue.size() > newSize)
dropNotification();
queue.resize(newSize);
queueSize = newSize;
}
private class ShareBuffer implements NotificationBuffer {
ShareBuffer(int size) {
this.size = size;
addSharer(this);
}
public NotificationResult
fetchNotifications(NotificationBufferFilter filter,
long startSequenceNumber,
long timeout,
int maxNotifications)
throws InterruptedException {
NotificationBuffer buf = ArrayNotificationBuffer.this;
return buf.fetchNotifications(filter, startSequenceNumber,
timeout, maxNotifications);
}
public void dispose() {
ArrayNotificationBuffer.this.removeSharer(this);
}
int getSize() {
return size;
}
private final int size;
}
// ARRAYNOTIFICATIONBUFFER IMPLEMENTATION
private ArrayNotificationBuffer(MBeanServer mbs, int queueSize) {
if (logger.traceOn())
logger.trace("Constructor", "queueSize=" + queueSize);
if (mbs == null || queueSize < 1)
throw new IllegalArgumentException("Bad args");
this.mBeanServer = mbs;
this.queueSize = queueSize;
this.queue = new ArrayQueue<NamedNotification>(queueSize);
this.earliestSequenceNumber = System.currentTimeMillis();
this.nextSequenceNumber = this.earliestSequenceNumber;
logger.trace("Constructor", "ends");
}
private synchronized boolean isDisposed() {
return disposed;
}
// We no longer support calling this method from outside.
// The JDK doesn't contain any such calls and users are not
// supposed to be accessing this class.
public void dispose() {
throw new UnsupportedOperationException();
}
/**
* <p>Fetch notifications that match the given listeners.</p>
*
* <p>The operation only considers notifications with a sequence
* number at least <code>startSequenceNumber</code>. It will take
* no longer than <code>timeout</code>, and will return no more
* than <code>maxNotifications</code> different notifications.</p>
*
* <p>If there are no notifications matching the criteria, the
* operation will block until one arrives, subject to the
* timeout.</p>
*
* @param filter an object that will add notifications to a
* {@code List<TargetedNotification>} if they match the current
* listeners with their filters.
* @param startSequenceNumber the first sequence number to
* consider.
* @param timeout the maximum time to wait. May be 0 to indicate
* not to wait if there are no notifications.
* @param maxNotifications the maximum number of notifications to
* return. May be 0 to indicate a wait for eligible notifications
* that will return a usable <code>nextSequenceNumber</code>. The
* {@link TargetedNotification} array in the returned {@link
* NotificationResult} may contain more than this number of
* elements but will not contain more than this number of
* different notifications.
*/
public NotificationResult
fetchNotifications(NotificationBufferFilter filter,
long startSequenceNumber,
long timeout,
int maxNotifications)
throws InterruptedException {
logger.trace("fetchNotifications", "starts");
if (startSequenceNumber < 0 || isDisposed()) {
synchronized(this) {
return new NotificationResult(earliestSequenceNumber(),
nextSequenceNumber(),
new TargetedNotification[0]);
}
}
// Check arg validity
if (filter == null
|| startSequenceNumber < 0 || timeout < 0
|| maxNotifications < 0) {
logger.trace("fetchNotifications", "Bad args");
throw new IllegalArgumentException("Bad args to fetch");
}
if (logger.debugOn()) {
logger.trace("fetchNotifications",
"filter=" + filter + "; startSeq=" +
startSequenceNumber + "; timeout=" + timeout +
"; max=" + maxNotifications);
}
if (startSequenceNumber > nextSequenceNumber()) {
final String msg = "Start sequence number too big: " +
startSequenceNumber + " > " + nextSequenceNumber();
logger.trace("fetchNotifications", msg);
throw new IllegalArgumentException(msg);
}
/* Determine the end time corresponding to the timeout value.
Caller may legitimately supply Long.MAX_VALUE to indicate no
timeout. In that case the addition will overflow and produce
a negative end time. Set end time to Long.MAX_VALUE in that
case. We assume System.currentTimeMillis() is positive. */
long endTime = System.currentTimeMillis() + timeout;
if (endTime < 0) // overflow
endTime = Long.MAX_VALUE;
if (logger.debugOn())
logger.debug("fetchNotifications", "endTime=" + endTime);
/* We set earliestSeq the first time through the loop. If we
set it here, notifications could be dropped before we
started examining them, so earliestSeq might not correspond
to the earliest notification we examined. */
long earliestSeq = -1;
long nextSeq = startSequenceNumber;
List<TargetedNotification> notifs =
new ArrayList<TargetedNotification>();
/* On exit from this loop, notifs, earliestSeq, and nextSeq must
all be correct values for the returned NotificationResult. */
while (true) {
logger.debug("fetchNotifications", "main loop starts");
NamedNotification candidate;
/* Get the next available notification regardless of filters,
or wait for one to arrive if there is none. */
synchronized (this) {
/* First time through. The current earliestSequenceNumber
is the first one we could have examined. */
if (earliestSeq < 0) {
earliestSeq = earliestSequenceNumber();
if (logger.debugOn()) {
logger.debug("fetchNotifications",
"earliestSeq=" + earliestSeq);
}
if (nextSeq < earliestSeq) {
nextSeq = earliestSeq;
logger.debug("fetchNotifications",
"nextSeq=earliestSeq");
}
} else
earliestSeq = earliestSequenceNumber();
/* If many notifications have been dropped since the
last time through, nextSeq could now be earlier
than the current earliest. If so, notifications
may have been lost and we return now so the caller
can see this next time it calls. */
if (nextSeq < earliestSeq) {
logger.trace("fetchNotifications",
"nextSeq=" + nextSeq + " < " + "earliestSeq=" +
earliestSeq + " so may have lost notifs");
break;
}
if (nextSeq < nextSequenceNumber()) {
candidate = notificationAt(nextSeq);
// Skip security check if NotificationBufferFilter is not overloaded
if (!(filter instanceof ServerNotifForwarder.NotifForwarderBufferFilter)) {
try {
ServerNotifForwarder.checkMBeanPermission(this.mBeanServer,
candidate.getObjectName(),"addNotificationListener");
} catch (InstanceNotFoundException | SecurityException e) {
if (logger.debugOn()) {
logger.debug("fetchNotifications", "candidate: " + candidate + " skipped. exception " + e);
}
++nextSeq;
continue;
}
}
if (logger.debugOn()) {
logger.debug("fetchNotifications", "candidate: " +
candidate);
logger.debug("fetchNotifications", "nextSeq now " +
nextSeq);
}
} else {
/* nextSeq is the largest sequence number. If we
already got notifications, return them now.
Otherwise wait for some to arrive, with
timeout. */
if (notifs.size() > 0) {
logger.debug("fetchNotifications",
"no more notifs but have some so don't wait");
break;
}
long toWait = endTime - System.currentTimeMillis();
if (toWait <= 0) {
logger.debug("fetchNotifications", "timeout");
break;
}
/* dispose called */
if (isDisposed()) {
if (logger.debugOn())
logger.debug("fetchNotifications",
"dispose callled, no wait");
return new NotificationResult(earliestSequenceNumber(),
nextSequenceNumber(),
new TargetedNotification[0]);
}
if (logger.debugOn())
logger.debug("fetchNotifications",
"wait(" + toWait + ")");
wait(toWait);
continue;
}
}
/* We have a candidate notification. See if it matches
our filters. We do this outside the synchronized block
so we don't hold up everyone accessing the buffer
(including notification senders) while we evaluate
potentially slow filters. */
ObjectName name = candidate.getObjectName();
Notification notif = candidate.getNotification();
List<TargetedNotification> matchedNotifs =
new ArrayList<TargetedNotification>();
logger.debug("fetchNotifications",
"applying filter to candidate");
filter.apply(matchedNotifs, name, notif);
if (matchedNotifs.size() > 0) {
/* We only check the max size now, so that our
returned nextSeq is as large as possible. This
prevents the caller from thinking it missed
interesting notifications when in fact we knew they
weren't. */
if (maxNotifications <= 0) {
logger.debug("fetchNotifications",
"reached maxNotifications");
break;
}
--maxNotifications;
if (logger.debugOn())
logger.debug("fetchNotifications", "add: " +
matchedNotifs);
notifs.addAll(matchedNotifs);
}
++nextSeq;
} // end while
/* Construct and return the result. */
int nnotifs = notifs.size();
TargetedNotification[] resultNotifs =
new TargetedNotification[nnotifs];
notifs.toArray(resultNotifs);
NotificationResult nr =
new NotificationResult(earliestSeq, nextSeq, resultNotifs);
if (logger.debugOn())
logger.debug("fetchNotifications", nr.toString());
logger.trace("fetchNotifications", "ends");
return nr;
}
synchronized long earliestSequenceNumber() {
return earliestSequenceNumber;
}
synchronized long nextSequenceNumber() {
return nextSequenceNumber;
}
synchronized void addNotification(NamedNotification notif) {
if (logger.traceOn())
logger.trace("addNotification", notif.toString());
while (queue.size() >= queueSize) {
dropNotification();
if (logger.debugOn()) {
logger.debug("addNotification",
"dropped oldest notif, earliestSeq=" +
earliestSequenceNumber);
}
}
queue.add(notif);
nextSequenceNumber++;
if (logger.debugOn())
logger.debug("addNotification", "nextSeq=" + nextSequenceNumber);
notifyAll();
}
private void dropNotification() {
queue.remove(0);
earliestSequenceNumber++;
}
synchronized NamedNotification notificationAt(long seqNo) {
long index = seqNo - earliestSequenceNumber;
if (index < 0 || index > Integer.MAX_VALUE) {
final String msg = "Bad sequence number: " + seqNo + " (earliest "
+ earliestSequenceNumber + ")";
logger.trace("notificationAt", msg);
throw new IllegalArgumentException(msg);
}
return queue.get((int) index);
}
private static class NamedNotification {
NamedNotification(ObjectName sender, Notification notif) {
this.sender = sender;
this.notification = notif;
}
ObjectName getObjectName() {
return sender;
}
Notification getNotification() {
return notification;
}
public String toString() {
return "NamedNotification(" + sender + ", " + notification + ")";
}
private final ObjectName sender;
private final Notification notification;
}
/*
* Add our listener to every NotificationBroadcaster MBean
* currently in the MBean server and to every
* NotificationBroadcaster later created.
*
* It would be really nice if we could just do
* mbs.addNotificationListener(new ObjectName("*:*"), ...);
* Definitely something for the next version of JMX.
*
* There is a nasty race condition that we must handle. We
* first register for MBean-creation notifications so we can add
* 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<Void>() {
public Void 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<Void>() {
public Void 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 {
private static final long serialVersionUID = 7378487660587592048L;
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();
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright (c) 2003, 2006, 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 com.sun.jmx.remote.internal;
import java.util.AbstractList;
import java.util.Iterator;
public class ArrayQueue<T> extends AbstractList<T> {
public ArrayQueue(int capacity) {
this.capacity = capacity + 1;
this.queue = newArray(capacity + 1);
this.head = 0;
this.tail = 0;
}
public void resize(int newcapacity) {
int size = size();
if (newcapacity < size)
throw new IndexOutOfBoundsException("Resizing would lose data");
newcapacity++;
if (newcapacity == this.capacity)
return;
T[] newqueue = newArray(newcapacity);
for (int i = 0; i < size; i++)
newqueue[i] = get(i);
this.capacity = newcapacity;
this.queue = newqueue;
this.head = 0;
this.tail = size;
}
@SuppressWarnings("unchecked")
private T[] newArray(int size) {
return (T[]) new Object[size];
}
public boolean add(T o) {
queue[tail] = o;
int newtail = (tail + 1) % capacity;
if (newtail == head)
throw new IndexOutOfBoundsException("Queue full");
tail = newtail;
return true; // we did add something
}
public T remove(int i) {
if (i != 0)
throw new IllegalArgumentException("Can only remove head of queue");
if (head == tail)
throw new IndexOutOfBoundsException("Queue empty");
T removed = queue[head];
queue[head] = null;
head = (head + 1) % capacity;
return removed;
}
public T get(int i) {
int size = size();
if (i < 0 || i >= size) {
final String msg = "Index " + i + ", queue size " + size;
throw new IndexOutOfBoundsException(msg);
}
int index = (head + i) % capacity;
return queue[index];
}
public int size() {
// Can't use % here because it's not mod: -3 % 2 is -1, not +1.
int diff = tail - head;
if (diff < 0)
diff += capacity;
return diff;
}
private int capacity;
private T[] queue;
private int head;
private int tail;
}

View File

@@ -0,0 +1,253 @@
/*
* Copyright (c) 2003, 2012, 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 com.sun.jmx.remote.internal;
import java.io.IOException;
import java.io.InterruptedIOException;
import com.sun.jmx.remote.util.ClassLogger;
import com.sun.jmx.remote.util.EnvHelp;
public abstract class ClientCommunicatorAdmin {
private static volatile long threadNo = 1;
public ClientCommunicatorAdmin(long period) {
this.period = period;
if (period > 0) {
checker = new Checker();
Thread t = new Thread(checker, "JMX client heartbeat " + ++threadNo);
t.setDaemon(true);
t.start();
} else
checker = null;
}
/**
* Called by a client to inform of getting an IOException.
*/
public void gotIOException (IOException ioe) throws IOException {
restart(ioe);
}
/**
* Called by this class to check a client connection.
*/
protected abstract void checkConnection() throws IOException;
/**
* Tells a client to re-start again.
*/
protected abstract void doStart() throws IOException;
/**
* Tells a client to stop because failing to call checkConnection.
*/
protected abstract void doStop();
/**
* Terminates this object.
*/
public void terminate() {
synchronized(lock) {
if (state == TERMINATED) {
return;
}
state = TERMINATED;
lock.notifyAll();
if (checker != null)
checker.stop();
}
}
private void restart(IOException ioe) throws IOException {
// check state
synchronized(lock) {
if (state == TERMINATED) {
throw new IOException("The client has been closed.");
} else if (state == FAILED) { // already failed to re-start by another thread
throw ioe;
} else if (state == RE_CONNECTING) {
// restart process has been called by another thread
// we need to wait
while(state == RE_CONNECTING) {
try {
lock.wait();
} catch (InterruptedException ire) {
// be asked to give up
InterruptedIOException iioe = new InterruptedIOException(ire.toString());
EnvHelp.initCause(iioe, ire);
throw iioe;
}
}
if (state == TERMINATED) {
throw new IOException("The client has been closed.");
} else if (state != CONNECTED) {
// restarted is failed by another thread
throw ioe;
}
return;
} else {
state = RE_CONNECTING;
lock.notifyAll();
}
}
// re-starting
try {
doStart();
synchronized(lock) {
if (state == TERMINATED) {
throw new IOException("The client has been closed.");
}
state = CONNECTED;
lock.notifyAll();
}
return;
} catch (Exception e) {
logger.warning("restart", "Failed to restart: " + e);
logger.debug("restart",e);
synchronized(lock) {
if (state == TERMINATED) {
throw new IOException("The client has been closed.");
}
state = FAILED;
lock.notifyAll();
}
try {
doStop();
} catch (Exception eee) {
// OK.
// We know there is a problem.
}
terminate();
throw ioe;
}
}
// --------------------------------------------------------------
// private varaibles
// --------------------------------------------------------------
private class Checker implements Runnable {
public void run() {
myThread = Thread.currentThread();
while (state != TERMINATED && !myThread.isInterrupted()) {
try {
Thread.sleep(period);
} catch (InterruptedException ire) {
// OK.
// We will check the state at the following steps
}
if (state == TERMINATED || myThread.isInterrupted()) {
break;
}
try {
checkConnection();
} catch (Exception e) {
synchronized(lock) {
if (state == TERMINATED || myThread.isInterrupted()) {
break;
}
}
e = (Exception)EnvHelp.getCause(e);
if (e instanceof IOException &&
!(e instanceof InterruptedIOException)) {
try {
gotIOException((IOException)e);
} catch (Exception ee) {
logger.warning("Checker-run",
"Failed to check connection: "+ e);
logger.warning("Checker-run", "stopping");
logger.debug("Checker-run",e);
break;
}
} else {
logger.warning("Checker-run",
"Failed to check the connection: " + e);
logger.debug("Checker-run",e);
// XXX stop checking?
break;
}
}
}
if (logger.traceOn()) {
logger.trace("Checker-run", "Finished.");
}
}
private void stop() {
if (myThread != null && myThread != Thread.currentThread()) {
myThread.interrupt();
}
}
private Thread myThread;
}
// --------------------------------------------------------------
// private variables
// --------------------------------------------------------------
private final Checker checker;
private long period;
// state
private final static int CONNECTED = 0;
private final static int RE_CONNECTING = 1;
private final static int FAILED = 2;
private final static int TERMINATED = 3;
private int state = CONNECTED;
private final int[] lock = new int[0];
private static final ClassLogger logger =
new ClassLogger("javax.management.remote.misc",
"ClientCommunicatorAdmin");
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright (c) 2003, 2006, 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 com.sun.jmx.remote.internal;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.security.auth.Subject;
/**
* <p>An identified listener. A listener has an Integer id that is
* unique per connector server. It selects notifications based on the
* ObjectName of the originator and an optional
* NotificationFilter.</p>
*/
public class ClientListenerInfo {
public ClientListenerInfo(Integer listenerID,
ObjectName name,
NotificationListener listener,
NotificationFilter filter,
Object handback,
Subject delegationSubject) {
this.listenerID = listenerID;
this.name = name;
this.listener = listener;
this.filter = filter;
this.handback = handback;
this.delegationSubject = delegationSubject;
}
public ObjectName getObjectName() {
return name;
}
public Integer getListenerID() {
return listenerID;
}
public NotificationFilter getNotificationFilter() {
return filter;
}
public NotificationListener getListener() {
return listener;
}
public Object getHandback() {
return handback;
}
public Subject getDelegationSubject() {
return delegationSubject;
}
public boolean sameAs(ObjectName name) {
return (getObjectName().equals(name));
}
public boolean sameAs(ObjectName name, NotificationListener listener) {
return ( getObjectName().equals(name) &&
getListener() == listener);
}
public boolean sameAs(ObjectName name, NotificationListener listener, NotificationFilter filter, Object handback) {
return ( getObjectName().equals(name) &&
getListener() == listener &&
getNotificationFilter() == filter &&
getHandback() == handback);
}
private final ObjectName name;
private final Integer listenerID;
private final NotificationFilter filter;
private final NotificationListener listener;
private final Object handback;
private final Subject delegationSubject;
}

View File

@@ -0,0 +1,921 @@
/*
* Copyright (c) 2002, 2015, 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 com.sun.jmx.remote.internal;
import java.io.IOException;
import java.io.NotSerializableException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import javax.security.auth.Subject;
import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.NotificationFilter;
import javax.management.ObjectName;
import javax.management.MBeanServerNotification;
import javax.management.InstanceNotFoundException;
import javax.management.ListenerNotFoundException;
import javax.management.remote.NotificationResult;
import javax.management.remote.TargetedNotification;
import com.sun.jmx.remote.util.ClassLogger;
import com.sun.jmx.remote.util.EnvHelp;
import java.rmi.UnmarshalException;
public abstract class ClientNotifForwarder {
private final AccessControlContext acc;
public ClientNotifForwarder(Map env) {
this(null, env);
}
private static int threadId;
/* An Executor that allows at most one executing and one pending
Runnable. It uses at most one thread -- as soon as there is
no pending Runnable the thread can exit. Another thread is
created as soon as there is a new pending Runnable. This
Executor is adapted for use in a situation where each Runnable
usually schedules up another Runnable. On return from the
first one, the second one is immediately executed. So this
just becomes a complicated way to write a while loop, but with
the advantage that you can replace it with another Executor,
for instance one that you are using to execute a bunch of other
unrelated work.
You might expect that a java.util.concurrent.ThreadPoolExecutor
with corePoolSize=0 and maximumPoolSize=1 would have the same
behavior, but it does not. A ThreadPoolExecutor only creates
a new thread when a new task is submitted and the number of
existing threads is < corePoolSize. This can never happen when
corePoolSize=0, so new threads are never created. Surprising,
but there you are.
*/
private static class LinearExecutor implements Executor {
public synchronized void execute(Runnable command) {
if (this.command != null)
throw new IllegalArgumentException("More than one command");
this.command = command;
if (thread == null) {
thread = new Thread() {
@Override
public void run() {
while (true) {
Runnable r;
synchronized (LinearExecutor.this) {
if (LinearExecutor.this.command == null) {
thread = null;
return;
} else {
r = LinearExecutor.this.command;
LinearExecutor.this.command = null;
}
}
r.run();
}
}
};
thread.setDaemon(true);
thread.setName("ClientNotifForwarder-" + ++threadId);
thread.start();
}
}
private Runnable command;
private Thread thread;
}
public ClientNotifForwarder(ClassLoader defaultClassLoader, Map<String, ?> env) {
maxNotifications = EnvHelp.getMaxFetchNotifNumber(env);
timeout = EnvHelp.getFetchTimeout(env);
/* You can supply an Executor in which the remote call to
fetchNotifications will be made. The Executor's execute
method reschedules another task, so you must not use
an Executor that executes tasks in the caller's thread. */
Executor ex = (Executor)
env.get("jmx.remote.x.fetch.notifications.executor");
if (ex == null)
ex = new LinearExecutor();
else if (logger.traceOn())
logger.trace("ClientNotifForwarder", "executor is " + ex);
this.defaultClassLoader = defaultClassLoader;
this.executor = ex;
this.acc = AccessController.getContext();
}
/**
* Called to to fetch notifications from a server.
*/
abstract protected NotificationResult fetchNotifs(long clientSequenceNumber,
int maxNotifications,
long timeout)
throws IOException, ClassNotFoundException;
abstract protected Integer addListenerForMBeanRemovedNotif()
throws IOException, InstanceNotFoundException;
abstract protected void removeListenerForMBeanRemovedNotif(Integer id)
throws IOException, InstanceNotFoundException,
ListenerNotFoundException;
/**
* Used to send out a notification about lost notifs
*/
abstract protected void lostNotifs(String message, long number);
public synchronized void addNotificationListener(Integer listenerID,
ObjectName name,
NotificationListener listener,
NotificationFilter filter,
Object handback,
Subject delegationSubject)
throws IOException, InstanceNotFoundException {
if (logger.traceOn()) {
logger.trace("addNotificationListener",
"Add the listener "+listener+" at "+name);
}
infoList.put(listenerID,
new ClientListenerInfo(listenerID,
name,
listener,
filter,
handback,
delegationSubject));
init(false);
}
public synchronized Integer[]
removeNotificationListener(ObjectName name,
NotificationListener listener)
throws ListenerNotFoundException, IOException {
beforeRemove();
if (logger.traceOn()) {
logger.trace("removeNotificationListener",
"Remove the listener "+listener+" from "+name);
}
List<Integer> ids = new ArrayList<Integer>();
List<ClientListenerInfo> values =
new ArrayList<ClientListenerInfo>(infoList.values());
for (int i=values.size()-1; i>=0; i--) {
ClientListenerInfo li = values.get(i);
if (li.sameAs(name, listener)) {
ids.add(li.getListenerID());
infoList.remove(li.getListenerID());
}
}
if (ids.isEmpty())
throw new ListenerNotFoundException("Listener not found");
return ids.toArray(new Integer[0]);
}
public synchronized Integer
removeNotificationListener(ObjectName name,
NotificationListener listener,
NotificationFilter filter,
Object handback)
throws ListenerNotFoundException, IOException {
if (logger.traceOn()) {
logger.trace("removeNotificationListener",
"Remove the listener "+listener+" from "+name);
}
beforeRemove();
Integer id = null;
List<ClientListenerInfo> values =
new ArrayList<ClientListenerInfo>(infoList.values());
for (int i=values.size()-1; i>=0; i--) {
ClientListenerInfo li = values.get(i);
if (li.sameAs(name, listener, filter, handback)) {
id=li.getListenerID();
infoList.remove(id);
break;
}
}
if (id == null)
throw new ListenerNotFoundException("Listener not found");
return id;
}
public synchronized Integer[] removeNotificationListener(ObjectName name) {
if (logger.traceOn()) {
logger.trace("removeNotificationListener",
"Remove all listeners registered at "+name);
}
List<Integer> ids = new ArrayList<Integer>();
List<ClientListenerInfo> values =
new ArrayList<ClientListenerInfo>(infoList.values());
for (int i=values.size()-1; i>=0; i--) {
ClientListenerInfo li = values.get(i);
if (li.sameAs(name)) {
ids.add(li.getListenerID());
infoList.remove(li.getListenerID());
}
}
return ids.toArray(new Integer[0]);
}
/*
* Called when a connector is doing reconnection. Like <code>postReconnection</code>,
* this method is intended to be called only by a client connector:
* <code>RMIConnector</code> and <code>ClientIntermediary</code>.
* Call this method will set the flag beingReconnection to <code>true</code>,
* and the thread used to fetch notifis will be stopped, a new thread can be
* created only after the method <code>postReconnection</code> is called.
*
* It is caller's responsiblity to not re-call this method before calling
* <code>postReconnection</code>.
*/
public synchronized ClientListenerInfo[] preReconnection() throws IOException {
if (state == TERMINATED || beingReconnected) { // should never
throw new IOException("Illegal state.");
}
final ClientListenerInfo[] tmp =
infoList.values().toArray(new ClientListenerInfo[0]);
beingReconnected = true;
infoList.clear();
return tmp;
}
/**
* Called after reconnection is finished.
* This method is intended to be called only by a client connector:
* <code>RMIConnector</code> and <code>ClientIntermediary</code>.
*/
public synchronized void postReconnection(ClientListenerInfo[] listenerInfos)
throws IOException {
if (state == TERMINATED) {
return;
}
while (state == STOPPING) {
try {
wait();
} catch (InterruptedException ire) {
IOException ioe = new IOException(ire.toString());
EnvHelp.initCause(ioe, ire);
throw ioe;
}
}
final boolean trace = logger.traceOn();
final int len = listenerInfos.length;
for (int i=0; i<len; i++) {
if (trace) {
logger.trace("addNotificationListeners",
"Add a listener at "+
listenerInfos[i].getListenerID());
}
infoList.put(listenerInfos[i].getListenerID(), listenerInfos[i]);
}
beingReconnected = false;
notifyAll();
if (currentFetchThread == Thread.currentThread() ||
state == STARTING || state == STARTED) { // doing or waiting reconnection
// only update mbeanRemovedNotifID
try {
mbeanRemovedNotifID = addListenerForMBeanRemovedNotif();
} catch (Exception e) {
final String msg =
"Failed to register a listener to the mbean " +
"server: the client will not do clean when an MBean " +
"is unregistered";
if (logger.traceOn()) {
logger.trace("init", msg, e);
}
}
} else {
while (state == STOPPING) {
try {
wait();
} catch (InterruptedException ire) {
IOException ioe = new IOException(ire.toString());
EnvHelp.initCause(ioe, ire);
throw ioe;
}
}
if (listenerInfos.length > 0) { // old listeners are re-added
init(true); // not update clientSequenceNumber
} else if (infoList.size() > 0) { // only new listeners added during reconnection
init(false); // need update clientSequenceNumber
}
}
}
public synchronized void terminate() {
if (state == TERMINATED) {
return;
}
if (logger.traceOn()) {
logger.trace("terminate", "Terminating...");
}
if (state == STARTED) {
infoList.clear();
}
setState(TERMINATED);
}
// -------------------------------------------------
// private classes
// -------------------------------------------------
//
private class NotifFetcher implements Runnable {
private volatile boolean alreadyLogged = false;
private void logOnce(String msg, SecurityException x) {
if (alreadyLogged) return;
// Log only once.
logger.config("setContextClassLoader",msg);
if (x != null) logger.fine("setContextClassLoader", x);
alreadyLogged = true;
}
// Set new context class loader, returns previous one.
private final ClassLoader setContextClassLoader(final ClassLoader loader) {
final AccessControlContext ctxt = ClientNotifForwarder.this.acc;
// if ctxt is null, log a config message and throw a
// SecurityException.
if (ctxt == null) {
logOnce("AccessControlContext must not be null.",null);
throw new SecurityException("AccessControlContext must not be null");
}
return AccessController.doPrivileged(
new PrivilegedAction<ClassLoader>() {
public ClassLoader run() {
try {
// get context class loader - may throw
// SecurityException - though unlikely.
final ClassLoader previous =
Thread.currentThread().getContextClassLoader();
// if nothing needs to be done, break here...
if (loader == previous) return previous;
// reset context class loader - may throw
// SecurityException
Thread.currentThread().setContextClassLoader(loader);
return previous;
} catch (SecurityException x) {
logOnce("Permission to set ContextClassLoader missing. " +
"Notifications will not be dispatched. " +
"Please check your Java policy configuration: " +
x, x);
throw x;
}
}
}, ctxt);
}
public void run() {
final ClassLoader previous;
if (defaultClassLoader != null) {
previous = setContextClassLoader(defaultClassLoader);
} else {
previous = null;
}
try {
doRun();
} finally {
if (defaultClassLoader != null) {
setContextClassLoader(previous);
}
}
}
private void doRun() {
synchronized (ClientNotifForwarder.this) {
currentFetchThread = Thread.currentThread();
if (state == STARTING) {
setState(STARTED);
}
}
NotificationResult nr = null;
if (!shouldStop() && (nr = fetchNotifs()) != null) {
// nr == null means got exception
final TargetedNotification[] notifs =
nr.getTargetedNotifications();
final int len = notifs.length;
final Map<Integer, ClientListenerInfo> listeners;
final Integer myListenerID;
long missed = 0;
synchronized(ClientNotifForwarder.this) {
// check sequence number.
//
if (clientSequenceNumber >= 0) {
missed = nr.getEarliestSequenceNumber() -
clientSequenceNumber;
}
clientSequenceNumber = nr.getNextSequenceNumber();
listeners = new HashMap<Integer, ClientListenerInfo>();
for (int i = 0 ; i < len ; i++) {
final TargetedNotification tn = notifs[i];
final Integer listenerID = tn.getListenerID();
// check if an mbean unregistration notif
if (!listenerID.equals(mbeanRemovedNotifID)) {
final ClientListenerInfo li = infoList.get(listenerID);
if (li != null) {
listeners.put(listenerID, li);
}
continue;
}
final Notification notif = tn.getNotification();
final String unreg =
MBeanServerNotification.UNREGISTRATION_NOTIFICATION;
if (notif instanceof MBeanServerNotification &&
notif.getType().equals(unreg)) {
MBeanServerNotification mbsn =
(MBeanServerNotification) notif;
ObjectName name = mbsn.getMBeanName();
removeNotificationListener(name);
}
}
myListenerID = mbeanRemovedNotifID;
}
if (missed > 0) {
final String msg =
"May have lost up to " + missed +
" notification" + (missed == 1 ? "" : "s");
lostNotifs(msg, missed);
logger.trace("NotifFetcher.run", msg);
}
// forward
for (int i = 0 ; i < len ; i++) {
final TargetedNotification tn = notifs[i];
dispatchNotification(tn,myListenerID,listeners);
}
}
synchronized (ClientNotifForwarder.this) {
currentFetchThread = null;
}
if (nr == null) {
if (logger.traceOn()) {
logger.trace("NotifFetcher-run",
"Recieved null object as notifs, stops fetching because the "
+ "notification server is terminated.");
}
}
if (nr == null || shouldStop()) {
// tell that the thread is REALLY stopped
setState(STOPPED);
try {
removeListenerForMBeanRemovedNotif(mbeanRemovedNotifID);
} catch (Exception e) {
if (logger.traceOn()) {
logger.trace("NotifFetcher-run",
"removeListenerForMBeanRemovedNotif", e);
}
}
} else {
executor.execute(this);
}
}
void dispatchNotification(TargetedNotification tn,
Integer myListenerID,
Map<Integer, ClientListenerInfo> listeners) {
final Notification notif = tn.getNotification();
final Integer listenerID = tn.getListenerID();
if (listenerID.equals(myListenerID)) return;
final ClientListenerInfo li = listeners.get(listenerID);
if (li == null) {
logger.trace("NotifFetcher.dispatch",
"Listener ID not in map");
return;
}
NotificationListener l = li.getListener();
Object h = li.getHandback();
try {
l.handleNotification(notif, h);
} catch (RuntimeException e) {
final String msg =
"Failed to forward a notification " +
"to a listener";
logger.trace("NotifFetcher-run", msg, e);
}
}
private NotificationResult fetchNotifs() {
try {
NotificationResult nr = ClientNotifForwarder.this.
fetchNotifs(clientSequenceNumber,maxNotifications,
timeout);
if (logger.traceOn()) {
logger.trace("NotifFetcher-run",
"Got notifications from the server: "+nr);
}
return nr;
} catch (ClassNotFoundException | NotSerializableException | UnmarshalException e) {
logger.trace("NotifFetcher.fetchNotifs", e);
return fetchOneNotif();
} catch (IOException ioe) {
if (!shouldStop()) {
logger.error("NotifFetcher-run",
"Failed to fetch notification, " +
"stopping thread. Error is: " + ioe, ioe);
logger.debug("NotifFetcher-run",ioe);
}
// no more fetching
return null;
}
}
/* Fetch one notification when we suspect that it might be a
notification that we can't deserialize (because of a
missing class). First we ask for 0 notifications with 0
timeout. This allows us to skip sequence numbers for
notifications that don't match our filters. Then we ask
for one notification. If that produces a
ClassNotFoundException, NotSerializableException or
UnmarshalException, we increase our sequence number and ask again.
Eventually we will either get a successful notification, or a
return with 0 notifications. In either case we can return a
NotificationResult. This algorithm works (albeit less
well) even if the server implementation doesn't optimize a
request for 0 notifications to skip sequence numbers for
notifications that don't match our filters.
If we had at least one
ClassNotFoundException/NotSerializableException/UnmarshalException,
then we must emit a JMXConnectionNotification.LOST_NOTIFS.
*/
private NotificationResult fetchOneNotif() {
ClientNotifForwarder cnf = ClientNotifForwarder.this;
long startSequenceNumber = clientSequenceNumber;
int notFoundCount = 0;
NotificationResult result = null;
long firstEarliest = -1;
while (result == null && !shouldStop()) {
NotificationResult nr;
try {
// 0 notifs to update startSequenceNumber
nr = cnf.fetchNotifs(startSequenceNumber, 0, 0L);
} catch (ClassNotFoundException e) {
logger.warning("NotifFetcher.fetchOneNotif",
"Impossible exception: " + e);
logger.debug("NotifFetcher.fetchOneNotif",e);
return null;
} catch (IOException e) {
if (!shouldStop())
logger.trace("NotifFetcher.fetchOneNotif", e);
return null;
}
if (shouldStop() || nr == null)
return null;
startSequenceNumber = nr.getNextSequenceNumber();
if (firstEarliest < 0)
firstEarliest = nr.getEarliestSequenceNumber();
try {
// 1 notif to skip possible missing class
result = cnf.fetchNotifs(startSequenceNumber, 1, 0L);
} catch (ClassNotFoundException | NotSerializableException | UnmarshalException e) {
logger.warning("NotifFetcher.fetchOneNotif",
"Failed to deserialize a notification: "+e.toString());
if (logger.traceOn()) {
logger.trace("NotifFetcher.fetchOneNotif",
"Failed to deserialize a notification.", e);
}
notFoundCount++;
startSequenceNumber++;
} catch (Exception e) {
if (!shouldStop())
logger.trace("NotifFetcher.fetchOneNotif", e);
return null;
}
}
if (notFoundCount > 0) {
final String msg =
"Dropped " + notFoundCount + " notification" +
(notFoundCount == 1 ? "" : "s") +
" because classes were missing locally or incompatible";
lostNotifs(msg, notFoundCount);
// Even if result.getEarliestSequenceNumber() is now greater than
// it was initially, meaning some notifs have been dropped
// from the buffer, we don't want the caller to see that
// because it is then likely to renotify about the lost notifs.
// So we put back the first value of earliestSequenceNumber
// that we saw.
if (result != null) {
result = new NotificationResult(
firstEarliest, result.getNextSequenceNumber(),
result.getTargetedNotifications());
}
}
return result;
}
private boolean shouldStop() {
synchronized (ClientNotifForwarder.this) {
if (state != STARTED) {
return true;
} else if (infoList.size() == 0) {
// no more listener, stop fetching
setState(STOPPING);
return true;
}
return false;
}
}
}
// -------------------------------------------------
// private methods
// -------------------------------------------------
private synchronized void setState(int newState) {
if (state == TERMINATED) {
return;
}
state = newState;
this.notifyAll();
}
/*
* Called to decide whether need to start a thread for fetching notifs.
* <P>The parameter reconnected will decide whether to initilize the clientSequenceNumber,
* initilaizing the clientSequenceNumber means to ignore all notifications arrived before.
* If it is reconnected, we will not initialize in order to get all notifications arrived
* during the reconnection. It may cause the newly registered listeners to receive some
* notifications arrived before its registray.
*/
private synchronized void init(boolean reconnected) throws IOException {
switch (state) {
case STARTED:
return;
case STARTING:
return;
case TERMINATED:
throw new IOException("The ClientNotifForwarder has been terminated.");
case STOPPING:
if (beingReconnected == true) {
// wait for another thread to do, which is doing reconnection
return;
}
while (state == STOPPING) { // make sure only one fetching thread.
try {
wait();
} catch (InterruptedException ire) {
IOException ioe = new IOException(ire.toString());
EnvHelp.initCause(ioe, ire);
throw ioe;
}
}
// re-call this method to check the state again,
// the state can be other value like TERMINATED.
init(reconnected);
return;
case STOPPED:
if (beingReconnected == true) {
// wait for another thread to do, which is doing reconnection
return;
}
if (logger.traceOn()) {
logger.trace("init", "Initializing...");
}
// init the clientSequenceNumber if not reconnected.
if (!reconnected) {
try {
NotificationResult nr = fetchNotifs(-1, 0, 0);
if (state != STOPPED) { // JDK-8038940
// reconnection must happen during
// fetchNotifs(-1, 0, 0), and a new
// thread takes over the fetching job
return;
}
clientSequenceNumber = nr.getNextSequenceNumber();
} catch (ClassNotFoundException e) {
// can't happen
logger.warning("init", "Impossible exception: "+ e);
logger.debug("init",e);
}
}
// for cleaning
try {
mbeanRemovedNotifID = addListenerForMBeanRemovedNotif();
} catch (Exception e) {
final String msg =
"Failed to register a listener to the mbean " +
"server: the client will not do clean when an MBean " +
"is unregistered";
if (logger.traceOn()) {
logger.trace("init", msg, e);
}
}
setState(STARTING);
// start fetching
executor.execute(new NotifFetcher());
return;
default:
// should not
throw new IOException("Unknown state.");
}
}
/**
* Import: should not remove a listener during reconnection, the reconnection
* needs to change the listener list and that will possibly make removal fail.
*/
private synchronized void beforeRemove() throws IOException {
while (beingReconnected) {
if (state == TERMINATED) {
throw new IOException("Terminated.");
}
try {
wait();
} catch (InterruptedException ire) {
IOException ioe = new IOException(ire.toString());
EnvHelp.initCause(ioe, ire);
throw ioe;
}
}
if (state == TERMINATED) {
throw new IOException("Terminated.");
}
}
// -------------------------------------------------
// private variables
// -------------------------------------------------
private final ClassLoader defaultClassLoader;
private final Executor executor;
private final Map<Integer, ClientListenerInfo> infoList =
new HashMap<Integer, ClientListenerInfo>();
// notif stuff
private long clientSequenceNumber = -1;
private final int maxNotifications;
private final long timeout;
private Integer mbeanRemovedNotifID = null;
private Thread currentFetchThread;
// state
/**
* This state means that a thread is being created for fetching and forwarding notifications.
*/
private static final int STARTING = 0;
/**
* This state tells that a thread has been started for fetching and forwarding notifications.
*/
private static final int STARTED = 1;
/**
* This state means that the fetching thread is informed to stop.
*/
private static final int STOPPING = 2;
/**
* This state means that the fetching thread is already stopped.
*/
private static final int STOPPED = 3;
/**
* This state means that this object is terminated and no more thread will be created
* for fetching notifications.
*/
private static final int TERMINATED = 4;
private int state = STOPPED;
/**
* This variable is used to tell whether a connector (RMIConnector or ClientIntermediary)
* is doing reconnection.
* This variable will be set to true by the method <code>preReconnection</code>, and set
* to false by <code>postReconnection</code>.
* When beingReconnected == true, no thread will be created for fetching notifications.
*/
private boolean beingReconnected = false;
private static final ClassLogger logger =
new ClassLogger("javax.management.remote.misc",
"ClientNotifForwarder");
}

View File

@@ -0,0 +1,190 @@
/*
* Copyright (c) 2009, 2012, 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 com.sun.jmx.remote.internal;
import java.util.Properties;
import java.io.IOException;
import java.rmi.Remote;
import java.rmi.NoSuchObjectException;
import java.security.AccessController;
import java.security.PrivilegedAction;
/**
* A helper class for RMI-IIOP and CORBA APIs.
*/
public final class IIOPHelper {
private IIOPHelper() { }
// loads IIOPProxy implementation class if available
private static final String IMPL_CLASS =
"com.sun.jmx.remote.protocol.iiop.IIOPProxyImpl";
private static final IIOPProxy proxy =
AccessController.doPrivileged(new PrivilegedAction<IIOPProxy>() {
public IIOPProxy run() {
try {
Class<?> c = Class.forName(IMPL_CLASS, true,
IIOPHelper.class.getClassLoader());
return (IIOPProxy)c.newInstance();
} catch (ClassNotFoundException cnf) {
return null;
} catch (InstantiationException e) {
throw new AssertionError(e);
} catch (IllegalAccessException e) {
throw new AssertionError(e);
}
}});
/**
* Returns true if RMI-IIOP and CORBA is available.
*/
public static boolean isAvailable() {
return proxy != null;
}
private static void ensureAvailable() {
if (proxy == null)
throw new AssertionError("Should not here");
}
/**
* Returns true if the given object is a Stub.
*/
public static boolean isStub(Object obj) {
return (proxy == null) ? false : proxy.isStub(obj);
}
/**
* Returns the Delegate to which the given Stub delegates.
*/
public static Object getDelegate(Object stub) {
ensureAvailable();
return proxy.getDelegate(stub);
}
/**
* Sets the Delegate for a given Stub.
*/
public static void setDelegate(Object stub, Object delegate) {
ensureAvailable();
proxy.setDelegate(stub, delegate);
}
/**
* Returns the ORB associated with the given stub
*
* @throws UnsupportedOperationException
* if the object does not support the operation that
* was invoked
*/
public static Object getOrb(Object stub) {
ensureAvailable();
return proxy.getOrb(stub);
}
/**
* Connects the Stub to the given ORB.
*/
public static void connect(Object stub, Object orb)
throws IOException
{
if (proxy == null)
throw new IOException("Connection to ORB failed, RMI/IIOP not available");
proxy.connect(stub, orb);
}
/**
* Returns true if the given object is an ORB.
*/
public static boolean isOrb(Object obj) {
return (proxy == null) ? false : proxy.isOrb(obj);
}
/**
* Creates, and returns, a new ORB instance.
*/
public static Object createOrb(String[] args, Properties props)
throws IOException
{
if (proxy == null)
throw new IOException("ORB initialization failed, RMI/IIOP not available");
return proxy.createOrb(args, props);
}
/**
* Converts a string, produced by the object_to_string method, back
* to a CORBA object reference.
*/
public static Object stringToObject(Object orb, String str) {
ensureAvailable();
return proxy.stringToObject(orb, str);
}
/**
* Converts the given CORBA object reference to a string.
*/
public static String objectToString(Object orb, Object obj) {
ensureAvailable();
return proxy.objectToString(orb, obj);
}
/**
* Checks to ensure that an object of a remote or abstract interface
* type can be cast to a desired type.
*/
public static <T> T narrow(Object narrowFrom, Class<T> narrowTo) {
ensureAvailable();
return proxy.narrow(narrowFrom, narrowTo);
}
/**
* Makes a server object ready to receive remote calls
*/
public static void exportObject(Remote obj) throws IOException {
if (proxy == null)
throw new IOException("RMI object cannot be exported, RMI/IIOP not available");
proxy.exportObject(obj);
}
/**
* Deregisters a server object from the runtime.
*/
public static void unexportObject(Remote obj) throws IOException {
if (proxy == null)
throw new NoSuchObjectException("Object not exported");
proxy.unexportObject(obj);
}
/**
* Returns a stub for the given server object.
*/
public static Remote toStub(Remote obj) throws IOException {
if (proxy == null)
throw new NoSuchObjectException("Object not exported");
return proxy.toStub(obj);
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright (c) 2009, 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 com.sun.jmx.remote.internal;
import java.util.Properties;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.NoSuchObjectException;
/**
* An interface to a subset of the RMI-IIOP and CORBA APIs to avoid a
* static dependencies on the types defined by these APIs.
*/
public interface IIOPProxy {
/**
* Returns true if the given object is a Stub.
*/
boolean isStub(Object obj);
/**
* Returns the Delegate to which the given Stub delegates.
*/
Object getDelegate(Object stub);
/**
* Sets the Delegate for a given Stub.
*/
void setDelegate(Object stub, Object delegate);
/**
* Returns the ORB associated with the given stub
*
* @throws UnsupportedOperationException
* if the object does not support the operation that
* was invoked
*/
Object getOrb(Object stub);
/**
* Connects the Stub to the given ORB.
*/
void connect(Object stub, Object orb) throws RemoteException;
/**
* Returns true if the given object is an ORB.
*/
boolean isOrb(Object obj);
/**
* Creates, and returns, a new ORB instance.
*/
Object createOrb(String[] args, Properties props);
/**
* Converts a string, produced by the object_to_string method, back
* to a CORBA object reference.
*/
Object stringToObject(Object orb, String str);
/**
* Converts the given CORBA object reference to a string.
*/
String objectToString(Object orb, Object obj);
/**
* Checks to ensure that an object of a remote or abstract interface
* type can be cast to a desired type.
*/
<T> T narrow(Object narrowFrom, Class<T> narrowTo);
/**
* Makes a server object ready to receive remote calls
*/
void exportObject(Remote obj) throws RemoteException;
/**
* Deregisters a server object from the runtime.
*/
void unexportObject(Remote obj) throws NoSuchObjectException;
/**
* Returns a stub for the given server object.
*/
Remote toStub(Remote obj) throws NoSuchObjectException;
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) 2003, 2006, 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 com.sun.jmx.remote.internal;
import java.util.Set;
import javax.management.remote.NotificationResult;
import javax.management.remote.TargetedNotification;
/** A buffer of notifications received from an MBean server. */
public interface NotificationBuffer {
/**
* <p>Fetch notifications that match the given listeners.</p>
*
* <p>The operation only considers notifications with a sequence
* number at least <code>startSequenceNumber</code>. It will take
* no longer than <code>timeout</code>, and will return no more
* than <code>maxNotifications</code> different notifications.</p>
*
* <p>If there are no notifications matching the criteria, the
* operation will block until one arrives, subject to the
* timeout.</p>
*
* @param filter an object that will add notifications to a
* {@code List<TargetedNotification>} if they match the current
* listeners with their filters.
* @param startSequenceNumber the first sequence number to
* consider.
* @param timeout the maximum time to wait. May be 0 to indicate
* not to wait if there are no notifications.
* @param maxNotifications the maximum number of notifications to
* return. May be 0 to indicate a wait for eligible notifications
* that will return a usable <code>nextSequenceNumber</code>. The
* {@link TargetedNotification} array in the returned {@link
* NotificationResult} may contain more than this number of
* elements but will not contain more than this number of
* different notifications.
*/
public NotificationResult
fetchNotifications(NotificationBufferFilter filter,
long startSequenceNumber,
long timeout,
int maxNotifications)
throws InterruptedException;
/**
* <p>Discard this buffer.</p>
*/
public void dispose();
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2006, 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 com.sun.jmx.remote.internal;
import java.util.List;
import javax.management.Notification;
import javax.management.ObjectName;
import javax.management.remote.TargetedNotification;
public interface NotificationBufferFilter {
/**
* Add the given notification coming from the given MBean to the list
* iff it matches this filter's rules.
*/
public void apply(List<TargetedNotification> targetedNotifs,
ObjectName source, Notification notif);
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright (c) 2003, 2008, 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 com.sun.jmx.remote.internal;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.lang.reflect.Method;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.server.RemoteObject;
import java.rmi.server.RemoteRef;
@SuppressWarnings("deprecation")
public class ProxyRef implements RemoteRef {
private static final long serialVersionUID = -6503061366316814723L;
public ProxyRef(RemoteRef ref) {
this.ref = ref;
}
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException {
ref.readExternal(in);
}
public void writeExternal(ObjectOutput out) throws IOException {
ref.writeExternal(out);
}
/**
* @deprecated
*/
@Deprecated
public void invoke(java.rmi.server.RemoteCall call) throws Exception {
ref.invoke(call);
}
public Object invoke(Remote obj, Method method, Object[] params,
long opnum) throws Exception {
return ref.invoke(obj, method, params, opnum);
}
/**
* @deprecated
*/
@Deprecated
public void done(java.rmi.server.RemoteCall call) throws RemoteException {
ref.done(call);
}
public String getRefClass(ObjectOutput out) {
return ref.getRefClass(out);
}
/**
* @deprecated
*/
@Deprecated
public java.rmi.server.RemoteCall newCall(RemoteObject obj,
java.rmi.server.Operation[] op, int opnum,
long hash) throws RemoteException {
return ref.newCall(obj, op, opnum, hash);
}
public boolean remoteEquals(RemoteRef obj) {
return ref.remoteEquals(obj);
}
public int remoteHashCode() {
return ref.remoteHashCode();
}
public String remoteToString() {
return ref.remoteToString();
}
protected RemoteRef ref;
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) 2003, 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 com.sun.jmx.remote.internal;
import java.rmi.NoSuchObjectException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMIServerSocketFactory;
import java.rmi.server.UnicastRemoteObject;
/**
* <p>Unpublished interface controlling how the RMI Connector Server
* exports objects. The RMIServerImpl object and each
* RMIConnectionImpl object are exported using the exporter. The
* default exporter calls {@link
* UnicastRemoteObject#exportObject(Remote, int,
* RMIClientSocketFactory, RMIServerSocketFactory)} to export objects
* and {@link UnicastRemoteObject#unexportObject(Remote, boolean)} to
* unexport them. A replacement exporter can be specified via the
* {@link #EXPORTER_ATTRIBUTE} property in the environment Map passed
* to the RMI connector server.</p>
*/
public interface RMIExporter {
public static final String EXPORTER_ATTRIBUTE =
"com.sun.jmx.remote.rmi.exporter";
public Remote exportObject(Remote obj,
int port,
RMIClientSocketFactory csf,
RMIServerSocketFactory ssf)
throws RemoteException;
public boolean unexportObject(Remote obj, boolean force)
throws NoSuchObjectException;
}

View File

@@ -0,0 +1,232 @@
/*
* Copyright (c) 2003, 2004, 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 com.sun.jmx.remote.internal;
import java.io.IOException;
import com.sun.jmx.remote.util.ClassLogger;
public abstract class ServerCommunicatorAdmin {
public ServerCommunicatorAdmin(long timeout) {
if (logger.traceOn()) {
logger.trace("Constructor",
"Creates a new ServerCommunicatorAdmin object "+
"with the timeout "+timeout);
}
this.timeout = timeout;
timestamp = 0;
if (timeout < Long.MAX_VALUE) {
Runnable timeoutTask = new Timeout();
final Thread t = new Thread(timeoutTask);
t.setName("JMX server connection timeout " + t.getId());
// If you change this name you will need to change a unit test
// (NoServerTimeoutTest)
t.setDaemon(true);
t.start();
}
}
/**
* Tells that a new request message is received.
* A caller of this method should always call the method
* <code>rspOutgoing</code> to inform that a response is sent out
* for the received request.
* @return the value of the termination flag:
* <ul><code>true</code> if the connection is already being terminated,
* <br><code>false</code> otherwise.</ul>
*/
public boolean reqIncoming() {
if (logger.traceOn()) {
logger.trace("reqIncoming", "Receive a new request.");
}
synchronized(lock) {
if (terminated) {
logger.warning("reqIncoming",
"The server has decided to close " +
"this client connection.");
}
++currentJobs;
return terminated;
}
}
/**
* Tells that a response is sent out for a received request.
* @return the value of the termination flag:
* <ul><code>true</code> if the connection is already being terminated,
* <br><code>false</code> otherwise.</ul>
*/
public boolean rspOutgoing() {
if (logger.traceOn()) {
logger.trace("reqIncoming", "Finish a request.");
}
synchronized(lock) {
if (--currentJobs == 0) {
timestamp = System.currentTimeMillis();
logtime("Admin: Timestamp=",timestamp);
// tells the adminor to restart waiting with timeout
lock.notify();
}
return terminated;
}
}
/**
* Called by this class to tell an implementation to do stop.
*/
protected abstract void doStop();
/**
* Terminates this object.
* Called only by outside, so do not need to call doStop
*/
public void terminate() {
if (logger.traceOn()) {
logger.trace("terminate",
"terminate the ServerCommunicatorAdmin object.");
}
synchronized(lock) {
if (terminated) {
return;
}
terminated = true;
// tell Timeout to terminate
lock.notify();
}
}
// --------------------------------------------------------------
// private classes
// --------------------------------------------------------------
private class Timeout implements Runnable {
public void run() {
boolean stopping = false;
synchronized(lock) {
if (timestamp == 0) timestamp = System.currentTimeMillis();
logtime("Admin: timeout=",timeout);
logtime("Admin: Timestamp=",timestamp);
while(!terminated) {
try {
// wait until there is no more job
while(!terminated && currentJobs != 0) {
if (logger.traceOn()) {
logger.trace("Timeout-run",
"Waiting without timeout.");
}
lock.wait();
}
if (terminated) return;
final long remaining =
timeout - (System.currentTimeMillis() - timestamp);
logtime("Admin: remaining timeout=",remaining);
if (remaining > 0) {
if (logger.traceOn()) {
logger.trace("Timeout-run",
"Waiting with timeout: "+
remaining + " ms remaining");
}
lock.wait(remaining);
}
if (currentJobs > 0) continue;
final long elapsed =
System.currentTimeMillis() - timestamp;
logtime("Admin: elapsed=",elapsed);
if (!terminated && elapsed > timeout) {
if (logger.traceOn()) {
logger.trace("Timeout-run",
"timeout elapsed");
}
logtime("Admin: timeout elapsed! "+
elapsed+">",timeout);
// stopping
terminated = true;
stopping = true;
break;
}
} catch (InterruptedException ire) {
logger.warning("Timeout-run","Unexpected Exception: "+
ire);
logger.debug("Timeout-run",ire);
return;
}
}
}
if (stopping) {
if (logger.traceOn()) {
logger.trace("Timeout-run", "Call the doStop.");
}
doStop();
}
}
}
private void logtime(String desc,long time) {
timelogger.trace("synchro",desc+time);
}
// --------------------------------------------------------------
// private variables
// --------------------------------------------------------------
private long timestamp;
private final int[] lock = new int[0];
private int currentJobs = 0;
private long timeout;
// state issue
private boolean terminated = false;
private static final ClassLogger logger =
new ClassLogger("javax.management.remote.misc",
"ServerCommunicatorAdmin");
private static final ClassLogger timelogger =
new ClassLogger("javax.management.remote.timeout",
"ServerCommunicatorAdmin");
}

View File

@@ -0,0 +1,506 @@
/*
* Copyright (c) 2002, 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 com.sun.jmx.remote.internal;
import com.sun.jmx.remote.security.NotificationAccessController;
import com.sun.jmx.remote.util.ClassLogger;
import com.sun.jmx.remote.util.EnvHelp;
import java.io.IOException;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.management.InstanceNotFoundException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanPermission;
import javax.management.MBeanServer;
import javax.management.MBeanServerDelegate;
import javax.management.MBeanServerNotification;
import javax.management.Notification;
import javax.management.NotificationBroadcaster;
import javax.management.NotificationFilter;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.remote.NotificationResult;
import javax.management.remote.TargetedNotification;
import javax.management.MalformedObjectNameException;
import javax.security.auth.Subject;
public class ServerNotifForwarder {
public ServerNotifForwarder(MBeanServer mbeanServer,
Map<String, ?> env,
NotificationBuffer notifBuffer,
String connectionId) {
this.mbeanServer = mbeanServer;
this.notifBuffer = notifBuffer;
this.connectionId = connectionId;
connectionTimeout = EnvHelp.getServerConnectionTimeout(env);
String stringBoolean = (String) env.get("jmx.remote.x.check.notification.emission");
checkNotificationEmission = EnvHelp.computeBooleanFromString( stringBoolean );
notificationAccessController =
EnvHelp.getNotificationAccessController(env);
}
public Integer addNotificationListener(final ObjectName name,
final NotificationFilter filter)
throws InstanceNotFoundException, IOException {
if (logger.traceOn()) {
logger.trace("addNotificationListener",
"Add a listener at " + name);
}
checkState();
// Explicitly check MBeanPermission for addNotificationListener
//
checkMBeanPermission(name, "addNotificationListener");
if (notificationAccessController != null) {
notificationAccessController.addNotificationListener(
connectionId, name, getSubject());
}
try {
boolean instanceOf =
AccessController.doPrivileged(
new PrivilegedExceptionAction<Boolean>() {
public Boolean run() throws InstanceNotFoundException {
return mbeanServer.isInstanceOf(name, broadcasterClass);
}
});
if (!instanceOf) {
throw new IllegalArgumentException("The specified MBean [" +
name + "] is not a " +
"NotificationBroadcaster " +
"object.");
}
} catch (PrivilegedActionException e) {
throw (InstanceNotFoundException) extractException(e);
}
final Integer id = getListenerID();
// 6238731: set the default domain if no domain is set.
ObjectName nn = name;
if (name.getDomain() == null || name.getDomain().equals("")) {
try {
nn = ObjectName.getInstance(mbeanServer.getDefaultDomain(),
name.getKeyPropertyList());
} catch (MalformedObjectNameException mfoe) {
// impossible, but...
IOException ioe = new IOException(mfoe.getMessage());
ioe.initCause(mfoe);
throw ioe;
}
}
synchronized (listenerMap) {
IdAndFilter idaf = new IdAndFilter(id, filter);
Set<IdAndFilter> set = listenerMap.get(nn);
// Tread carefully because if set.size() == 1 it may be the
// Collections.singleton we make here, which is unmodifiable.
if (set == null)
set = Collections.singleton(idaf);
else {
if (set.size() == 1)
set = new HashSet<IdAndFilter>(set);
set.add(idaf);
}
listenerMap.put(nn, set);
}
return id;
}
public void removeNotificationListener(ObjectName name,
Integer[] listenerIDs)
throws Exception {
if (logger.traceOn()) {
logger.trace("removeNotificationListener",
"Remove some listeners from " + name);
}
checkState();
// Explicitly check MBeanPermission for removeNotificationListener
//
checkMBeanPermission(name, "removeNotificationListener");
if (notificationAccessController != null) {
notificationAccessController.removeNotificationListener(
connectionId, name, getSubject());
}
Exception re = null;
for (int i = 0 ; i < listenerIDs.length ; i++) {
try {
removeNotificationListener(name, listenerIDs[i]);
} catch (Exception e) {
// Give back the first exception
//
if (re != null) {
re = e;
}
}
}
if (re != null) {
throw re;
}
}
public void removeNotificationListener(ObjectName name, Integer listenerID)
throws
InstanceNotFoundException,
ListenerNotFoundException,
IOException {
if (logger.traceOn()) {
logger.trace("removeNotificationListener",
"Remove the listener " + listenerID + " from " + name);
}
checkState();
if (name != null && !name.isPattern()) {
if (!mbeanServer.isRegistered(name)) {
throw new InstanceNotFoundException("The MBean " + name +
" is not registered.");
}
}
synchronized (listenerMap) {
// Tread carefully because if set.size() == 1 it may be a
// Collections.singleton, which is unmodifiable.
Set<IdAndFilter> set = listenerMap.get(name);
IdAndFilter idaf = new IdAndFilter(listenerID, null);
if (set == null || !set.contains(idaf))
throw new ListenerNotFoundException("Listener not found");
if (set.size() == 1)
listenerMap.remove(name);
else
set.remove(idaf);
}
}
/* This is the object that will apply our filtering to candidate
* notifications. First of all, if there are no listeners for the
* ObjectName that the notification is coming from, we go no further.
* Then, for each listener, we must apply the corresponding filter (if any)
* and ignore the listener if the filter rejects. Finally, we apply
* some access checks which may also reject the listener.
*
* A given notification may trigger several listeners on the same MBean,
* which is why listenerMap is a Map<ObjectName, Set<IdAndFilter>> and
* why we add the found notifications to a supplied List rather than
* just returning a boolean.
*/
private final NotifForwarderBufferFilter bufferFilter = new NotifForwarderBufferFilter();
final class NotifForwarderBufferFilter implements NotificationBufferFilter {
public void apply(List<TargetedNotification> targetedNotifs,
ObjectName source, Notification notif) {
// We proceed in two stages here, to avoid holding the listenerMap
// lock while invoking the filters (which are user code).
final IdAndFilter[] candidates;
synchronized (listenerMap) {
final Set<IdAndFilter> set = listenerMap.get(source);
if (set == null) {
logger.debug("bufferFilter", "no listeners for this name");
return;
}
candidates = new IdAndFilter[set.size()];
set.toArray(candidates);
}
// We don't synchronize on targetedNotifs, because it is a local
// variable of our caller and no other thread can see it.
for (IdAndFilter idaf : candidates) {
final NotificationFilter nf = idaf.getFilter();
if (nf == null || nf.isNotificationEnabled(notif)) {
logger.debug("bufferFilter", "filter matches");
final TargetedNotification tn =
new TargetedNotification(notif, idaf.getId());
if (allowNotificationEmission(source, tn))
targetedNotifs.add(tn);
}
}
}
};
public NotificationResult fetchNotifs(long startSequenceNumber,
long timeout,
int maxNotifications) {
if (logger.traceOn()) {
logger.trace("fetchNotifs", "Fetching notifications, the " +
"startSequenceNumber is " + startSequenceNumber +
", the timeout is " + timeout +
", the maxNotifications is " + maxNotifications);
}
NotificationResult nr;
final long t = Math.min(connectionTimeout, timeout);
try {
nr = notifBuffer.fetchNotifications(bufferFilter,
startSequenceNumber,
t, maxNotifications);
snoopOnUnregister(nr);
} catch (InterruptedException ire) {
nr = new NotificationResult(0L, 0L, new TargetedNotification[0]);
}
if (logger.traceOn()) {
logger.trace("fetchNotifs", "Forwarding the notifs: "+nr);
}
return nr;
}
// The standard RMI connector client will register a listener on the MBeanServerDelegate
// in order to be told when MBeans are unregistered. We snoop on fetched notifications
// so that we can know too, and remove the corresponding entry from the listenerMap.
// See 6957378.
private void snoopOnUnregister(NotificationResult nr) {
List<IdAndFilter> copy = null;
synchronized (listenerMap) {
Set<IdAndFilter> delegateSet = listenerMap.get(MBeanServerDelegate.DELEGATE_NAME);
if (delegateSet == null || delegateSet.isEmpty()) {
return;
}
copy = new ArrayList<>(delegateSet);
}
for (TargetedNotification tn : nr.getTargetedNotifications()) {
Integer id = tn.getListenerID();
for (IdAndFilter idaf : copy) {
if (idaf.id == id) {
// This is a notification from the MBeanServerDelegate.
Notification n = tn.getNotification();
if (n instanceof MBeanServerNotification &&
n.getType().equals(MBeanServerNotification.UNREGISTRATION_NOTIFICATION)) {
MBeanServerNotification mbsn = (MBeanServerNotification) n;
ObjectName gone = mbsn.getMBeanName();
synchronized (listenerMap) {
listenerMap.remove(gone);
}
}
}
}
}
}
public void terminate() {
if (logger.traceOn()) {
logger.trace("terminate", "Be called.");
}
synchronized(terminationLock) {
if (terminated) {
return;
}
terminated = true;
synchronized(listenerMap) {
listenerMap.clear();
}
}
if (logger.traceOn()) {
logger.trace("terminate", "Terminated.");
}
}
//----------------
// PRIVATE METHODS
//----------------
private Subject getSubject() {
return Subject.getSubject(AccessController.getContext());
}
private void checkState() throws IOException {
synchronized(terminationLock) {
if (terminated) {
throw new IOException("The connection has been terminated.");
}
}
}
private Integer getListenerID() {
synchronized(listenerCounterLock) {
return listenerCounter++;
}
}
/**
* Explicitly check the MBeanPermission for
* the current access control context.
*/
public final void checkMBeanPermission(
final ObjectName name, final String actions)
throws InstanceNotFoundException, SecurityException {
checkMBeanPermission(mbeanServer,name,actions);
}
static void checkMBeanPermission(
final MBeanServer mbs, final ObjectName name, final String actions)
throws InstanceNotFoundException, SecurityException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
AccessControlContext acc = AccessController.getContext();
ObjectInstance oi;
try {
oi = AccessController.doPrivileged(
new PrivilegedExceptionAction<ObjectInstance>() {
public ObjectInstance run()
throws InstanceNotFoundException {
return mbs.getObjectInstance(name);
}
});
} catch (PrivilegedActionException e) {
throw (InstanceNotFoundException) extractException(e);
}
String classname = oi.getClassName();
MBeanPermission perm = new MBeanPermission(
classname,
null,
name,
actions);
sm.checkPermission(perm, acc);
}
}
/**
* Check if the caller has the right to get the following notifications.
*/
private boolean allowNotificationEmission(ObjectName name,
TargetedNotification tn) {
try {
if (checkNotificationEmission) {
checkMBeanPermission(name, "addNotificationListener");
}
if (notificationAccessController != null) {
notificationAccessController.fetchNotification(
connectionId, name, tn.getNotification(), getSubject());
}
return true;
} catch (SecurityException e) {
if (logger.debugOn()) {
logger.debug("fetchNotifs", "Notification " +
tn.getNotification() + " not forwarded: the " +
"caller didn't have the required access rights");
}
return false;
} catch (Exception e) {
if (logger.debugOn()) {
logger.debug("fetchNotifs", "Notification " +
tn.getNotification() + " not forwarded: " +
"got an unexpected exception: " + e);
}
return false;
}
}
/**
* 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 class IdAndFilter {
private Integer id;
private NotificationFilter filter;
IdAndFilter(Integer id, NotificationFilter filter) {
this.id = id;
this.filter = filter;
}
Integer getId() {
return this.id;
}
NotificationFilter getFilter() {
return this.filter;
}
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public boolean equals(Object o) {
return ((o instanceof IdAndFilter) &&
((IdAndFilter) o).getId().equals(getId()));
}
}
//------------------
// PRIVATE VARIABLES
//------------------
private MBeanServer mbeanServer;
private final String connectionId;
private final long connectionTimeout;
private static int listenerCounter = 0;
private final static int[] listenerCounterLock = new int[0];
private NotificationBuffer notifBuffer;
private final Map<ObjectName, Set<IdAndFilter>> listenerMap =
new HashMap<ObjectName, Set<IdAndFilter>>();
private boolean terminated = false;
private final int[] terminationLock = new int[0];
static final String broadcasterClass =
NotificationBroadcaster.class.getName();
private final boolean checkNotificationEmission;
private final NotificationAccessController notificationAccessController;
private static final ClassLogger logger =
new ClassLogger("javax.management.remote.misc", "ServerNotifForwarder");
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2003, 2008, 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 com.sun.jmx.remote.internal;
import java.io.IOException;
import java.rmi.MarshalledObject;
public interface Unmarshal {
public Object get(MarshalledObject<?> mo)
throws IOException, ClassNotFoundException;
}