feat(jdk8): move files to new folder to avoid resources compiled.
This commit is contained in:
@@ -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();
|
||||
}
|
||||
102
jdkSrc/jdk8/com/sun/jmx/remote/internal/ArrayQueue.java
Normal file
102
jdkSrc/jdk8/com/sun/jmx/remote/internal/ArrayQueue.java
Normal 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;
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
106
jdkSrc/jdk8/com/sun/jmx/remote/internal/ClientListenerInfo.java
Normal file
106
jdkSrc/jdk8/com/sun/jmx/remote/internal/ClientListenerInfo.java
Normal 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;
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
190
jdkSrc/jdk8/com/sun/jmx/remote/internal/IIOPHelper.java
Normal file
190
jdkSrc/jdk8/com/sun/jmx/remote/internal/IIOPHelper.java
Normal 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);
|
||||
}
|
||||
}
|
||||
110
jdkSrc/jdk8/com/sun/jmx/remote/internal/IIOPProxy.java
Normal file
110
jdkSrc/jdk8/com/sun/jmx/remote/internal/IIOPProxy.java
Normal 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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
103
jdkSrc/jdk8/com/sun/jmx/remote/internal/ProxyRef.java
Normal file
103
jdkSrc/jdk8/com/sun/jmx/remote/internal/ProxyRef.java
Normal 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;
|
||||
}
|
||||
59
jdkSrc/jdk8/com/sun/jmx/remote/internal/RMIExporter.java
Normal file
59
jdkSrc/jdk8/com/sun/jmx/remote/internal/RMIExporter.java
Normal 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;
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
34
jdkSrc/jdk8/com/sun/jmx/remote/internal/Unmarshal.java
Normal file
34
jdkSrc/jdk8/com/sun/jmx/remote/internal/Unmarshal.java
Normal 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;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.protocol.iiop;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.management.remote.JMXConnectorProvider;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
import javax.management.remote.rmi.RMIConnector;
|
||||
|
||||
public class ClientProvider implements JMXConnectorProvider {
|
||||
|
||||
public JMXConnector newJMXConnector(JMXServiceURL serviceURL,
|
||||
Map<String,?> environment)
|
||||
throws IOException {
|
||||
if (!serviceURL.getProtocol().equals("iiop")) {
|
||||
throw new MalformedURLException("Protocol not iiop: " +
|
||||
serviceURL.getProtocol());
|
||||
}
|
||||
return new RMIConnector(serviceURL, environment);
|
||||
}
|
||||
}
|
||||
157
jdkSrc/jdk8/com/sun/jmx/remote/protocol/iiop/IIOPProxyImpl.java
Normal file
157
jdkSrc/jdk8/com/sun/jmx/remote/protocol/iiop/IIOPProxyImpl.java
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (c) 2009,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.protocol.iiop;
|
||||
|
||||
import org.omg.CORBA.ORB;
|
||||
import org.omg.CORBA.portable.Delegate;
|
||||
import javax.rmi.PortableRemoteObject;
|
||||
import javax.rmi.CORBA.Stub;
|
||||
|
||||
import java.util.Properties;
|
||||
import java.rmi.Remote;
|
||||
import java.rmi.RemoteException;
|
||||
import java.rmi.NoSuchObjectException;
|
||||
|
||||
import com.sun.jmx.remote.internal.IIOPProxy;
|
||||
import java.io.SerializablePermission;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.Permissions;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.security.ProtectionDomain;
|
||||
|
||||
/**
|
||||
* An implementation of IIOPProxy that simply delegates to the appropriate
|
||||
* RMI-IIOP and CORBA APIs.
|
||||
*/
|
||||
|
||||
public class IIOPProxyImpl implements IIOPProxy {
|
||||
// special ACC used to initialize the IIOP stub
|
||||
// the only allowed privilege is SerializablePermission("enableSubclassImplementation")
|
||||
private static final AccessControlContext STUB_ACC;
|
||||
|
||||
static {
|
||||
Permissions p = new Permissions();
|
||||
p.add(new SerializablePermission("enableSubclassImplementation"));
|
||||
STUB_ACC = new AccessControlContext(
|
||||
new ProtectionDomain[]{
|
||||
new ProtectionDomain(null, p)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public IIOPProxyImpl() { }
|
||||
|
||||
@Override
|
||||
public boolean isStub(Object obj) {
|
||||
return (obj instanceof Stub);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDelegate(Object stub) {
|
||||
return ((Stub)stub)._get_delegate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDelegate(Object stub, Object delegate) {
|
||||
((Stub)stub)._set_delegate((Delegate)delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getOrb(Object stub) {
|
||||
try {
|
||||
return ((Stub)stub)._orb();
|
||||
} catch (org.omg.CORBA.BAD_OPERATION x) {
|
||||
throw new UnsupportedOperationException(x);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(Object stub, Object orb)
|
||||
throws RemoteException
|
||||
{
|
||||
((Stub)stub).connect((ORB)orb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOrb(Object obj) {
|
||||
return (obj instanceof ORB);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createOrb(String[] args, Properties props) {
|
||||
return ORB.init(args, props);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object stringToObject(Object orb, String str) {
|
||||
return ((ORB)orb).string_to_object(str);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String objectToString(Object orb, Object obj) {
|
||||
return ((ORB)orb).object_to_string((org.omg.CORBA.Object)obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T narrow(Object narrowFrom, Class<T> narrowTo) {
|
||||
return (T)PortableRemoteObject.narrow(narrowFrom, narrowTo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportObject(Remote obj) throws RemoteException {
|
||||
PortableRemoteObject.exportObject(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unexportObject(Remote obj) throws NoSuchObjectException {
|
||||
PortableRemoteObject.unexportObject(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Remote toStub(final Remote obj) throws NoSuchObjectException {
|
||||
if (System.getSecurityManager() == null) {
|
||||
return PortableRemoteObject.toStub(obj);
|
||||
} else {
|
||||
try {
|
||||
return AccessController.doPrivileged(new PrivilegedExceptionAction<Remote>() {
|
||||
|
||||
@Override
|
||||
public Remote run() throws Exception {
|
||||
return PortableRemoteObject.toStub(obj);
|
||||
}
|
||||
}, STUB_ACC);
|
||||
} catch (PrivilegedActionException e) {
|
||||
if (e.getException() instanceof NoSuchObjectException) {
|
||||
throw (NoSuchObjectException)e.getException();
|
||||
}
|
||||
throw new RuntimeException("Unexpected exception type", e.getException());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* 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.protocol.iiop;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.omg.CORBA.Any;
|
||||
import org.omg.CORBA.Context;
|
||||
import org.omg.CORBA.NO_IMPLEMENT;
|
||||
import org.omg.CORBA.ORB;
|
||||
import org.omg.CORBA.TypeCode;
|
||||
import org.omg.CORBA.portable.BoxedValueHelper;
|
||||
|
||||
@SuppressWarnings({"deprecation", "rawtypes"})
|
||||
public class ProxyInputStream extends org.omg.CORBA_2_3.portable.InputStream {
|
||||
public ProxyInputStream(org.omg.CORBA.portable.InputStream in) {
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
public boolean read_boolean() {
|
||||
return in.read_boolean();
|
||||
}
|
||||
|
||||
public char read_char() {
|
||||
return in.read_char();
|
||||
}
|
||||
|
||||
public char read_wchar() {
|
||||
return in.read_wchar();
|
||||
}
|
||||
|
||||
public byte read_octet() {
|
||||
return in.read_octet();
|
||||
}
|
||||
|
||||
public short read_short() {
|
||||
return in.read_short();
|
||||
}
|
||||
|
||||
public short read_ushort() {
|
||||
return in.read_ushort();
|
||||
}
|
||||
|
||||
public int read_long() {
|
||||
return in.read_long();
|
||||
}
|
||||
|
||||
public int read_ulong() {
|
||||
return in.read_ulong();
|
||||
}
|
||||
|
||||
public long read_longlong() {
|
||||
return in.read_longlong();
|
||||
}
|
||||
|
||||
public long read_ulonglong() {
|
||||
return in.read_ulonglong();
|
||||
}
|
||||
|
||||
public float read_float() {
|
||||
return in.read_float();
|
||||
}
|
||||
|
||||
public double read_double() {
|
||||
return in.read_double();
|
||||
}
|
||||
|
||||
public String read_string() {
|
||||
return in.read_string();
|
||||
}
|
||||
|
||||
public String read_wstring() {
|
||||
return in.read_wstring();
|
||||
}
|
||||
|
||||
public void read_boolean_array(boolean[] value, int offset, int length) {
|
||||
in.read_boolean_array(value, offset, length);
|
||||
}
|
||||
|
||||
public void read_char_array(char[] value, int offset, int length) {
|
||||
in.read_char_array(value, offset, length);
|
||||
}
|
||||
|
||||
public void read_wchar_array(char[] value, int offset, int length) {
|
||||
in.read_wchar_array(value, offset, length);
|
||||
}
|
||||
|
||||
public void read_octet_array(byte[] value, int offset, int length) {
|
||||
in.read_octet_array(value, offset, length);
|
||||
}
|
||||
|
||||
public void read_short_array(short[] value, int offset, int length) {
|
||||
in.read_short_array(value, offset, length);
|
||||
}
|
||||
|
||||
public void read_ushort_array(short[] value, int offset, int length) {
|
||||
in.read_ushort_array(value, offset, length);
|
||||
}
|
||||
|
||||
public void read_long_array(int[] value, int offset, int length) {
|
||||
in.read_long_array(value, offset, length);
|
||||
}
|
||||
|
||||
public void read_ulong_array(int[] value, int offset, int length) {
|
||||
in.read_ulong_array(value, offset, length);
|
||||
}
|
||||
|
||||
public void read_longlong_array(long[] value, int offset, int length) {
|
||||
in.read_longlong_array(value, offset, length);
|
||||
}
|
||||
|
||||
public void read_ulonglong_array(long[] value, int offset, int length) {
|
||||
in.read_ulonglong_array(value, offset, length);
|
||||
}
|
||||
|
||||
public void read_float_array(float[] value, int offset, int length) {
|
||||
in.read_float_array(value, offset, length);
|
||||
}
|
||||
|
||||
public void read_double_array(double[] value, int offset, int length) {
|
||||
in.read_double_array(value, offset, length);
|
||||
}
|
||||
|
||||
public org.omg.CORBA.Object read_Object() {
|
||||
return in.read_Object();
|
||||
}
|
||||
|
||||
public TypeCode read_TypeCode() {
|
||||
return in.read_TypeCode();
|
||||
}
|
||||
|
||||
public Any read_any() {
|
||||
return in.read_any();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public org.omg.CORBA.Principal read_Principal() {
|
||||
return in.read_Principal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return in.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal read_fixed() {
|
||||
return in.read_fixed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context read_Context() {
|
||||
return in.read_Context();
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.omg.CORBA.Object read_Object(java.lang.Class clz) {
|
||||
return in.read_Object(clz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ORB orb() {
|
||||
return in.orb();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable read_value() {
|
||||
return narrow().read_value();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable read_value(Class clz) {
|
||||
return narrow().read_value(clz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable read_value(BoxedValueHelper factory) {
|
||||
return narrow().read_value(factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable read_value(String rep_id) {
|
||||
return narrow().read_value(rep_id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable read_value(Serializable value) {
|
||||
return narrow().read_value(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object read_abstract_interface() {
|
||||
return narrow().read_abstract_interface();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object read_abstract_interface(Class clz) {
|
||||
return narrow().read_abstract_interface(clz);
|
||||
}
|
||||
|
||||
protected org.omg.CORBA_2_3.portable.InputStream narrow() {
|
||||
if (in instanceof org.omg.CORBA_2_3.portable.InputStream)
|
||||
return (org.omg.CORBA_2_3.portable.InputStream) in;
|
||||
throw new NO_IMPLEMENT();
|
||||
}
|
||||
|
||||
public org.omg.CORBA.portable.InputStream getProxiedInputStream() {
|
||||
return in;
|
||||
}
|
||||
|
||||
protected final org.omg.CORBA.portable.InputStream in;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.protocol.iiop;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXConnectorServerProvider;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
import javax.management.remote.rmi.RMIConnectorServer;
|
||||
|
||||
public class ServerProvider implements JMXConnectorServerProvider {
|
||||
|
||||
public JMXConnectorServer newJMXConnectorServer(JMXServiceURL serviceURL,
|
||||
Map<String,?> environment,
|
||||
MBeanServer mbeanServer)
|
||||
throws IOException {
|
||||
if (!serviceURL.getProtocol().equals("iiop")) {
|
||||
throw new MalformedURLException("Protocol not iiop: " +
|
||||
serviceURL.getProtocol());
|
||||
}
|
||||
return new RMIConnectorServer(serviceURL, environment, mbeanServer);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 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.protocol.rmi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.management.remote.JMXConnectorProvider;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
import javax.management.remote.rmi.RMIConnector;
|
||||
|
||||
public class ClientProvider implements JMXConnectorProvider {
|
||||
|
||||
public JMXConnector newJMXConnector(JMXServiceURL serviceURL,
|
||||
Map<String,?> environment)
|
||||
throws IOException {
|
||||
if (!serviceURL.getProtocol().equals("rmi")) {
|
||||
throw new MalformedURLException("Protocol not rmi: " +
|
||||
serviceURL.getProtocol());
|
||||
}
|
||||
return new RMIConnector(serviceURL, environment);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.protocol.rmi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXConnectorServerProvider;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
import javax.management.remote.rmi.RMIConnectorServer;
|
||||
|
||||
public class ServerProvider implements JMXConnectorServerProvider {
|
||||
|
||||
public JMXConnectorServer newJMXConnectorServer(JMXServiceURL serviceURL,
|
||||
Map<String,?> environment,
|
||||
MBeanServer mbeanServer)
|
||||
throws IOException {
|
||||
if (!serviceURL.getProtocol().equals("rmi")) {
|
||||
throw new MalformedURLException("Protocol not rmi: " +
|
||||
serviceURL.getProtocol());
|
||||
}
|
||||
return new RMIConnectorServer(serviceURL, environment, mbeanServer);
|
||||
}
|
||||
|
||||
}
|
||||
570
jdkSrc/jdk8/com/sun/jmx/remote/security/FileLoginModule.java
Normal file
570
jdkSrc/jdk8/com/sun/jmx/remote/security/FileLoginModule.java
Normal file
@@ -0,0 +1,570 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 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.security;
|
||||
|
||||
import com.sun.jmx.mbeanserver.GetPropertyAction;
|
||||
import com.sun.jmx.mbeanserver.Util;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FilePermission;
|
||||
import java.io.IOException;
|
||||
import java.security.AccessControlException;
|
||||
import java.security.AccessController;
|
||||
import java.util.Arrays;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.security.auth.*;
|
||||
import javax.security.auth.callback.*;
|
||||
import javax.security.auth.login.*;
|
||||
import javax.security.auth.spi.*;
|
||||
import javax.management.remote.JMXPrincipal;
|
||||
|
||||
import com.sun.jmx.remote.util.ClassLogger;
|
||||
import com.sun.jmx.remote.util.EnvHelp;
|
||||
import sun.management.jmxremote.ConnectorBootstrap;
|
||||
|
||||
/**
|
||||
* This {@link LoginModule} performs file-based authentication.
|
||||
*
|
||||
* <p> A supplied username and password is verified against the
|
||||
* corresponding user credentials stored in a designated password file.
|
||||
* If successful then a new {@link JMXPrincipal} is created with the
|
||||
* user's name and it is associated with the current {@link Subject}.
|
||||
* Such principals may be identified and granted management privileges in
|
||||
* the access control file for JMX remote management or in a Java security
|
||||
* policy.
|
||||
*
|
||||
* <p> The password file comprises a list of key-value pairs as specified in
|
||||
* {@link Properties}. The key represents a user's name and the value is its
|
||||
* associated cleartext password. By default, the following password file is
|
||||
* used:
|
||||
* <pre>
|
||||
* ${java.home}/lib/management/jmxremote.password
|
||||
* </pre>
|
||||
* A different password file can be specified via the <code>passwordFile</code>
|
||||
* configuration option.
|
||||
*
|
||||
* <p> This module recognizes the following <code>Configuration</code> options:
|
||||
* <dl>
|
||||
* <dt> <code>passwordFile</code> </dt>
|
||||
* <dd> the path to an alternative password file. It is used instead of
|
||||
* the default password file.</dd>
|
||||
*
|
||||
* <dt> <code>useFirstPass</code> </dt>
|
||||
* <dd> if <code>true</code>, this module retrieves the username and password
|
||||
* from the module's shared state, using "javax.security.auth.login.name"
|
||||
* and "javax.security.auth.login.password" as the respective keys. The
|
||||
* retrieved values are used for authentication. If authentication fails,
|
||||
* no attempt for a retry is made, and the failure is reported back to
|
||||
* the calling application.</dd>
|
||||
*
|
||||
* <dt> <code>tryFirstPass</code> </dt>
|
||||
* <dd> if <code>true</code>, this module retrieves the username and password
|
||||
* from the module's shared state, using "javax.security.auth.login.name"
|
||||
* and "javax.security.auth.login.password" as the respective keys. The
|
||||
* retrieved values are used for authentication. If authentication fails,
|
||||
* the module uses the CallbackHandler to retrieve a new username and
|
||||
* password, and another attempt to authenticate is made. If the
|
||||
* authentication fails, the failure is reported back to the calling
|
||||
* application.</dd>
|
||||
*
|
||||
* <dt> <code>storePass</code> </dt>
|
||||
* <dd> if <code>true</code>, this module stores the username and password
|
||||
* obtained from the CallbackHandler in the module's shared state, using
|
||||
* "javax.security.auth.login.name" and
|
||||
* "javax.security.auth.login.password" as the respective keys. This is
|
||||
* not performed if existing values already exist for the username and
|
||||
* password in the shared state, or if authentication fails.</dd>
|
||||
*
|
||||
* <dt> <code>clearPass</code> </dt>
|
||||
* <dd> if <code>true</code>, this module clears the username and password
|
||||
* stored in the module's shared state after both phases of authentication
|
||||
* (login and commit) have completed.</dd>
|
||||
* </dl>
|
||||
*/
|
||||
public class FileLoginModule implements LoginModule {
|
||||
|
||||
// Location of the default password file
|
||||
private static final String DEFAULT_PASSWORD_FILE_NAME =
|
||||
AccessController.doPrivileged(new GetPropertyAction("java.home")) +
|
||||
File.separatorChar + "lib" +
|
||||
File.separatorChar + "management" + File.separatorChar +
|
||||
ConnectorBootstrap.DefaultValues.PASSWORD_FILE_NAME;
|
||||
|
||||
// Key to retrieve the stored username
|
||||
private static final String USERNAME_KEY =
|
||||
"javax.security.auth.login.name";
|
||||
|
||||
// Key to retrieve the stored password
|
||||
private static final String PASSWORD_KEY =
|
||||
"javax.security.auth.login.password";
|
||||
|
||||
// Log messages
|
||||
private static final ClassLogger logger =
|
||||
new ClassLogger("javax.management.remote.misc", "FileLoginModule");
|
||||
|
||||
// Configurable options
|
||||
private boolean useFirstPass = false;
|
||||
private boolean tryFirstPass = false;
|
||||
private boolean storePass = false;
|
||||
private boolean clearPass = false;
|
||||
|
||||
// Authentication status
|
||||
private boolean succeeded = false;
|
||||
private boolean commitSucceeded = false;
|
||||
|
||||
// Supplied username and password
|
||||
private String username;
|
||||
private char[] password;
|
||||
private JMXPrincipal user;
|
||||
|
||||
// Initial state
|
||||
private Subject subject;
|
||||
private CallbackHandler callbackHandler;
|
||||
private Map<String, Object> sharedState;
|
||||
private Map<String, ?> options;
|
||||
private String passwordFile;
|
||||
private String passwordFileDisplayName;
|
||||
private boolean userSuppliedPasswordFile;
|
||||
private boolean hasJavaHomePermission;
|
||||
private Properties userCredentials;
|
||||
|
||||
/**
|
||||
* Initialize this <code>LoginModule</code>.
|
||||
*
|
||||
* @param subject the <code>Subject</code> to be authenticated.
|
||||
* @param callbackHandler a <code>CallbackHandler</code> to acquire the
|
||||
* user's name and password.
|
||||
* @param sharedState shared <code>LoginModule</code> state.
|
||||
* @param options options specified in the login
|
||||
* <code>Configuration</code> for this particular
|
||||
* <code>LoginModule</code>.
|
||||
*/
|
||||
public void initialize(Subject subject, CallbackHandler callbackHandler,
|
||||
Map<String,?> sharedState,
|
||||
Map<String,?> options)
|
||||
{
|
||||
|
||||
this.subject = subject;
|
||||
this.callbackHandler = callbackHandler;
|
||||
this.sharedState = Util.cast(sharedState);
|
||||
this.options = options;
|
||||
|
||||
// initialize any configured options
|
||||
tryFirstPass =
|
||||
"true".equalsIgnoreCase((String)options.get("tryFirstPass"));
|
||||
useFirstPass =
|
||||
"true".equalsIgnoreCase((String)options.get("useFirstPass"));
|
||||
storePass =
|
||||
"true".equalsIgnoreCase((String)options.get("storePass"));
|
||||
clearPass =
|
||||
"true".equalsIgnoreCase((String)options.get("clearPass"));
|
||||
|
||||
passwordFile = (String)options.get("passwordFile");
|
||||
passwordFileDisplayName = passwordFile;
|
||||
userSuppliedPasswordFile = true;
|
||||
|
||||
// set the location of the password file
|
||||
if (passwordFile == null) {
|
||||
passwordFile = DEFAULT_PASSWORD_FILE_NAME;
|
||||
userSuppliedPasswordFile = false;
|
||||
try {
|
||||
System.getProperty("java.home");
|
||||
hasJavaHomePermission = true;
|
||||
passwordFileDisplayName = passwordFile;
|
||||
} catch (SecurityException e) {
|
||||
hasJavaHomePermission = false;
|
||||
passwordFileDisplayName =
|
||||
ConnectorBootstrap.DefaultValues.PASSWORD_FILE_NAME;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin user authentication (Authentication Phase 1).
|
||||
*
|
||||
* <p> Acquire the user's name and password and verify them against
|
||||
* the corresponding credentials from the password file.
|
||||
*
|
||||
* @return true always, since this <code>LoginModule</code>
|
||||
* should not be ignored.
|
||||
* @exception FailedLoginException if the authentication fails.
|
||||
* @exception LoginException if this <code>LoginModule</code>
|
||||
* is unable to perform the authentication.
|
||||
*/
|
||||
public boolean login() throws LoginException {
|
||||
|
||||
try {
|
||||
loadPasswordFile();
|
||||
} catch (IOException ioe) {
|
||||
LoginException le = new LoginException(
|
||||
"Error: unable to load the password file: " +
|
||||
passwordFileDisplayName);
|
||||
throw EnvHelp.initCause(le, ioe);
|
||||
}
|
||||
|
||||
if (userCredentials == null) {
|
||||
throw new LoginException
|
||||
("Error: unable to locate the users' credentials.");
|
||||
}
|
||||
|
||||
if (logger.debugOn()) {
|
||||
logger.debug("login",
|
||||
"Using password file: " + passwordFileDisplayName);
|
||||
}
|
||||
|
||||
// attempt the authentication
|
||||
if (tryFirstPass) {
|
||||
|
||||
try {
|
||||
// attempt the authentication by getting the
|
||||
// username and password from shared state
|
||||
attemptAuthentication(true);
|
||||
|
||||
// authentication succeeded
|
||||
succeeded = true;
|
||||
if (logger.debugOn()) {
|
||||
logger.debug("login",
|
||||
"Authentication using cached password has succeeded");
|
||||
}
|
||||
return true;
|
||||
|
||||
} catch (LoginException le) {
|
||||
// authentication failed -- try again below by prompting
|
||||
cleanState();
|
||||
logger.debug("login",
|
||||
"Authentication using cached password has failed");
|
||||
}
|
||||
|
||||
} else if (useFirstPass) {
|
||||
|
||||
try {
|
||||
// attempt the authentication by getting the
|
||||
// username and password from shared state
|
||||
attemptAuthentication(true);
|
||||
|
||||
// authentication succeeded
|
||||
succeeded = true;
|
||||
if (logger.debugOn()) {
|
||||
logger.debug("login",
|
||||
"Authentication using cached password has succeeded");
|
||||
}
|
||||
return true;
|
||||
|
||||
} catch (LoginException le) {
|
||||
// authentication failed
|
||||
cleanState();
|
||||
logger.debug("login",
|
||||
"Authentication using cached password has failed");
|
||||
|
||||
throw le;
|
||||
}
|
||||
}
|
||||
|
||||
if (logger.debugOn()) {
|
||||
logger.debug("login", "Acquiring password");
|
||||
}
|
||||
|
||||
// attempt the authentication using the supplied username and password
|
||||
try {
|
||||
attemptAuthentication(false);
|
||||
|
||||
// authentication succeeded
|
||||
succeeded = true;
|
||||
if (logger.debugOn()) {
|
||||
logger.debug("login", "Authentication has succeeded");
|
||||
}
|
||||
return true;
|
||||
|
||||
} catch (LoginException le) {
|
||||
cleanState();
|
||||
logger.debug("login", "Authentication has failed");
|
||||
|
||||
throw le;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete user authentication (Authentication Phase 2).
|
||||
*
|
||||
* <p> This method is called if the LoginContext's
|
||||
* overall authentication has succeeded
|
||||
* (all the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL
|
||||
* LoginModules have succeeded).
|
||||
*
|
||||
* <p> If this LoginModule's own authentication attempt
|
||||
* succeeded (checked by retrieving the private state saved by the
|
||||
* <code>login</code> method), then this method associates a
|
||||
* <code>JMXPrincipal</code> with the <code>Subject</code> located in the
|
||||
* <code>LoginModule</code>. If this LoginModule's own
|
||||
* authentication attempted failed, then this method removes
|
||||
* any state that was originally saved.
|
||||
*
|
||||
* @exception LoginException if the commit fails
|
||||
* @return true if this LoginModule's own login and commit
|
||||
* attempts succeeded, or false otherwise.
|
||||
*/
|
||||
public boolean commit() throws LoginException {
|
||||
|
||||
if (succeeded == false) {
|
||||
return false;
|
||||
} else {
|
||||
if (subject.isReadOnly()) {
|
||||
cleanState();
|
||||
throw new LoginException("Subject is read-only");
|
||||
}
|
||||
// add Principals to the Subject
|
||||
if (!subject.getPrincipals().contains(user)) {
|
||||
subject.getPrincipals().add(user);
|
||||
}
|
||||
|
||||
if (logger.debugOn()) {
|
||||
logger.debug("commit",
|
||||
"Authentication has completed successfully");
|
||||
}
|
||||
}
|
||||
// in any case, clean out state
|
||||
cleanState();
|
||||
commitSucceeded = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abort user authentication (Authentication Phase 2).
|
||||
*
|
||||
* <p> This method is called if the LoginContext's overall authentication
|
||||
* failed (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL
|
||||
* LoginModules did not succeed).
|
||||
*
|
||||
* <p> If this LoginModule's own authentication attempt
|
||||
* succeeded (checked by retrieving the private state saved by the
|
||||
* <code>login</code> and <code>commit</code> methods),
|
||||
* then this method cleans up any state that was originally saved.
|
||||
*
|
||||
* @exception LoginException if the abort fails.
|
||||
* @return false if this LoginModule's own login and/or commit attempts
|
||||
* failed, and true otherwise.
|
||||
*/
|
||||
public boolean abort() throws LoginException {
|
||||
|
||||
if (logger.debugOn()) {
|
||||
logger.debug("abort",
|
||||
"Authentication has not completed successfully");
|
||||
}
|
||||
|
||||
if (succeeded == false) {
|
||||
return false;
|
||||
} else if (succeeded == true && commitSucceeded == false) {
|
||||
|
||||
// Clean out state
|
||||
succeeded = false;
|
||||
cleanState();
|
||||
user = null;
|
||||
} else {
|
||||
// overall authentication succeeded and commit succeeded,
|
||||
// but someone else's commit failed
|
||||
logout();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logout a user.
|
||||
*
|
||||
* <p> This method removes the Principals
|
||||
* that were added by the <code>commit</code> method.
|
||||
*
|
||||
* @exception LoginException if the logout fails.
|
||||
* @return true in all cases since this <code>LoginModule</code>
|
||||
* should not be ignored.
|
||||
*/
|
||||
public boolean logout() throws LoginException {
|
||||
if (subject.isReadOnly()) {
|
||||
cleanState();
|
||||
throw new LoginException ("Subject is read-only");
|
||||
}
|
||||
subject.getPrincipals().remove(user);
|
||||
|
||||
// clean out state
|
||||
cleanState();
|
||||
succeeded = false;
|
||||
commitSucceeded = false;
|
||||
user = null;
|
||||
|
||||
if (logger.debugOn()) {
|
||||
logger.debug("logout", "Subject is being logged out");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt authentication
|
||||
*
|
||||
* @param usePasswdFromSharedState a flag to tell this method whether
|
||||
* to retrieve the password from the sharedState.
|
||||
*/
|
||||
@SuppressWarnings("unchecked") // sharedState used as Map<String,Object>
|
||||
private void attemptAuthentication(boolean usePasswdFromSharedState)
|
||||
throws LoginException {
|
||||
|
||||
// get the username and password
|
||||
getUsernamePassword(usePasswdFromSharedState);
|
||||
|
||||
String localPassword;
|
||||
|
||||
// userCredentials is initialized in login()
|
||||
if (((localPassword = userCredentials.getProperty(username)) == null) ||
|
||||
(! localPassword.equals(new String(password)))) {
|
||||
|
||||
// username not found or passwords do not match
|
||||
if (logger.debugOn()) {
|
||||
logger.debug("login", "Invalid username or password");
|
||||
}
|
||||
throw new FailedLoginException("Invalid username or password");
|
||||
}
|
||||
|
||||
// Save the username and password in the shared state
|
||||
// only if authentication succeeded
|
||||
if (storePass &&
|
||||
!sharedState.containsKey(USERNAME_KEY) &&
|
||||
!sharedState.containsKey(PASSWORD_KEY)) {
|
||||
sharedState.put(USERNAME_KEY, username);
|
||||
sharedState.put(PASSWORD_KEY, password);
|
||||
}
|
||||
|
||||
// Create a new user principal
|
||||
user = new JMXPrincipal(username);
|
||||
|
||||
if (logger.debugOn()) {
|
||||
logger.debug("login",
|
||||
"User '" + username + "' successfully validated");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the password file.
|
||||
*/
|
||||
private void loadPasswordFile() throws IOException {
|
||||
FileInputStream fis;
|
||||
try {
|
||||
fis = new FileInputStream(passwordFile);
|
||||
} catch (SecurityException e) {
|
||||
if (userSuppliedPasswordFile || hasJavaHomePermission) {
|
||||
throw e;
|
||||
} else {
|
||||
final FilePermission fp =
|
||||
new FilePermission(passwordFileDisplayName, "read");
|
||||
AccessControlException ace = new AccessControlException(
|
||||
"access denied " + fp.toString());
|
||||
ace.setStackTrace(e.getStackTrace());
|
||||
throw ace;
|
||||
}
|
||||
}
|
||||
try {
|
||||
final BufferedInputStream bis = new BufferedInputStream(fis);
|
||||
try {
|
||||
userCredentials = new Properties();
|
||||
userCredentials.load(bis);
|
||||
} finally {
|
||||
bis.close();
|
||||
}
|
||||
} finally {
|
||||
fis.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the username and password.
|
||||
* This method does not return any value.
|
||||
* Instead, it sets global name and password variables.
|
||||
*
|
||||
* <p> Also note that this method will set the username and password
|
||||
* values in the shared state in case subsequent LoginModules
|
||||
* want to use them via use/tryFirstPass.
|
||||
*
|
||||
* @param usePasswdFromSharedState boolean that tells this method whether
|
||||
* to retrieve the password from the sharedState.
|
||||
*/
|
||||
private void getUsernamePassword(boolean usePasswdFromSharedState)
|
||||
throws LoginException {
|
||||
|
||||
if (usePasswdFromSharedState) {
|
||||
// use the password saved by the first module in the stack
|
||||
username = (String)sharedState.get(USERNAME_KEY);
|
||||
password = (char[])sharedState.get(PASSWORD_KEY);
|
||||
return;
|
||||
}
|
||||
|
||||
// acquire username and password
|
||||
if (callbackHandler == null)
|
||||
throw new LoginException("Error: no CallbackHandler available " +
|
||||
"to garner authentication information from the user");
|
||||
|
||||
Callback[] callbacks = new Callback[2];
|
||||
callbacks[0] = new NameCallback("username");
|
||||
callbacks[1] = new PasswordCallback("password", false);
|
||||
|
||||
try {
|
||||
callbackHandler.handle(callbacks);
|
||||
username = ((NameCallback)callbacks[0]).getName();
|
||||
char[] tmpPassword = ((PasswordCallback)callbacks[1]).getPassword();
|
||||
password = new char[tmpPassword.length];
|
||||
System.arraycopy(tmpPassword, 0,
|
||||
password, 0, tmpPassword.length);
|
||||
((PasswordCallback)callbacks[1]).clearPassword();
|
||||
|
||||
} catch (IOException ioe) {
|
||||
LoginException le = new LoginException(ioe.toString());
|
||||
throw EnvHelp.initCause(le, ioe);
|
||||
} catch (UnsupportedCallbackException uce) {
|
||||
LoginException le = new LoginException(
|
||||
"Error: " + uce.getCallback().toString() +
|
||||
" not available to garner authentication " +
|
||||
"information from the user");
|
||||
throw EnvHelp.initCause(le, uce);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean out state because of a failed authentication attempt
|
||||
*/
|
||||
private void cleanState() {
|
||||
username = null;
|
||||
if (password != null) {
|
||||
Arrays.fill(password, ' ');
|
||||
password = null;
|
||||
}
|
||||
|
||||
if (clearPass) {
|
||||
sharedState.remove(USERNAME_KEY);
|
||||
sharedState.remove(PASSWORD_KEY);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 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.security;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.AccessController;
|
||||
import java.security.Principal;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import javax.management.remote.JMXPrincipal;
|
||||
import javax.management.remote.JMXAuthenticator;
|
||||
import javax.security.auth.AuthPermission;
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.callback.*;
|
||||
import javax.security.auth.login.AppConfigurationEntry;
|
||||
import javax.security.auth.login.Configuration;
|
||||
import javax.security.auth.login.LoginContext;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import javax.security.auth.spi.LoginModule;
|
||||
import com.sun.jmx.remote.util.ClassLogger;
|
||||
import com.sun.jmx.remote.util.EnvHelp;
|
||||
|
||||
/**
|
||||
* <p>This class represents a
|
||||
* <a href="{@docRoot}/../guide/security/jaas/JAASRefGuide.html">JAAS</a>
|
||||
* based implementation of the {@link JMXAuthenticator} interface.</p>
|
||||
*
|
||||
* <p>Authentication is performed by passing the supplied user's credentials
|
||||
* to one or more authentication mechanisms ({@link LoginModule}) for
|
||||
* verification. An authentication mechanism acquires the user's credentials
|
||||
* by calling {@link NameCallback} and/or {@link PasswordCallback}.
|
||||
* If authentication is successful then an authenticated {@link Subject}
|
||||
* filled in with a {@link Principal} is returned. Authorization checks
|
||||
* will then be performed based on this <code>Subject</code>.</p>
|
||||
*
|
||||
* <p>By default, a single file-based authentication mechanism
|
||||
* {@link FileLoginModule} is configured (<code>FileLoginConfig</code>).</p>
|
||||
*
|
||||
* <p>To override the default configuration use the
|
||||
* <code>com.sun.management.jmxremote.login.config</code> management property
|
||||
* described in the JRE/lib/management/management.properties file.
|
||||
* Set this property to the name of a JAAS configuration entry and ensure that
|
||||
* the entry is loaded by the installed {@link Configuration}. In addition,
|
||||
* ensure that the authentication mechanisms specified in the entry acquire
|
||||
* the user's credentials by calling {@link NameCallback} and
|
||||
* {@link PasswordCallback} and that they return a {@link Subject} filled-in
|
||||
* with a {@link Principal}, for those users that are successfully
|
||||
* authenticated.</p>
|
||||
*/
|
||||
public final class JMXPluggableAuthenticator implements JMXAuthenticator {
|
||||
|
||||
/**
|
||||
* Creates an instance of <code>JMXPluggableAuthenticator</code>
|
||||
* and initializes it with a {@link LoginContext}.
|
||||
*
|
||||
* @param env the environment containing configuration properties for the
|
||||
* authenticator. Can be null, which is equivalent to an empty
|
||||
* Map.
|
||||
* @exception SecurityException if the authentication mechanism cannot be
|
||||
* initialized.
|
||||
*/
|
||||
public JMXPluggableAuthenticator(Map<?, ?> env) {
|
||||
|
||||
String loginConfigName = null;
|
||||
String passwordFile = null;
|
||||
|
||||
if (env != null) {
|
||||
loginConfigName = (String) env.get(LOGIN_CONFIG_PROP);
|
||||
passwordFile = (String) env.get(PASSWORD_FILE_PROP);
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
if (loginConfigName != null) {
|
||||
// use the supplied JAAS login configuration
|
||||
loginContext =
|
||||
new LoginContext(loginConfigName, new JMXCallbackHandler());
|
||||
|
||||
} else {
|
||||
// use the default JAAS login configuration (file-based)
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
sm.checkPermission(
|
||||
new AuthPermission("createLoginContext." +
|
||||
LOGIN_CONFIG_NAME));
|
||||
}
|
||||
|
||||
final String pf = passwordFile;
|
||||
try {
|
||||
loginContext = AccessController.doPrivileged(
|
||||
new PrivilegedExceptionAction<LoginContext>() {
|
||||
public LoginContext run() throws LoginException {
|
||||
return new LoginContext(
|
||||
LOGIN_CONFIG_NAME,
|
||||
null,
|
||||
new JMXCallbackHandler(),
|
||||
new FileLoginConfig(pf));
|
||||
}
|
||||
});
|
||||
} catch (PrivilegedActionException pae) {
|
||||
throw (LoginException) pae.getException();
|
||||
}
|
||||
}
|
||||
|
||||
} catch (LoginException le) {
|
||||
authenticationFailure("authenticate", le);
|
||||
|
||||
} catch (SecurityException se) {
|
||||
authenticationFailure("authenticate", se);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate the <code>MBeanServerConnection</code> client
|
||||
* with the given client credentials.
|
||||
*
|
||||
* @param credentials the user-defined credentials to be passed in
|
||||
* to the server in order to authenticate the user before creating
|
||||
* the <code>MBeanServerConnection</code>. This parameter must
|
||||
* be a two-element <code>String[]</code> containing the client's
|
||||
* username and password in that order.
|
||||
*
|
||||
* @return the authenticated subject containing a
|
||||
* <code>JMXPrincipal(username)</code>.
|
||||
*
|
||||
* @exception SecurityException if the server cannot authenticate the user
|
||||
* with the provided credentials.
|
||||
*/
|
||||
public Subject authenticate(Object credentials) {
|
||||
// Verify that credentials is of type String[].
|
||||
//
|
||||
if (!(credentials instanceof String[])) {
|
||||
// Special case for null so we get a more informative message
|
||||
if (credentials == null)
|
||||
authenticationFailure("authenticate", "Credentials required");
|
||||
|
||||
final String message =
|
||||
"Credentials should be String[] instead of " +
|
||||
credentials.getClass().getName();
|
||||
authenticationFailure("authenticate", message);
|
||||
}
|
||||
// Verify that the array contains two elements.
|
||||
//
|
||||
final String[] aCredentials = (String[]) credentials;
|
||||
if (aCredentials.length != 2) {
|
||||
final String message =
|
||||
"Credentials should have 2 elements not " +
|
||||
aCredentials.length;
|
||||
authenticationFailure("authenticate", message);
|
||||
}
|
||||
// Verify that username exists and the associated
|
||||
// password matches the one supplied by the client.
|
||||
//
|
||||
username = aCredentials[0];
|
||||
password = aCredentials[1];
|
||||
if (username == null || password == null) {
|
||||
final String message = "Username or password is null";
|
||||
authenticationFailure("authenticate", message);
|
||||
}
|
||||
|
||||
// Perform authentication
|
||||
try {
|
||||
loginContext.login();
|
||||
final Subject subject = loginContext.getSubject();
|
||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||
public Void run() {
|
||||
subject.setReadOnly();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
return subject;
|
||||
|
||||
} catch (LoginException le) {
|
||||
authenticationFailure("authenticate", le);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void authenticationFailure(String method, String message)
|
||||
throws SecurityException {
|
||||
final String msg = "Authentication failed! " + message;
|
||||
final SecurityException e = new SecurityException(msg);
|
||||
logException(method, msg, e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
private static void authenticationFailure(String method,
|
||||
Exception exception)
|
||||
throws SecurityException {
|
||||
String msg;
|
||||
SecurityException se;
|
||||
if (exception instanceof SecurityException) {
|
||||
msg = exception.getMessage();
|
||||
se = (SecurityException) exception;
|
||||
} else {
|
||||
msg = "Authentication failed! " + exception.getMessage();
|
||||
final SecurityException e = new SecurityException(msg);
|
||||
EnvHelp.initCause(e, exception);
|
||||
se = e;
|
||||
}
|
||||
logException(method, msg, se);
|
||||
throw se;
|
||||
}
|
||||
|
||||
private static void logException(String method,
|
||||
String message,
|
||||
Exception e) {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace(method, message);
|
||||
}
|
||||
if (logger.debugOn()) {
|
||||
logger.debug(method, e);
|
||||
}
|
||||
}
|
||||
|
||||
private LoginContext loginContext;
|
||||
private String username;
|
||||
private String password;
|
||||
private static final String LOGIN_CONFIG_PROP =
|
||||
"jmx.remote.x.login.config";
|
||||
private static final String LOGIN_CONFIG_NAME = "JMXPluggableAuthenticator";
|
||||
private static final String PASSWORD_FILE_PROP =
|
||||
"jmx.remote.x.password.file";
|
||||
private static final ClassLogger logger =
|
||||
new ClassLogger("javax.management.remote.misc", LOGIN_CONFIG_NAME);
|
||||
|
||||
/**
|
||||
* This callback handler supplies the username and password (which was
|
||||
* originally supplied by the JMX user) to the JAAS login module performing
|
||||
* the authentication. No interactive user prompting is required because the
|
||||
* credentials are already available to this class (via its enclosing class).
|
||||
*/
|
||||
private final class JMXCallbackHandler implements CallbackHandler {
|
||||
|
||||
/**
|
||||
* Sets the username and password in the appropriate Callback object.
|
||||
*/
|
||||
public void handle(Callback[] callbacks)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
|
||||
for (int i = 0; i < callbacks.length; i++) {
|
||||
if (callbacks[i] instanceof NameCallback) {
|
||||
((NameCallback)callbacks[i]).setName(username);
|
||||
|
||||
} else if (callbacks[i] instanceof PasswordCallback) {
|
||||
((PasswordCallback)callbacks[i])
|
||||
.setPassword(password.toCharArray());
|
||||
|
||||
} else {
|
||||
throw new UnsupportedCallbackException
|
||||
(callbacks[i], "Unrecognized Callback");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class defines the JAAS configuration for file-based authentication.
|
||||
* It is equivalent to the following textual configuration entry:
|
||||
* <pre>
|
||||
* JMXPluggableAuthenticator {
|
||||
* com.sun.jmx.remote.security.FileLoginModule required;
|
||||
* };
|
||||
* </pre>
|
||||
*/
|
||||
private static class FileLoginConfig extends Configuration {
|
||||
|
||||
// The JAAS configuration for file-based authentication
|
||||
private AppConfigurationEntry[] entries;
|
||||
|
||||
// The classname of the login module for file-based authentication
|
||||
private static final String FILE_LOGIN_MODULE =
|
||||
FileLoginModule.class.getName();
|
||||
|
||||
// The option that identifies the password file to use
|
||||
private static final String PASSWORD_FILE_OPTION = "passwordFile";
|
||||
|
||||
/**
|
||||
* Creates an instance of <code>FileLoginConfig</code>
|
||||
*
|
||||
* @param passwordFile A filepath that identifies the password file to use.
|
||||
* If null then the default password file is used.
|
||||
*/
|
||||
public FileLoginConfig(String passwordFile) {
|
||||
|
||||
Map<String, String> options;
|
||||
if (passwordFile != null) {
|
||||
options = new HashMap<String, String>(1);
|
||||
options.put(PASSWORD_FILE_OPTION, passwordFile);
|
||||
} else {
|
||||
options = Collections.emptyMap();
|
||||
}
|
||||
|
||||
entries = new AppConfigurationEntry[] {
|
||||
new AppConfigurationEntry(FILE_LOGIN_MODULE,
|
||||
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
|
||||
options)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the JAAS configuration for file-based authentication
|
||||
*/
|
||||
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
|
||||
|
||||
return name.equals(LOGIN_CONFIG_NAME) ? entries : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the configuration.
|
||||
*/
|
||||
public void refresh() {
|
||||
// the configuration is fixed
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2005, 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.security;
|
||||
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.CodeSource;
|
||||
import java.security.Permissions;
|
||||
import java.security.ProtectionDomain;
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.SubjectDomainCombiner;
|
||||
|
||||
/**
|
||||
* <p>This class represents an extension to the {@link SubjectDomainCombiner}
|
||||
* and is used to add a new {@link ProtectionDomain}, comprised of a null
|
||||
* codesource/signers and an empty permission set, to the access control
|
||||
* context with which this combiner is combined.</p>
|
||||
*
|
||||
* <p>When the {@link #combine} method is called the {@link ProtectionDomain}
|
||||
* is augmented with the permissions granted to the set of principals present
|
||||
* in the supplied {@link Subject}.</p>
|
||||
*/
|
||||
public class JMXSubjectDomainCombiner extends SubjectDomainCombiner {
|
||||
|
||||
public JMXSubjectDomainCombiner(Subject s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
public ProtectionDomain[] combine(ProtectionDomain[] current,
|
||||
ProtectionDomain[] assigned) {
|
||||
// Add a new ProtectionDomain with the null codesource/signers, and
|
||||
// the empty permission set, to the end of the array containing the
|
||||
// 'current' protections domains, i.e. the ones that will be augmented
|
||||
// with the permissions granted to the set of principals present in
|
||||
// the supplied subject.
|
||||
//
|
||||
ProtectionDomain[] newCurrent;
|
||||
if (current == null || current.length == 0) {
|
||||
newCurrent = new ProtectionDomain[1];
|
||||
newCurrent[0] = pdNoPerms;
|
||||
} else {
|
||||
newCurrent = new ProtectionDomain[current.length + 1];
|
||||
for (int i = 0; i < current.length; i++) {
|
||||
newCurrent[i] = current[i];
|
||||
}
|
||||
newCurrent[current.length] = pdNoPerms;
|
||||
}
|
||||
return super.combine(newCurrent, assigned);
|
||||
}
|
||||
|
||||
/**
|
||||
* A null CodeSource.
|
||||
*/
|
||||
private static final CodeSource nullCodeSource =
|
||||
new CodeSource(null, (java.security.cert.Certificate[]) null);
|
||||
|
||||
/**
|
||||
* A ProtectionDomain with a null CodeSource and an empty permission set.
|
||||
*/
|
||||
private static final ProtectionDomain pdNoPerms =
|
||||
new ProtectionDomain(nullCodeSource, new Permissions(), null, null);
|
||||
|
||||
/**
|
||||
* Get the current AccessControlContext combined with the supplied subject.
|
||||
*/
|
||||
public static AccessControlContext getContext(Subject subject) {
|
||||
return new AccessControlContext(AccessController.getContext(),
|
||||
new JMXSubjectDomainCombiner(subject));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the AccessControlContext of the domain combiner created with
|
||||
* the supplied subject, i.e. an AccessControlContext with the domain
|
||||
* combiner created with the supplied subject and where the caller's
|
||||
* context has been removed.
|
||||
*/
|
||||
public static AccessControlContext
|
||||
getDomainCombinerContext(Subject subject) {
|
||||
return new AccessControlContext(
|
||||
new AccessControlContext(new ProtectionDomain[0]),
|
||||
new JMXSubjectDomainCombiner(subject));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,665 @@
|
||||
/*
|
||||
* 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.security;
|
||||
|
||||
import com.sun.jmx.mbeanserver.GetPropertyAction;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.security.AccessController;
|
||||
import java.util.Set;
|
||||
import javax.management.Attribute;
|
||||
import javax.management.AttributeList;
|
||||
import javax.management.AttributeNotFoundException;
|
||||
import javax.management.InstanceNotFoundException;
|
||||
import javax.management.InstanceAlreadyExistsException;
|
||||
import javax.management.IntrospectionException;
|
||||
import javax.management.InvalidAttributeValueException;
|
||||
import javax.management.ListenerNotFoundException;
|
||||
import javax.management.MBeanException;
|
||||
import javax.management.MBeanInfo;
|
||||
import javax.management.MBeanRegistrationException;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.NotCompliantMBeanException;
|
||||
import javax.management.NotificationFilter;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectInstance;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.OperationsException;
|
||||
import javax.management.QueryExp;
|
||||
import javax.management.ReflectionException;
|
||||
import javax.management.loading.ClassLoaderRepository;
|
||||
import javax.management.remote.MBeanServerForwarder;
|
||||
|
||||
/**
|
||||
* <p>An object of this class implements the MBeanServer interface
|
||||
* and, for each of its methods, calls an appropriate checking method
|
||||
* and then forwards the request to a wrapped MBeanServer object. The
|
||||
* checking method may throw a RuntimeException if the operation is
|
||||
* not allowed; in this case the request is not forwarded to the
|
||||
* wrapped object.</p>
|
||||
*
|
||||
* <p>A typical use of this class is to insert it between a connector server
|
||||
* such as the RMI connector and the MBeanServer with which the connector
|
||||
* is associated. Requests from the connector client can then be filtered
|
||||
* and those operations that are not allowed, or not allowed in a particular
|
||||
* context, can be rejected by throwing a <code>SecurityException</code>
|
||||
* in the corresponding <code>check*</code> method.</p>
|
||||
*
|
||||
* <p>This is an abstract class, because in its implementation none of
|
||||
* the checking methods does anything. To be useful, it must be
|
||||
* subclassed and at least one of the checking methods overridden to
|
||||
* do some checking. Some or all of the MBeanServer methods may also
|
||||
* be overridden, for instance if the default checking behavior is
|
||||
* inappropriate.</p>
|
||||
*
|
||||
* <p>If there is no SecurityManager, then the access controller will refuse
|
||||
* to create an MBean that is a ClassLoader, which includes MLets, or to
|
||||
* execute the method addURL on an MBean that is an MLet. This prevents
|
||||
* people from opening security holes unintentionally. Otherwise, it
|
||||
* would not be obvious that granting write access grants the ability to
|
||||
* download and execute arbitrary code in the target MBean server. Advanced
|
||||
* users who do want the ability to use MLets are presumably advanced enough
|
||||
* to handle policy files and security managers.</p>
|
||||
*/
|
||||
public abstract class MBeanServerAccessController
|
||||
implements MBeanServerForwarder {
|
||||
|
||||
public MBeanServer getMBeanServer() {
|
||||
return mbs;
|
||||
}
|
||||
|
||||
public void setMBeanServer(MBeanServer mbs) {
|
||||
if (mbs == null)
|
||||
throw new IllegalArgumentException("Null MBeanServer");
|
||||
if (this.mbs != null)
|
||||
throw new IllegalArgumentException("MBeanServer object already " +
|
||||
"initialized");
|
||||
this.mbs = mbs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the caller can do read operations. This method does
|
||||
* nothing if so, otherwise throws SecurityException.
|
||||
*/
|
||||
protected abstract void checkRead();
|
||||
|
||||
/**
|
||||
* Check if the caller can do write operations. This method does
|
||||
* nothing if so, otherwise throws SecurityException.
|
||||
*/
|
||||
protected abstract void checkWrite();
|
||||
|
||||
/**
|
||||
* Check if the caller can create the named class. The default
|
||||
* implementation of this method calls {@link #checkWrite()}.
|
||||
*/
|
||||
protected void checkCreate(String className) {
|
||||
checkWrite();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the caller can unregister the named MBean. The default
|
||||
* implementation of this method calls {@link #checkWrite()}.
|
||||
*/
|
||||
protected void checkUnregister(ObjectName name) {
|
||||
checkWrite();
|
||||
}
|
||||
|
||||
//--------------------------------------------
|
||||
//--------------------------------------------
|
||||
//
|
||||
// Implementation of the MBeanServer interface
|
||||
//
|
||||
//--------------------------------------------
|
||||
//--------------------------------------------
|
||||
|
||||
/**
|
||||
* Call <code>checkRead()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public void addNotificationListener(ObjectName name,
|
||||
NotificationListener listener,
|
||||
NotificationFilter filter,
|
||||
Object handback)
|
||||
throws InstanceNotFoundException {
|
||||
checkRead();
|
||||
getMBeanServer().addNotificationListener(name, listener,
|
||||
filter, handback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkRead()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public void addNotificationListener(ObjectName name,
|
||||
ObjectName listener,
|
||||
NotificationFilter filter,
|
||||
Object handback)
|
||||
throws InstanceNotFoundException {
|
||||
checkRead();
|
||||
getMBeanServer().addNotificationListener(name, listener,
|
||||
filter, handback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkCreate(className)</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public ObjectInstance createMBean(String className, ObjectName name)
|
||||
throws
|
||||
ReflectionException,
|
||||
InstanceAlreadyExistsException,
|
||||
MBeanRegistrationException,
|
||||
MBeanException,
|
||||
NotCompliantMBeanException {
|
||||
checkCreate(className);
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm == null) {
|
||||
Object object = getMBeanServer().instantiate(className);
|
||||
checkClassLoader(object);
|
||||
return getMBeanServer().registerMBean(object, name);
|
||||
} else {
|
||||
return getMBeanServer().createMBean(className, name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkCreate(className)</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public ObjectInstance createMBean(String className, ObjectName name,
|
||||
Object params[], String signature[])
|
||||
throws
|
||||
ReflectionException,
|
||||
InstanceAlreadyExistsException,
|
||||
MBeanRegistrationException,
|
||||
MBeanException,
|
||||
NotCompliantMBeanException {
|
||||
checkCreate(className);
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm == null) {
|
||||
Object object = getMBeanServer().instantiate(className,
|
||||
params,
|
||||
signature);
|
||||
checkClassLoader(object);
|
||||
return getMBeanServer().registerMBean(object, name);
|
||||
} else {
|
||||
return getMBeanServer().createMBean(className, name,
|
||||
params, signature);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkCreate(className)</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public ObjectInstance createMBean(String className,
|
||||
ObjectName name,
|
||||
ObjectName loaderName)
|
||||
throws
|
||||
ReflectionException,
|
||||
InstanceAlreadyExistsException,
|
||||
MBeanRegistrationException,
|
||||
MBeanException,
|
||||
NotCompliantMBeanException,
|
||||
InstanceNotFoundException {
|
||||
checkCreate(className);
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm == null) {
|
||||
Object object = getMBeanServer().instantiate(className,
|
||||
loaderName);
|
||||
checkClassLoader(object);
|
||||
return getMBeanServer().registerMBean(object, name);
|
||||
} else {
|
||||
return getMBeanServer().createMBean(className, name, loaderName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkCreate(className)</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public ObjectInstance createMBean(String className,
|
||||
ObjectName name,
|
||||
ObjectName loaderName,
|
||||
Object params[],
|
||||
String signature[])
|
||||
throws
|
||||
ReflectionException,
|
||||
InstanceAlreadyExistsException,
|
||||
MBeanRegistrationException,
|
||||
MBeanException,
|
||||
NotCompliantMBeanException,
|
||||
InstanceNotFoundException {
|
||||
checkCreate(className);
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm == null) {
|
||||
Object object = getMBeanServer().instantiate(className,
|
||||
loaderName,
|
||||
params,
|
||||
signature);
|
||||
checkClassLoader(object);
|
||||
return getMBeanServer().registerMBean(object, name);
|
||||
} else {
|
||||
return getMBeanServer().createMBean(className, name, loaderName,
|
||||
params, signature);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkRead()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
@Deprecated
|
||||
public ObjectInputStream deserialize(ObjectName name, byte[] data)
|
||||
throws InstanceNotFoundException, OperationsException {
|
||||
checkRead();
|
||||
return getMBeanServer().deserialize(name, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkRead()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
@Deprecated
|
||||
public ObjectInputStream deserialize(String className, byte[] data)
|
||||
throws OperationsException, ReflectionException {
|
||||
checkRead();
|
||||
return getMBeanServer().deserialize(className, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkRead()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
@Deprecated
|
||||
public ObjectInputStream deserialize(String className,
|
||||
ObjectName loaderName,
|
||||
byte[] data)
|
||||
throws
|
||||
InstanceNotFoundException,
|
||||
OperationsException,
|
||||
ReflectionException {
|
||||
checkRead();
|
||||
return getMBeanServer().deserialize(className, loaderName, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkRead()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public Object getAttribute(ObjectName name, String attribute)
|
||||
throws
|
||||
MBeanException,
|
||||
AttributeNotFoundException,
|
||||
InstanceNotFoundException,
|
||||
ReflectionException {
|
||||
checkRead();
|
||||
return getMBeanServer().getAttribute(name, attribute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkRead()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public AttributeList getAttributes(ObjectName name, String[] attributes)
|
||||
throws InstanceNotFoundException, ReflectionException {
|
||||
checkRead();
|
||||
return getMBeanServer().getAttributes(name, attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkRead()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public ClassLoader getClassLoader(ObjectName loaderName)
|
||||
throws InstanceNotFoundException {
|
||||
checkRead();
|
||||
return getMBeanServer().getClassLoader(loaderName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkRead()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public ClassLoader getClassLoaderFor(ObjectName mbeanName)
|
||||
throws InstanceNotFoundException {
|
||||
checkRead();
|
||||
return getMBeanServer().getClassLoaderFor(mbeanName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkRead()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public ClassLoaderRepository getClassLoaderRepository() {
|
||||
checkRead();
|
||||
return getMBeanServer().getClassLoaderRepository();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkRead()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public String getDefaultDomain() {
|
||||
checkRead();
|
||||
return getMBeanServer().getDefaultDomain();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkRead()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public String[] getDomains() {
|
||||
checkRead();
|
||||
return getMBeanServer().getDomains();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkRead()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public Integer getMBeanCount() {
|
||||
checkRead();
|
||||
return getMBeanServer().getMBeanCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkRead()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public MBeanInfo getMBeanInfo(ObjectName name)
|
||||
throws
|
||||
InstanceNotFoundException,
|
||||
IntrospectionException,
|
||||
ReflectionException {
|
||||
checkRead();
|
||||
return getMBeanServer().getMBeanInfo(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkRead()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public ObjectInstance getObjectInstance(ObjectName name)
|
||||
throws InstanceNotFoundException {
|
||||
checkRead();
|
||||
return getMBeanServer().getObjectInstance(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkCreate(className)</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public Object instantiate(String className)
|
||||
throws ReflectionException, MBeanException {
|
||||
checkCreate(className);
|
||||
return getMBeanServer().instantiate(className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkCreate(className)</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public Object instantiate(String className,
|
||||
Object params[],
|
||||
String signature[])
|
||||
throws ReflectionException, MBeanException {
|
||||
checkCreate(className);
|
||||
return getMBeanServer().instantiate(className, params, signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkCreate(className)</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public Object instantiate(String className, ObjectName loaderName)
|
||||
throws ReflectionException, MBeanException, InstanceNotFoundException {
|
||||
checkCreate(className);
|
||||
return getMBeanServer().instantiate(className, loaderName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkCreate(className)</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public Object instantiate(String className, ObjectName loaderName,
|
||||
Object params[], String signature[])
|
||||
throws ReflectionException, MBeanException, InstanceNotFoundException {
|
||||
checkCreate(className);
|
||||
return getMBeanServer().instantiate(className, loaderName,
|
||||
params, signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkWrite()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public Object invoke(ObjectName name, String operationName,
|
||||
Object params[], String signature[])
|
||||
throws
|
||||
InstanceNotFoundException,
|
||||
MBeanException,
|
||||
ReflectionException {
|
||||
checkWrite();
|
||||
checkMLetMethods(name, operationName);
|
||||
return getMBeanServer().invoke(name, operationName, params, signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkRead()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public boolean isInstanceOf(ObjectName name, String className)
|
||||
throws InstanceNotFoundException {
|
||||
checkRead();
|
||||
return getMBeanServer().isInstanceOf(name, className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkRead()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public boolean isRegistered(ObjectName name) {
|
||||
checkRead();
|
||||
return getMBeanServer().isRegistered(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkRead()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
|
||||
checkRead();
|
||||
return getMBeanServer().queryMBeans(name, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkRead()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
|
||||
checkRead();
|
||||
return getMBeanServer().queryNames(name, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkWrite()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public ObjectInstance registerMBean(Object object, ObjectName name)
|
||||
throws
|
||||
InstanceAlreadyExistsException,
|
||||
MBeanRegistrationException,
|
||||
NotCompliantMBeanException {
|
||||
checkWrite();
|
||||
return getMBeanServer().registerMBean(object, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkRead()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public void removeNotificationListener(ObjectName name,
|
||||
NotificationListener listener)
|
||||
throws InstanceNotFoundException, ListenerNotFoundException {
|
||||
checkRead();
|
||||
getMBeanServer().removeNotificationListener(name, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkRead()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public void removeNotificationListener(ObjectName name,
|
||||
NotificationListener listener,
|
||||
NotificationFilter filter,
|
||||
Object handback)
|
||||
throws InstanceNotFoundException, ListenerNotFoundException {
|
||||
checkRead();
|
||||
getMBeanServer().removeNotificationListener(name, listener,
|
||||
filter, handback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkRead()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public void removeNotificationListener(ObjectName name,
|
||||
ObjectName listener)
|
||||
throws InstanceNotFoundException, ListenerNotFoundException {
|
||||
checkRead();
|
||||
getMBeanServer().removeNotificationListener(name, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkRead()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public void removeNotificationListener(ObjectName name,
|
||||
ObjectName listener,
|
||||
NotificationFilter filter,
|
||||
Object handback)
|
||||
throws InstanceNotFoundException, ListenerNotFoundException {
|
||||
checkRead();
|
||||
getMBeanServer().removeNotificationListener(name, listener,
|
||||
filter, handback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkWrite()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public void setAttribute(ObjectName name, Attribute attribute)
|
||||
throws
|
||||
InstanceNotFoundException,
|
||||
AttributeNotFoundException,
|
||||
InvalidAttributeValueException,
|
||||
MBeanException,
|
||||
ReflectionException {
|
||||
checkWrite();
|
||||
getMBeanServer().setAttribute(name, attribute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkWrite()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public AttributeList setAttributes(ObjectName name,
|
||||
AttributeList attributes)
|
||||
throws InstanceNotFoundException, ReflectionException {
|
||||
checkWrite();
|
||||
return getMBeanServer().setAttributes(name, attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call <code>checkUnregister()</code>, then forward this method to the
|
||||
* wrapped object.
|
||||
*/
|
||||
public void unregisterMBean(ObjectName name)
|
||||
throws InstanceNotFoundException, MBeanRegistrationException {
|
||||
checkUnregister(name);
|
||||
getMBeanServer().unregisterMBean(name);
|
||||
}
|
||||
|
||||
//----------------
|
||||
// PRIVATE METHODS
|
||||
//----------------
|
||||
|
||||
private void checkClassLoader(Object object) {
|
||||
if (object instanceof ClassLoader)
|
||||
throw new SecurityException("Access denied! Creating an " +
|
||||
"MBean that is a ClassLoader " +
|
||||
"is forbidden unless a security " +
|
||||
"manager is installed.");
|
||||
}
|
||||
|
||||
private void checkMLetMethods(ObjectName name, String operation)
|
||||
throws InstanceNotFoundException {
|
||||
// Check if security manager installed
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
return;
|
||||
}
|
||||
// Check for addURL and getMBeansFromURL methods
|
||||
if (!operation.equals("addURL") &&
|
||||
!operation.equals("getMBeansFromURL")) {
|
||||
return;
|
||||
}
|
||||
// Check if MBean is instance of MLet
|
||||
if (!getMBeanServer().isInstanceOf(name,
|
||||
"javax.management.loading.MLet")) {
|
||||
return;
|
||||
}
|
||||
// Throw security exception
|
||||
if (operation.equals("addURL")) { // addURL
|
||||
throw new SecurityException("Access denied! MLet method addURL " +
|
||||
"cannot be invoked unless a security manager is installed.");
|
||||
} else { // getMBeansFromURL
|
||||
// Whether or not calling getMBeansFromURL is allowed is controlled
|
||||
// by the value of the "jmx.remote.x.mlet.allow.getMBeansFromURL"
|
||||
// system property. If the value of this property is true, calling
|
||||
// the MLet's getMBeansFromURL method is allowed. The default value
|
||||
// for this property is false.
|
||||
final String propName = "jmx.remote.x.mlet.allow.getMBeansFromURL";
|
||||
GetPropertyAction propAction = new GetPropertyAction(propName);
|
||||
String propValue = AccessController.doPrivileged(propAction);
|
||||
boolean allowGetMBeansFromURL = "true".equalsIgnoreCase(propValue);
|
||||
if (!allowGetMBeansFromURL) {
|
||||
throw new SecurityException("Access denied! MLet method " +
|
||||
"getMBeansFromURL cannot be invoked unless a " +
|
||||
"security manager is installed or the system property " +
|
||||
"-Djmx.remote.x.mlet.allow.getMBeansFromURL=true " +
|
||||
"is specified.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------
|
||||
// PRIVATE VARIABLES
|
||||
//------------------
|
||||
|
||||
private MBeanServer mbs;
|
||||
}
|
||||
@@ -0,0 +1,544 @@
|
||||
/*
|
||||
* 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.security;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.Principal;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.ObjectName;
|
||||
import javax.security.auth.Subject;
|
||||
|
||||
/**
|
||||
* <p>An object of this class implements the MBeanServerAccessController
|
||||
* interface and, for each of its methods, calls an appropriate checking
|
||||
* method and then forwards the request to a wrapped MBeanServer object.
|
||||
* The checking method may throw a SecurityException if the operation is
|
||||
* not allowed; in this case the request is not forwarded to the
|
||||
* wrapped object.</p>
|
||||
*
|
||||
* <p>This class implements the {@link #checkRead()}, {@link #checkWrite()},
|
||||
* {@link #checkCreate(String)}, and {@link #checkUnregister(ObjectName)}
|
||||
* methods based on an access level properties file containing username/access
|
||||
* level pairs. The set of username/access level pairs is passed either as a
|
||||
* filename which denotes a properties file on disk, or directly as an instance
|
||||
* of the {@link Properties} class. In both cases, the name of each property
|
||||
* represents a username, and the value of the property is the associated access
|
||||
* level. Thus, any given username either does not exist in the properties or
|
||||
* has exactly one access level. The same access level can be shared by several
|
||||
* usernames.</p>
|
||||
*
|
||||
* <p>The supported access level values are {@code readonly} and
|
||||
* {@code readwrite}. The {@code readwrite} access level can be
|
||||
* qualified by one or more <i>clauses</i>, where each clause looks
|
||||
* like <code>create <i>classNamePattern</i></code> or {@code
|
||||
* unregister}. For example:</p>
|
||||
*
|
||||
* <pre>
|
||||
* monitorRole readonly
|
||||
* controlRole readwrite \
|
||||
* create javax.management.timer.*,javax.management.monitor.* \
|
||||
* unregister
|
||||
* </pre>
|
||||
*
|
||||
* <p>(The continuation lines with {@code \} come from the parser for
|
||||
* Properties files.)</p>
|
||||
*/
|
||||
public class MBeanServerFileAccessController
|
||||
extends MBeanServerAccessController {
|
||||
|
||||
static final String READONLY = "readonly";
|
||||
static final String READWRITE = "readwrite";
|
||||
|
||||
static final String CREATE = "create";
|
||||
static final String UNREGISTER = "unregister";
|
||||
|
||||
private enum AccessType {READ, WRITE, CREATE, UNREGISTER};
|
||||
|
||||
private static class Access {
|
||||
final boolean write;
|
||||
final String[] createPatterns;
|
||||
private boolean unregister;
|
||||
|
||||
Access(boolean write, boolean unregister, List<String> createPatternList) {
|
||||
this.write = write;
|
||||
int npats = (createPatternList == null) ? 0 : createPatternList.size();
|
||||
if (npats == 0)
|
||||
this.createPatterns = NO_STRINGS;
|
||||
else
|
||||
this.createPatterns = createPatternList.toArray(new String[npats]);
|
||||
this.unregister = unregister;
|
||||
}
|
||||
|
||||
private final String[] NO_STRINGS = new String[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Create a new MBeanServerAccessController that forwards all the
|
||||
* MBeanServer requests to the MBeanServer set by invoking the {@link
|
||||
* #setMBeanServer} method after doing access checks based on read and
|
||||
* write permissions.</p>
|
||||
*
|
||||
* <p>This instance is initialized from the specified properties file.</p>
|
||||
*
|
||||
* @param accessFileName name of the file which denotes a properties
|
||||
* file on disk containing the username/access level entries.
|
||||
*
|
||||
* @exception IOException if the file does not exist, is a
|
||||
* directory rather than a regular file, or for some other
|
||||
* reason cannot be opened for reading.
|
||||
*
|
||||
* @exception IllegalArgumentException if any of the supplied access
|
||||
* level values differs from "readonly" or "readwrite".
|
||||
*/
|
||||
public MBeanServerFileAccessController(String accessFileName)
|
||||
throws IOException {
|
||||
super();
|
||||
this.accessFileName = accessFileName;
|
||||
Properties props = propertiesFromFile(accessFileName);
|
||||
parseProperties(props);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Create a new MBeanServerAccessController that forwards all the
|
||||
* MBeanServer requests to <code>mbs</code> after doing access checks
|
||||
* based on read and write permissions.</p>
|
||||
*
|
||||
* <p>This instance is initialized from the specified properties file.</p>
|
||||
*
|
||||
* @param accessFileName name of the file which denotes a properties
|
||||
* file on disk containing the username/access level entries.
|
||||
*
|
||||
* @param mbs the MBeanServer object to which requests will be forwarded.
|
||||
*
|
||||
* @exception IOException if the file does not exist, is a
|
||||
* directory rather than a regular file, or for some other
|
||||
* reason cannot be opened for reading.
|
||||
*
|
||||
* @exception IllegalArgumentException if any of the supplied access
|
||||
* level values differs from "readonly" or "readwrite".
|
||||
*/
|
||||
public MBeanServerFileAccessController(String accessFileName,
|
||||
MBeanServer mbs)
|
||||
throws IOException {
|
||||
this(accessFileName);
|
||||
setMBeanServer(mbs);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Create a new MBeanServerAccessController that forwards all the
|
||||
* MBeanServer requests to the MBeanServer set by invoking the {@link
|
||||
* #setMBeanServer} method after doing access checks based on read and
|
||||
* write permissions.</p>
|
||||
*
|
||||
* <p>This instance is initialized from the specified properties
|
||||
* instance. This constructor makes a copy of the properties
|
||||
* instance and it is the copy that is consulted to check the
|
||||
* username and access level of an incoming connection. The
|
||||
* original properties object can be modified without affecting
|
||||
* the copy. If the {@link #refresh} method is then called, the
|
||||
* <code>MBeanServerFileAccessController</code> will make a new
|
||||
* copy of the properties object at that time.</p>
|
||||
*
|
||||
* @param accessFileProps properties list containing the username/access
|
||||
* level entries.
|
||||
*
|
||||
* @exception IllegalArgumentException if <code>accessFileProps</code> is
|
||||
* <code>null</code> or if any of the supplied access level values differs
|
||||
* from "readonly" or "readwrite".
|
||||
*/
|
||||
public MBeanServerFileAccessController(Properties accessFileProps)
|
||||
throws IOException {
|
||||
super();
|
||||
if (accessFileProps == null)
|
||||
throw new IllegalArgumentException("Null properties");
|
||||
originalProps = accessFileProps;
|
||||
parseProperties(accessFileProps);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Create a new MBeanServerAccessController that forwards all the
|
||||
* MBeanServer requests to the MBeanServer set by invoking the {@link
|
||||
* #setMBeanServer} method after doing access checks based on read and
|
||||
* write permissions.</p>
|
||||
*
|
||||
* <p>This instance is initialized from the specified properties
|
||||
* instance. This constructor makes a copy of the properties
|
||||
* instance and it is the copy that is consulted to check the
|
||||
* username and access level of an incoming connection. The
|
||||
* original properties object can be modified without affecting
|
||||
* the copy. If the {@link #refresh} method is then called, the
|
||||
* <code>MBeanServerFileAccessController</code> will make a new
|
||||
* copy of the properties object at that time.</p>
|
||||
*
|
||||
* @param accessFileProps properties list containing the username/access
|
||||
* level entries.
|
||||
*
|
||||
* @param mbs the MBeanServer object to which requests will be forwarded.
|
||||
*
|
||||
* @exception IllegalArgumentException if <code>accessFileProps</code> is
|
||||
* <code>null</code> or if any of the supplied access level values differs
|
||||
* from "readonly" or "readwrite".
|
||||
*/
|
||||
public MBeanServerFileAccessController(Properties accessFileProps,
|
||||
MBeanServer mbs)
|
||||
throws IOException {
|
||||
this(accessFileProps);
|
||||
setMBeanServer(mbs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the caller can do read operations. This method does
|
||||
* nothing if so, otherwise throws SecurityException.
|
||||
*/
|
||||
@Override
|
||||
public void checkRead() {
|
||||
checkAccess(AccessType.READ, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the caller can do write operations. This method does
|
||||
* nothing if so, otherwise throws SecurityException.
|
||||
*/
|
||||
@Override
|
||||
public void checkWrite() {
|
||||
checkAccess(AccessType.WRITE, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the caller can create MBeans or instances of the given class.
|
||||
* This method does nothing if so, otherwise throws SecurityException.
|
||||
*/
|
||||
@Override
|
||||
public void checkCreate(String className) {
|
||||
checkAccess(AccessType.CREATE, className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the caller can do unregister operations. This method does
|
||||
* nothing if so, otherwise throws SecurityException.
|
||||
*/
|
||||
@Override
|
||||
public void checkUnregister(ObjectName name) {
|
||||
checkAccess(AccessType.UNREGISTER, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Refresh the set of username/access level entries.</p>
|
||||
*
|
||||
* <p>If this instance was created using the
|
||||
* {@link #MBeanServerFileAccessController(String)} or
|
||||
* {@link #MBeanServerFileAccessController(String,MBeanServer)}
|
||||
* constructors to specify a file from which the entries are read,
|
||||
* the file is re-read.</p>
|
||||
*
|
||||
* <p>If this instance was created using the
|
||||
* {@link #MBeanServerFileAccessController(Properties)} or
|
||||
* {@link #MBeanServerFileAccessController(Properties,MBeanServer)}
|
||||
* constructors then a new copy of the <code>Properties</code> object
|
||||
* is made.</p>
|
||||
*
|
||||
* @exception IOException if the file does not exist, is a
|
||||
* directory rather than a regular file, or for some other
|
||||
* reason cannot be opened for reading.
|
||||
*
|
||||
* @exception IllegalArgumentException if any of the supplied access
|
||||
* level values differs from "readonly" or "readwrite".
|
||||
*/
|
||||
public synchronized void refresh() throws IOException {
|
||||
Properties props;
|
||||
if (accessFileName == null)
|
||||
props = (Properties) originalProps;
|
||||
else
|
||||
props = propertiesFromFile(accessFileName);
|
||||
parseProperties(props);
|
||||
}
|
||||
|
||||
private static Properties propertiesFromFile(String fname)
|
||||
throws IOException {
|
||||
FileInputStream fin = new FileInputStream(fname);
|
||||
try {
|
||||
Properties p = new Properties();
|
||||
p.load(fin);
|
||||
return p;
|
||||
} finally {
|
||||
fin.close();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void checkAccess(AccessType requiredAccess, String arg) {
|
||||
final AccessControlContext acc = AccessController.getContext();
|
||||
final Subject s =
|
||||
AccessController.doPrivileged(new PrivilegedAction<Subject>() {
|
||||
public Subject run() {
|
||||
return Subject.getSubject(acc);
|
||||
}
|
||||
});
|
||||
if (s == null) return; /* security has not been enabled */
|
||||
final Set principals = s.getPrincipals();
|
||||
String newPropertyValue = null;
|
||||
for (Iterator i = principals.iterator(); i.hasNext(); ) {
|
||||
final Principal p = (Principal) i.next();
|
||||
Access access = accessMap.get(p.getName());
|
||||
if (access != null) {
|
||||
boolean ok;
|
||||
switch (requiredAccess) {
|
||||
case READ:
|
||||
ok = true; // all access entries imply read
|
||||
break;
|
||||
case WRITE:
|
||||
ok = access.write;
|
||||
break;
|
||||
case UNREGISTER:
|
||||
ok = access.unregister;
|
||||
if (!ok && access.write)
|
||||
newPropertyValue = "unregister";
|
||||
break;
|
||||
case CREATE:
|
||||
ok = checkCreateAccess(access, arg);
|
||||
if (!ok && access.write)
|
||||
newPropertyValue = "create " + arg;
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
if (ok)
|
||||
return;
|
||||
}
|
||||
}
|
||||
SecurityException se = new SecurityException("Access denied! Invalid " +
|
||||
"access level for requested MBeanServer operation.");
|
||||
// Add some more information to help people with deployments that
|
||||
// worked before we required explicit create clauses. We're not giving
|
||||
// any information to the bad guys, other than that the access control
|
||||
// is based on a file, which they could have worked out from the stack
|
||||
// trace anyway.
|
||||
if (newPropertyValue != null) {
|
||||
SecurityException se2 = new SecurityException("Access property " +
|
||||
"for this identity should be similar to: " + READWRITE +
|
||||
" " + newPropertyValue);
|
||||
se.initCause(se2);
|
||||
}
|
||||
throw se;
|
||||
}
|
||||
|
||||
private static boolean checkCreateAccess(Access access, String className) {
|
||||
for (String classNamePattern : access.createPatterns) {
|
||||
if (classNameMatch(classNamePattern, className))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean classNameMatch(String pattern, String className) {
|
||||
// We studiously avoided regexes when parsing the properties file,
|
||||
// because that is done whenever the VM is started with the
|
||||
// appropriate -Dcom.sun.management options, even if nobody ever
|
||||
// creates an MBean. We don't want to incur the overhead of loading
|
||||
// all the regex code whenever those options are specified, but if we
|
||||
// get as far as here then the VM is already running and somebody is
|
||||
// doing the very unusual operation of remotely creating an MBean.
|
||||
// Because that operation is so unusual, we don't try to optimize
|
||||
// by hand-matching or by caching compiled Pattern objects.
|
||||
StringBuilder sb = new StringBuilder();
|
||||
StringTokenizer stok = new StringTokenizer(pattern, "*", true);
|
||||
while (stok.hasMoreTokens()) {
|
||||
String tok = stok.nextToken();
|
||||
if (tok.equals("*"))
|
||||
sb.append("[^.]*");
|
||||
else
|
||||
sb.append(Pattern.quote(tok));
|
||||
}
|
||||
return className.matches(sb.toString());
|
||||
}
|
||||
|
||||
private void parseProperties(Properties props) {
|
||||
this.accessMap = new HashMap<String, Access>();
|
||||
for (Map.Entry<Object, Object> entry : props.entrySet()) {
|
||||
String identity = (String) entry.getKey();
|
||||
String accessString = (String) entry.getValue();
|
||||
Access access = Parser.parseAccess(identity, accessString);
|
||||
accessMap.put(identity, access);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Parser {
|
||||
private final static int EOS = -1; // pseudo-codepoint "end of string"
|
||||
static {
|
||||
assert !Character.isWhitespace(EOS);
|
||||
}
|
||||
|
||||
private final String identity; // just for better error messages
|
||||
private final String s; // the string we're parsing
|
||||
private final int len; // s.length()
|
||||
private int i;
|
||||
private int c;
|
||||
// At any point, either c is s.codePointAt(i), or i == len and
|
||||
// c is EOS. We use int rather than char because it is conceivable
|
||||
// (if unlikely) that a classname in a create clause might contain
|
||||
// "supplementary characters", the ones that don't fit in the original
|
||||
// 16 bits for Unicode.
|
||||
|
||||
private Parser(String identity, String s) {
|
||||
this.identity = identity;
|
||||
this.s = s;
|
||||
this.len = s.length();
|
||||
this.i = 0;
|
||||
if (i < len)
|
||||
this.c = s.codePointAt(i);
|
||||
else
|
||||
this.c = EOS;
|
||||
}
|
||||
|
||||
static Access parseAccess(String identity, String s) {
|
||||
return new Parser(identity, s).parseAccess();
|
||||
}
|
||||
|
||||
private Access parseAccess() {
|
||||
skipSpace();
|
||||
String type = parseWord();
|
||||
Access access;
|
||||
if (type.equals(READONLY))
|
||||
access = new Access(false, false, null);
|
||||
else if (type.equals(READWRITE))
|
||||
access = parseReadWrite();
|
||||
else {
|
||||
throw syntax("Expected " + READONLY + " or " + READWRITE +
|
||||
": " + type);
|
||||
}
|
||||
if (c != EOS)
|
||||
throw syntax("Extra text at end of line");
|
||||
return access;
|
||||
}
|
||||
|
||||
private Access parseReadWrite() {
|
||||
List<String> createClasses = new ArrayList<String>();
|
||||
boolean unregister = false;
|
||||
while (true) {
|
||||
skipSpace();
|
||||
if (c == EOS)
|
||||
break;
|
||||
String type = parseWord();
|
||||
if (type.equals(UNREGISTER))
|
||||
unregister = true;
|
||||
else if (type.equals(CREATE))
|
||||
parseCreate(createClasses);
|
||||
else
|
||||
throw syntax("Unrecognized keyword " + type);
|
||||
}
|
||||
return new Access(true, unregister, createClasses);
|
||||
}
|
||||
|
||||
private void parseCreate(List<String> createClasses) {
|
||||
while (true) {
|
||||
skipSpace();
|
||||
createClasses.add(parseClassName());
|
||||
skipSpace();
|
||||
if (c == ',')
|
||||
next();
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private String parseClassName() {
|
||||
// We don't check that classname components begin with suitable
|
||||
// characters (so we accept 1.2.3 for example). This means that
|
||||
// there are only two states, which we can call dotOK and !dotOK
|
||||
// according as a dot (.) is legal or not. Initially we're in
|
||||
// !dotOK since a classname can't start with a dot; after a dot
|
||||
// we're in !dotOK again; and after any other characters we're in
|
||||
// dotOK. The classname is only accepted if we end in dotOK,
|
||||
// so we reject an empty name or a name that ends with a dot.
|
||||
final int start = i;
|
||||
boolean dotOK = false;
|
||||
while (true) {
|
||||
if (c == '.') {
|
||||
if (!dotOK)
|
||||
throw syntax("Bad . in class name");
|
||||
dotOK = false;
|
||||
} else if (c == '*' || Character.isJavaIdentifierPart(c))
|
||||
dotOK = true;
|
||||
else
|
||||
break;
|
||||
next();
|
||||
}
|
||||
String className = s.substring(start, i);
|
||||
if (!dotOK)
|
||||
throw syntax("Bad class name " + className);
|
||||
return className;
|
||||
}
|
||||
|
||||
// Advance c and i to the next character, unless already at EOS.
|
||||
private void next() {
|
||||
if (c != EOS) {
|
||||
i += Character.charCount(c);
|
||||
if (i < len)
|
||||
c = s.codePointAt(i);
|
||||
else
|
||||
c = EOS;
|
||||
}
|
||||
}
|
||||
|
||||
private void skipSpace() {
|
||||
while (Character.isWhitespace(c))
|
||||
next();
|
||||
}
|
||||
|
||||
private String parseWord() {
|
||||
skipSpace();
|
||||
if (c == EOS)
|
||||
throw syntax("Expected word at end of line");
|
||||
final int start = i;
|
||||
while (c != EOS && !Character.isWhitespace(c))
|
||||
next();
|
||||
String word = s.substring(start, i);
|
||||
skipSpace();
|
||||
return word;
|
||||
}
|
||||
|
||||
private IllegalArgumentException syntax(String msg) {
|
||||
return new IllegalArgumentException(
|
||||
msg + " [" + identity + " " + s + "]");
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Access> accessMap;
|
||||
private Properties originalProps;
|
||||
private String accessFileName;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 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.security;
|
||||
|
||||
import javax.management.Notification;
|
||||
import javax.management.ObjectName;
|
||||
import javax.security.auth.Subject;
|
||||
|
||||
/**
|
||||
* <p>This interface allows to control remote access to the
|
||||
* {@code addNotificationListener} and {@code removeNotificationListener}
|
||||
* methods when the notification listener parameter is of type
|
||||
* {@code NotificationListener} and also allows to control remote access
|
||||
* to the notifications being forwarded to the interested remote listeners.</p>
|
||||
*
|
||||
* <p>An implementation of this interface can be supplied to a
|
||||
* {@code JMXConnectorServer} in the environment map through the
|
||||
* {@code com.sun.jmx.remote.notification.access.controller}
|
||||
* environment map property.</p>
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
public interface NotificationAccessController {
|
||||
|
||||
/**
|
||||
* This method is called when a remote
|
||||
* {@link javax.management.remote.JMXConnector} invokes the method
|
||||
* {@link javax.management.MBeanServerConnection#addNotificationListener(ObjectName,NotificationListener,NotificationFilter,Object)}.
|
||||
*
|
||||
* @param connectionId the {@code connectionId} of the remote client
|
||||
* adding the listener.
|
||||
* @param name the name of the MBean where the listener is to be added.
|
||||
* @param subject the authenticated subject representing the remote client.
|
||||
*
|
||||
* @throws SecurityException if the remote client with the supplied
|
||||
* authenticated subject does not have the rights to add a listener
|
||||
* to the supplied MBean.
|
||||
*/
|
||||
public void addNotificationListener(String connectionId,
|
||||
ObjectName name,
|
||||
Subject subject)
|
||||
throws SecurityException;
|
||||
|
||||
/**
|
||||
* This method is called when a remote
|
||||
* {@link javax.management.remote.JMXConnector} invokes the method
|
||||
* {@link javax.management.MBeanServerConnection#removeNotificationListener(ObjectName,NotificationListener)}
|
||||
* or the method
|
||||
* {@link javax.management.MBeanServerConnection#removeNotificationListener(ObjectName,NotificationListener,NotificationFilter,Object)}.
|
||||
*
|
||||
* @param connectionId the {@code connectionId} of the remote client
|
||||
* removing the listener.
|
||||
* @param name the name of the MBean where the listener is to be removed.
|
||||
* @param subject the authenticated subject representing the remote client.
|
||||
*
|
||||
* @throws SecurityException if the remote client with the supplied
|
||||
* authenticated subject does not have the rights to remove a listener
|
||||
* from the supplied MBean.
|
||||
*/
|
||||
public void removeNotificationListener(String connectionId,
|
||||
ObjectName name,
|
||||
Subject subject)
|
||||
throws SecurityException;
|
||||
|
||||
/**
|
||||
* This method is called before the
|
||||
* {@link javax.management.remote.JMXConnectorServer}
|
||||
* forwards the notification to the interested remote
|
||||
* listener represented by the authenticated subject.
|
||||
*
|
||||
* @param connectionId the {@code connectionId} of the remote client
|
||||
* receiving the notification.
|
||||
* @param name the name of the MBean forwarding the notification.
|
||||
* @param notification the notification to be forwarded to the interested
|
||||
* remote listener.
|
||||
* @param subject the authenticated subject representing the remote client.
|
||||
*
|
||||
* @throws SecurityException if the remote client with
|
||||
* the supplied authenticated subject does not have the
|
||||
* rights to receive the notification.
|
||||
*/
|
||||
public void fetchNotification(String connectionId,
|
||||
ObjectName name,
|
||||
Notification notification,
|
||||
Subject subject)
|
||||
throws SecurityException;
|
||||
}
|
||||
125
jdkSrc/jdk8/com/sun/jmx/remote/security/SubjectDelegator.java
Normal file
125
jdkSrc/jdk8/com/sun/jmx/remote/security/SubjectDelegator.java
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2014, 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.security;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.Permission;
|
||||
import java.security.Principal;
|
||||
import java.security.PrivilegedAction;
|
||||
import javax.security.auth.Subject;
|
||||
|
||||
import javax.management.remote.SubjectDelegationPermission;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class SubjectDelegator {
|
||||
/* Return the AccessControlContext appropriate to execute an
|
||||
operation on behalf of the delegatedSubject. If the
|
||||
authenticatedAccessControlContext does not have permission to
|
||||
delegate to that subject, throw SecurityException. */
|
||||
public AccessControlContext
|
||||
delegatedContext(AccessControlContext authenticatedACC,
|
||||
Subject delegatedSubject,
|
||||
boolean removeCallerContext)
|
||||
throws SecurityException {
|
||||
|
||||
if (System.getSecurityManager() != null && authenticatedACC == null) {
|
||||
throw new SecurityException("Illegal AccessControlContext: null");
|
||||
}
|
||||
|
||||
// Check if the subject delegation permission allows the
|
||||
// authenticated subject to assume the identity of each
|
||||
// principal in the delegated subject
|
||||
//
|
||||
Collection<Principal> ps = getSubjectPrincipals(delegatedSubject);
|
||||
final Collection<Permission> permissions = new ArrayList<>(ps.size());
|
||||
for(Principal p : ps) {
|
||||
final String pname = p.getClass().getName() + "." + p.getName();
|
||||
permissions.add(new SubjectDelegationPermission(pname));
|
||||
}
|
||||
PrivilegedAction<Void> action =
|
||||
new PrivilegedAction<Void>() {
|
||||
public Void run() {
|
||||
for (Permission sdp : permissions) {
|
||||
AccessController.checkPermission(sdp);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
AccessController.doPrivileged(action, authenticatedACC);
|
||||
|
||||
return getDelegatedAcc(delegatedSubject, removeCallerContext);
|
||||
}
|
||||
|
||||
private AccessControlContext getDelegatedAcc(Subject delegatedSubject, boolean removeCallerContext) {
|
||||
if (removeCallerContext) {
|
||||
return JMXSubjectDomainCombiner.getDomainCombinerContext(delegatedSubject);
|
||||
} else {
|
||||
return JMXSubjectDomainCombiner.getContext(delegatedSubject);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the connector server creator can assume the identity of each
|
||||
* principal in the authenticated subject, i.e. check if the connector
|
||||
* server creator codebase contains a subject delegation permission for
|
||||
* each principal present in the authenticated subject.
|
||||
*
|
||||
* @return {@code true} if the connector server creator can delegate to all
|
||||
* the authenticated principals in the subject. Otherwise, {@code false}.
|
||||
*/
|
||||
public static synchronized boolean
|
||||
checkRemoveCallerContext(Subject subject) {
|
||||
try {
|
||||
for (Principal p : getSubjectPrincipals(subject)) {
|
||||
final String pname =
|
||||
p.getClass().getName() + "." + p.getName();
|
||||
final Permission sdp =
|
||||
new SubjectDelegationPermission(pname);
|
||||
AccessController.checkPermission(sdp);
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the {@linkplain Subject} principals
|
||||
* @param subject The subject
|
||||
* @return If the {@code Subject} is immutable it will return the principals directly.
|
||||
* If the {@code Subject} is mutable it will create an unmodifiable copy.
|
||||
*/
|
||||
private static Collection<Principal> getSubjectPrincipals(Subject subject) {
|
||||
if (subject.isReadOnly()) {
|
||||
return subject.getPrincipals();
|
||||
}
|
||||
|
||||
List<Principal> principals = Arrays.asList(subject.getPrincipals().toArray(new Principal[0]));
|
||||
return Collections.unmodifiableList(principals);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import javax.management.loading.ClassLoaderRepository;
|
||||
|
||||
public class ClassLoaderWithRepository extends ClassLoader {
|
||||
public ClassLoaderWithRepository(ClassLoaderRepository clr,
|
||||
ClassLoader cl2) {
|
||||
|
||||
if (clr == null) throw new
|
||||
IllegalArgumentException("Null ClassLoaderRepository object.");
|
||||
|
||||
repository = clr;
|
||||
this.cl2 = cl2;
|
||||
}
|
||||
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
Class<?> cls;
|
||||
try {
|
||||
cls = repository.loadClass(name);
|
||||
} catch (ClassNotFoundException cne) {
|
||||
if (cl2 != null) {
|
||||
return cl2.loadClass(name);
|
||||
} else {
|
||||
throw cne;
|
||||
}
|
||||
}
|
||||
|
||||
if(!cls.getName().equals(name)){
|
||||
if (cl2 != null) {
|
||||
return cl2.loadClass(name);
|
||||
} else {
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
}
|
||||
return cls;
|
||||
}
|
||||
|
||||
private ClassLoaderRepository repository;
|
||||
private ClassLoader cl2;
|
||||
}
|
||||
246
jdkSrc/jdk8/com/sun/jmx/remote/util/ClassLogger.java
Normal file
246
jdkSrc/jdk8/com/sun/jmx/remote/util/ClassLogger.java
Normal file
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class ClassLogger {
|
||||
|
||||
private static final boolean ok;
|
||||
private final String className;
|
||||
private final Logger logger;
|
||||
|
||||
static {
|
||||
/* We attempt to work even if we are running in J2SE 1.3, where
|
||||
there is no java.util.logging. The technique we use here is
|
||||
not strictly portable, but it does work with Sun's J2SE 1.3
|
||||
at least. This is just a best effort: the Right Thing is for
|
||||
people to use at least J2SE 1.4. */
|
||||
boolean loaded = false;
|
||||
try {
|
||||
Class<?> c = java.util.logging.Logger.class;
|
||||
loaded = true;
|
||||
} catch (Error e) {
|
||||
// OK.
|
||||
// java.util.logger package is not available in this jvm.
|
||||
}
|
||||
ok = loaded;
|
||||
}
|
||||
|
||||
public ClassLogger(String subsystem, String className) {
|
||||
if (ok)
|
||||
logger = Logger.getLogger(subsystem);
|
||||
else
|
||||
logger = null;
|
||||
this.className = className;
|
||||
}
|
||||
|
||||
public final boolean traceOn() {
|
||||
return finerOn();
|
||||
}
|
||||
|
||||
public final boolean debugOn() {
|
||||
return finestOn();
|
||||
}
|
||||
|
||||
public final boolean warningOn() {
|
||||
return ok && logger.isLoggable(java.util.logging.Level.WARNING);
|
||||
}
|
||||
|
||||
public final boolean infoOn() {
|
||||
return ok && logger.isLoggable(java.util.logging.Level.INFO);
|
||||
}
|
||||
|
||||
public final boolean configOn() {
|
||||
return ok && logger.isLoggable(java.util.logging.Level.CONFIG);
|
||||
}
|
||||
|
||||
public final boolean fineOn() {
|
||||
return ok && logger.isLoggable(java.util.logging.Level.FINE);
|
||||
}
|
||||
|
||||
public final boolean finerOn() {
|
||||
return ok && logger.isLoggable(java.util.logging.Level.FINER);
|
||||
}
|
||||
|
||||
public final boolean finestOn() {
|
||||
return ok && logger.isLoggable(java.util.logging.Level.FINEST);
|
||||
}
|
||||
|
||||
public final void debug(String func, String msg) {
|
||||
finest(func,msg);
|
||||
}
|
||||
|
||||
public final void debug(String func, Throwable t) {
|
||||
finest(func,t);
|
||||
}
|
||||
|
||||
public final void debug(String func, String msg, Throwable t) {
|
||||
finest(func,msg,t);
|
||||
}
|
||||
|
||||
public final void trace(String func, String msg) {
|
||||
finer(func,msg);
|
||||
}
|
||||
|
||||
public final void trace(String func, Throwable t) {
|
||||
finer(func,t);
|
||||
}
|
||||
|
||||
public final void trace(String func, String msg, Throwable t) {
|
||||
finer(func,msg,t);
|
||||
}
|
||||
|
||||
public final void error(String func, String msg) {
|
||||
severe(func,msg);
|
||||
}
|
||||
|
||||
public final void error(String func, Throwable t) {
|
||||
severe(func,t);
|
||||
}
|
||||
|
||||
public final void error(String func, String msg, Throwable t) {
|
||||
severe(func,msg,t);
|
||||
}
|
||||
|
||||
public final void finest(String func, String msg) {
|
||||
if (ok)
|
||||
logger.logp(java.util.logging.Level.FINEST, className, func, msg);
|
||||
}
|
||||
|
||||
public final void finest(String func, Throwable t) {
|
||||
if (ok)
|
||||
logger.logp(java.util.logging.Level.FINEST, className, func,
|
||||
t.toString(), t);
|
||||
}
|
||||
|
||||
public final void finest(String func, String msg, Throwable t) {
|
||||
if (ok)
|
||||
logger.logp(java.util.logging.Level.FINEST, className, func, msg,
|
||||
t);
|
||||
}
|
||||
|
||||
public final void finer(String func, String msg) {
|
||||
if (ok)
|
||||
logger.logp(java.util.logging.Level.FINER, className, func, msg);
|
||||
}
|
||||
|
||||
public final void finer(String func, Throwable t) {
|
||||
if (ok)
|
||||
logger.logp(java.util.logging.Level.FINER, className, func,
|
||||
t.toString(), t);
|
||||
}
|
||||
|
||||
public final void finer(String func, String msg, Throwable t) {
|
||||
if (ok)
|
||||
logger.logp(java.util.logging.Level.FINER, className, func, msg,t);
|
||||
}
|
||||
|
||||
public final void fine(String func, String msg) {
|
||||
if (ok)
|
||||
logger.logp(java.util.logging.Level.FINE, className, func, msg);
|
||||
}
|
||||
|
||||
public final void fine(String func, Throwable t) {
|
||||
if (ok)
|
||||
logger.logp(java.util.logging.Level.FINE, className, func,
|
||||
t.toString(), t);
|
||||
}
|
||||
|
||||
public final void fine(String func, String msg, Throwable t) {
|
||||
if (ok)
|
||||
logger.logp(java.util.logging.Level.FINE, className, func, msg,
|
||||
t);
|
||||
}
|
||||
|
||||
public final void config(String func, String msg) {
|
||||
if (ok)
|
||||
logger.logp(java.util.logging.Level.CONFIG, className, func, msg);
|
||||
}
|
||||
|
||||
public final void config(String func, Throwable t) {
|
||||
if (ok)
|
||||
logger.logp(java.util.logging.Level.CONFIG, className, func,
|
||||
t.toString(), t);
|
||||
}
|
||||
|
||||
public final void config(String func, String msg, Throwable t) {
|
||||
if (ok)
|
||||
logger.logp(java.util.logging.Level.CONFIG, className, func, msg,
|
||||
t);
|
||||
}
|
||||
|
||||
public final void info(String func, String msg) {
|
||||
if (ok)
|
||||
logger.logp(java.util.logging.Level.INFO, className, func, msg);
|
||||
}
|
||||
|
||||
public final void info(String func, Throwable t) {
|
||||
if (ok)
|
||||
logger.logp(java.util.logging.Level.INFO, className, func,
|
||||
t.toString(), t);
|
||||
}
|
||||
|
||||
public final void info(String func, String msg, Throwable t) {
|
||||
if (ok)
|
||||
logger.logp(java.util.logging.Level.INFO, className, func, msg,
|
||||
t);
|
||||
}
|
||||
|
||||
public final void warning(String func, String msg) {
|
||||
if (ok)
|
||||
logger.logp(java.util.logging.Level.WARNING, className, func, msg);
|
||||
}
|
||||
|
||||
public final void warning(String func, Throwable t) {
|
||||
if (ok)
|
||||
logger.logp(java.util.logging.Level.WARNING, className, func,
|
||||
t.toString(), t);
|
||||
}
|
||||
|
||||
public final void warning(String func, String msg, Throwable t) {
|
||||
if (ok)
|
||||
logger.logp(java.util.logging.Level.WARNING, className, func, msg,
|
||||
t);
|
||||
}
|
||||
|
||||
public final void severe(String func, String msg) {
|
||||
if (ok)
|
||||
logger.logp(java.util.logging.Level.SEVERE, className, func, msg);
|
||||
}
|
||||
|
||||
public final void severe(String func, Throwable t) {
|
||||
if (ok)
|
||||
logger.logp(java.util.logging.Level.SEVERE, className, func,
|
||||
t.toString(), t);
|
||||
}
|
||||
|
||||
public final void severe(String func, String msg, Throwable t) {
|
||||
if (ok)
|
||||
logger.logp(java.util.logging.Level.SEVERE, className, func, msg,
|
||||
t);
|
||||
}
|
||||
}
|
||||
783
jdkSrc/jdk8/com/sun/jmx/remote/util/EnvHelp.java
Normal file
783
jdkSrc/jdk8/com/sun/jmx/remote/util/EnvHelp.java
Normal file
@@ -0,0 +1,783 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2003, 2016, 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.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.SortedMap;
|
||||
import java.util.SortedSet;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import java.security.AccessController;
|
||||
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.InstanceNotFoundException;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXConnectorServerFactory;
|
||||
import com.sun.jmx.mbeanserver.GetPropertyAction;
|
||||
import com.sun.jmx.remote.security.NotificationAccessController;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
|
||||
public class EnvHelp {
|
||||
|
||||
/**
|
||||
* Name of the attribute that specifies a list of class names acceptable
|
||||
* as parameters to the {@link RMIServer#newClient(java.lang.Object) RMIServer.newClient()}
|
||||
* remote method call.
|
||||
* <p>
|
||||
* This list of classes should correspond to the transitive closure of the
|
||||
* credentials class (or classes) used by the installed {@linkplain JMXAuthenticator}
|
||||
* associated with the {@linkplain RMIServer} implementation.
|
||||
* <p>
|
||||
* If the attribute is not set, or is null, then any class is
|
||||
* deemed acceptable.
|
||||
*/
|
||||
public static final String CREDENTIAL_TYPES =
|
||||
"jmx.remote.rmi.server.credential.types";
|
||||
|
||||
/**
|
||||
* <p>Name of the attribute that specifies a default class loader
|
||||
* object.
|
||||
* The value associated with this attribute is a ClassLoader object</p>
|
||||
*/
|
||||
private static final String DEFAULT_CLASS_LOADER =
|
||||
JMXConnectorFactory.DEFAULT_CLASS_LOADER;
|
||||
|
||||
/**
|
||||
* <p>Name of the attribute that specifies a default class loader
|
||||
* ObjectName.
|
||||
* The value associated with this attribute is an ObjectName object</p>
|
||||
*/
|
||||
private static final String DEFAULT_CLASS_LOADER_NAME =
|
||||
JMXConnectorServerFactory.DEFAULT_CLASS_LOADER_NAME;
|
||||
|
||||
/**
|
||||
* Get the Connector Server default class loader.
|
||||
* <p>
|
||||
* Returns:
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li>
|
||||
* The ClassLoader object found in <var>env</var> for
|
||||
* <code>jmx.remote.default.class.loader</code>, if any.
|
||||
* </li>
|
||||
* <li>
|
||||
* The ClassLoader pointed to by the ObjectName found in
|
||||
* <var>env</var> for <code>jmx.remote.default.class.loader.name</code>,
|
||||
* and registered in <var>mbs</var> if any.
|
||||
* </li>
|
||||
* <li>
|
||||
* The current thread's context classloader otherwise.
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* @param env Environment attributes.
|
||||
* @param mbs The MBeanServer for which the connector server provides
|
||||
* remote access.
|
||||
*
|
||||
* @return the connector server's default class loader.
|
||||
*
|
||||
* @exception IllegalArgumentException if one of the following is true:
|
||||
* <ul>
|
||||
* <li>both
|
||||
* <code>jmx.remote.default.class.loader</code> and
|
||||
* <code>jmx.remote.default.class.loader.name</code> are specified,
|
||||
* </li>
|
||||
* <li>or
|
||||
* <code>jmx.remote.default.class.loader</code> is not
|
||||
* an instance of {@link ClassLoader},
|
||||
* </li>
|
||||
* <li>or
|
||||
* <code>jmx.remote.default.class.loader.name</code> is not
|
||||
* an instance of {@link ObjectName},
|
||||
* </li>
|
||||
* <li>or
|
||||
* <code>jmx.remote.default.class.loader.name</code> is specified
|
||||
* but <var>mbs</var> is null.
|
||||
* </li>
|
||||
* @exception InstanceNotFoundException if
|
||||
* <code>jmx.remote.default.class.loader.name</code> is specified
|
||||
* and the ClassLoader MBean is not found in <var>mbs</var>.
|
||||
*/
|
||||
public static ClassLoader resolveServerClassLoader(Map<String, ?> env,
|
||||
MBeanServer mbs)
|
||||
throws InstanceNotFoundException {
|
||||
|
||||
if (env == null)
|
||||
return Thread.currentThread().getContextClassLoader();
|
||||
|
||||
Object loader = env.get(DEFAULT_CLASS_LOADER);
|
||||
Object name = env.get(DEFAULT_CLASS_LOADER_NAME);
|
||||
|
||||
if (loader != null && name != null) {
|
||||
final String msg = "Only one of " +
|
||||
DEFAULT_CLASS_LOADER + " or " +
|
||||
DEFAULT_CLASS_LOADER_NAME +
|
||||
" should be specified.";
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
|
||||
if (loader == null && name == null)
|
||||
return Thread.currentThread().getContextClassLoader();
|
||||
|
||||
if (loader != null) {
|
||||
if (loader instanceof ClassLoader) {
|
||||
return (ClassLoader) loader;
|
||||
} else {
|
||||
final String msg =
|
||||
"ClassLoader object is not an instance of " +
|
||||
ClassLoader.class.getName() + " : " +
|
||||
loader.getClass().getName();
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
ObjectName on;
|
||||
if (name instanceof ObjectName) {
|
||||
on = (ObjectName) name;
|
||||
} else {
|
||||
final String msg =
|
||||
"ClassLoader name is not an instance of " +
|
||||
ObjectName.class.getName() + " : " +
|
||||
name.getClass().getName();
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
|
||||
if (mbs == null)
|
||||
throw new IllegalArgumentException("Null MBeanServer object");
|
||||
|
||||
return mbs.getClassLoader(on);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Connector Client default class loader.
|
||||
* <p>
|
||||
* Returns:
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li>
|
||||
* The ClassLoader object found in <var>env</var> for
|
||||
* <code>jmx.remote.default.class.loader</code>, if any.
|
||||
* </li>
|
||||
* <li>The <tt>Thread.currentThread().getContextClassLoader()</tt>
|
||||
* otherwise.
|
||||
* </li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Usually a Connector Client will call
|
||||
* <pre>
|
||||
* ClassLoader dcl = EnvHelp.resolveClientClassLoader(env);
|
||||
* </pre>
|
||||
* in its <code>connect(Map env)</code> method.
|
||||
*
|
||||
* @return The connector client default class loader.
|
||||
*
|
||||
* @exception IllegalArgumentException if
|
||||
* <code>jmx.remote.default.class.loader</code> is specified
|
||||
* and is not an instance of {@link ClassLoader}.
|
||||
*/
|
||||
public static ClassLoader resolveClientClassLoader(Map<String, ?> env) {
|
||||
|
||||
if (env == null)
|
||||
return Thread.currentThread().getContextClassLoader();
|
||||
|
||||
Object loader = env.get(DEFAULT_CLASS_LOADER);
|
||||
|
||||
if (loader == null)
|
||||
return Thread.currentThread().getContextClassLoader();
|
||||
|
||||
if (loader instanceof ClassLoader) {
|
||||
return (ClassLoader) loader;
|
||||
} else {
|
||||
final String msg =
|
||||
"ClassLoader object is not an instance of " +
|
||||
ClassLoader.class.getName() + " : " +
|
||||
loader.getClass().getName();
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the cause field of a {@code Throwable} object.
|
||||
*
|
||||
* @param throwable The {@code Throwable} on which the cause is set.
|
||||
* @param cause The cause to set on the supplied {@code Throwable}.
|
||||
* @return the {@code Throwable} with the cause field initialized.
|
||||
*/
|
||||
public static <T extends Throwable> T initCause(T throwable,
|
||||
Throwable cause) {
|
||||
throwable.initCause(cause);
|
||||
return throwable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cause field of a {@code Throwable} object.
|
||||
* The cause field can be got only if <var>t</var> has an
|
||||
* {@link Throwable#getCause()} method (JDK Version >= 1.4)
|
||||
* @param t {@code Throwable} on which the cause must be set.
|
||||
* @return the cause if getCause() succeeded and the got value is not
|
||||
* null, otherwise return the <var>t</var>.
|
||||
*/
|
||||
public static Throwable getCause(Throwable t) {
|
||||
Throwable ret = t;
|
||||
|
||||
try {
|
||||
java.lang.reflect.Method getCause =
|
||||
t.getClass().getMethod("getCause", (Class<?>[]) null);
|
||||
ret = (Throwable)getCause.invoke(t, (Object[]) null);
|
||||
|
||||
} catch (Exception e) {
|
||||
// OK.
|
||||
// it must be older than 1.4.
|
||||
}
|
||||
return (ret != null) ? ret: t;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>Name of the attribute that specifies the size of a notification
|
||||
* buffer for a connector server. The default value is 1000.
|
||||
*/
|
||||
public static final String BUFFER_SIZE_PROPERTY =
|
||||
"jmx.remote.x.notification.buffer.size";
|
||||
|
||||
|
||||
/**
|
||||
* Returns the size of a notification buffer for a connector server.
|
||||
* The default value is 1000.
|
||||
*/
|
||||
public static int getNotifBufferSize(Map<String, ?> env) {
|
||||
int defaultQueueSize = 1000; // default value
|
||||
|
||||
// keep it for the compability for the fix:
|
||||
// 6174229: Environment parameter should be notification.buffer.size
|
||||
// instead of buffer.size
|
||||
final String oldP = "jmx.remote.x.buffer.size";
|
||||
|
||||
// the default value re-specified in the system
|
||||
try {
|
||||
GetPropertyAction act = new GetPropertyAction(BUFFER_SIZE_PROPERTY);
|
||||
String s = AccessController.doPrivileged(act);
|
||||
if (s != null) {
|
||||
defaultQueueSize = Integer.parseInt(s);
|
||||
} else { // try the old one
|
||||
act = new GetPropertyAction(oldP);
|
||||
s = AccessController.doPrivileged(act);
|
||||
if (s != null) {
|
||||
defaultQueueSize = Integer.parseInt(s);
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
logger.warning("getNotifBufferSize",
|
||||
"Can't use System property "+
|
||||
BUFFER_SIZE_PROPERTY+ ": " + e);
|
||||
logger.debug("getNotifBufferSize", e);
|
||||
}
|
||||
|
||||
int queueSize = defaultQueueSize;
|
||||
|
||||
try {
|
||||
if (env.containsKey(BUFFER_SIZE_PROPERTY)) {
|
||||
queueSize = (int)EnvHelp.getIntegerAttribute(env,BUFFER_SIZE_PROPERTY,
|
||||
defaultQueueSize,0,
|
||||
Integer.MAX_VALUE);
|
||||
} else { // try the old one
|
||||
queueSize = (int)EnvHelp.getIntegerAttribute(env,oldP,
|
||||
defaultQueueSize,0,
|
||||
Integer.MAX_VALUE);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
logger.warning("getNotifBufferSize",
|
||||
"Can't determine queuesize (using default): "+
|
||||
e);
|
||||
logger.debug("getNotifBufferSize", e);
|
||||
}
|
||||
|
||||
return queueSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Name of the attribute that specifies the maximum number of
|
||||
* notifications that a client will fetch from its server.. The
|
||||
* value associated with this attribute should be an
|
||||
* <code>Integer</code> object. The default value is 1000.</p>
|
||||
*/
|
||||
public static final String MAX_FETCH_NOTIFS =
|
||||
"jmx.remote.x.notification.fetch.max";
|
||||
|
||||
/**
|
||||
* Returns the maximum notification number which a client will
|
||||
* fetch every time.
|
||||
*/
|
||||
public static int getMaxFetchNotifNumber(Map<String, ?> env) {
|
||||
return (int) getIntegerAttribute(env, MAX_FETCH_NOTIFS, 1000, 1,
|
||||
Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Name of the attribute that specifies the timeout for a
|
||||
* client to fetch notifications from its server. The value
|
||||
* associated with this attribute should be a <code>Long</code>
|
||||
* object. The default value is 60000 milliseconds.</p>
|
||||
*/
|
||||
public static final String FETCH_TIMEOUT =
|
||||
"jmx.remote.x.notification.fetch.timeout";
|
||||
|
||||
/**
|
||||
* Returns the timeout for a client to fetch notifications.
|
||||
*/
|
||||
public static long getFetchTimeout(Map<String, ?> env) {
|
||||
return getIntegerAttribute(env, FETCH_TIMEOUT, 60000L, 0,
|
||||
Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Name of the attribute that specifies an object that will check
|
||||
* accesses to add/removeNotificationListener and also attempts to
|
||||
* receive notifications. The value associated with this attribute
|
||||
* should be a <code>NotificationAccessController</code> object.
|
||||
* The default value is null.</p>
|
||||
* This field is not public because of its com.sun dependency.
|
||||
*/
|
||||
public static final String NOTIF_ACCESS_CONTROLLER =
|
||||
"com.sun.jmx.remote.notification.access.controller";
|
||||
|
||||
public static NotificationAccessController getNotificationAccessController(
|
||||
Map<String, ?> env) {
|
||||
return (env == null) ? null :
|
||||
(NotificationAccessController) env.get(NOTIF_ACCESS_CONTROLLER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an integer-valued attribute with name <code>name</code>
|
||||
* from <code>env</code>. If <code>env</code> is null, or does
|
||||
* not contain an entry for <code>name</code>, return
|
||||
* <code>defaultValue</code>. The value may be a Number, or it
|
||||
* may be a String that is parsable as a long. It must be at
|
||||
* least <code>minValue</code> and at most<code>maxValue</code>.
|
||||
*
|
||||
* @throws IllegalArgumentException if <code>env</code> contains
|
||||
* an entry for <code>name</code> but it does not meet the
|
||||
* constraints above.
|
||||
*/
|
||||
public static long getIntegerAttribute(Map<String, ?> env, String name,
|
||||
long defaultValue, long minValue,
|
||||
long maxValue) {
|
||||
final Object o;
|
||||
|
||||
if (env == null || (o = env.get(name)) == null)
|
||||
return defaultValue;
|
||||
|
||||
final long result;
|
||||
|
||||
if (o instanceof Number)
|
||||
result = ((Number) o).longValue();
|
||||
else if (o instanceof String) {
|
||||
result = Long.parseLong((String) o);
|
||||
/* May throw a NumberFormatException, which is an
|
||||
IllegalArgumentException. */
|
||||
} else {
|
||||
final String msg =
|
||||
"Attribute " + name + " value must be Integer or String: " + o;
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
|
||||
if (result < minValue) {
|
||||
final String msg =
|
||||
"Attribute " + name + " value must be at least " + minValue +
|
||||
": " + result;
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
|
||||
if (result > maxValue) {
|
||||
final String msg =
|
||||
"Attribute " + name + " value must be at most " + maxValue +
|
||||
": " + result;
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static final String DEFAULT_ORB="java.naming.corba.orb";
|
||||
|
||||
/* Check that all attributes have a key that is a String.
|
||||
Could make further checks, e.g. appropriate types for attributes. */
|
||||
public static void checkAttributes(Map<?, ?> attributes) {
|
||||
for (Object key : attributes.keySet()) {
|
||||
if (!(key instanceof String)) {
|
||||
final String msg =
|
||||
"Attributes contain key that is not a string: " + key;
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Return a writable map containing only those attributes that are
|
||||
serializable, and that are not hidden by
|
||||
jmx.remote.x.hidden.attributes or the default list of hidden
|
||||
attributes. */
|
||||
public static <V> Map<String, V> filterAttributes(Map<String, V> attributes) {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("filterAttributes", "starts");
|
||||
}
|
||||
|
||||
SortedMap<String, V> map = new TreeMap<String, V>(attributes);
|
||||
purgeUnserializable(map.values());
|
||||
hideAttributes(map);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove from the given Collection any element that is not a
|
||||
* serializable object.
|
||||
*/
|
||||
private static void purgeUnserializable(Collection<?> objects) {
|
||||
logger.trace("purgeUnserializable", "starts");
|
||||
ObjectOutputStream oos = null;
|
||||
int i = 0;
|
||||
for (Iterator<?> it = objects.iterator(); it.hasNext(); i++) {
|
||||
Object v = it.next();
|
||||
|
||||
if (v == null || v instanceof String) {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("purgeUnserializable",
|
||||
"Value trivially serializable: " + v);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
if (oos == null)
|
||||
oos = new ObjectOutputStream(new SinkOutputStream());
|
||||
oos.writeObject(v);
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("purgeUnserializable",
|
||||
"Value serializable: " + v);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("purgeUnserializable",
|
||||
"Value not serializable: " + v + ": " +
|
||||
e);
|
||||
}
|
||||
it.remove();
|
||||
oos = null; // ObjectOutputStream invalid after exception
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of this attribute, if present, is a string specifying
|
||||
* what other attributes should not appear in
|
||||
* JMXConnectorServer.getAttributes(). It is a space-separated
|
||||
* list of attribute patterns, where each pattern is either an
|
||||
* attribute name, or an attribute prefix followed by a "*"
|
||||
* character. The "*" has no special significance anywhere except
|
||||
* at the end of a pattern. By default, this list is added to the
|
||||
* list defined by {@link #DEFAULT_HIDDEN_ATTRIBUTES} (which
|
||||
* uses the same format). If the value of this attribute begins
|
||||
* with an "=", then the remainder of the string defines the
|
||||
* complete list of attribute patterns.
|
||||
*/
|
||||
public static final String HIDDEN_ATTRIBUTES =
|
||||
"jmx.remote.x.hidden.attributes";
|
||||
|
||||
/**
|
||||
* Default list of attributes not to show.
|
||||
* @see #HIDDEN_ATTRIBUTES
|
||||
*/
|
||||
/* This list is copied directly from the spec, plus
|
||||
java.naming.security.*. Most of the attributes here would have
|
||||
been eliminated from the map anyway because they are typically
|
||||
not serializable. But just in case they are, we list them here
|
||||
to conform to the spec. */
|
||||
public static final String DEFAULT_HIDDEN_ATTRIBUTES =
|
||||
"java.naming.security.* " +
|
||||
"jmx.remote.authenticator " +
|
||||
"jmx.remote.context " +
|
||||
"jmx.remote.default.class.loader " +
|
||||
"jmx.remote.message.connection.server " +
|
||||
"jmx.remote.object.wrapping " +
|
||||
"jmx.remote.rmi.client.socket.factory " +
|
||||
"jmx.remote.rmi.server.socket.factory " +
|
||||
"jmx.remote.sasl.callback.handler " +
|
||||
"jmx.remote.tls.socket.factory " +
|
||||
"jmx.remote.x.access.file " +
|
||||
"jmx.remote.x.password.file ";
|
||||
|
||||
private static final SortedSet<String> defaultHiddenStrings =
|
||||
new TreeSet<String>();
|
||||
private static final SortedSet<String> defaultHiddenPrefixes =
|
||||
new TreeSet<String>();
|
||||
|
||||
private static void hideAttributes(SortedMap<String, ?> map) {
|
||||
if (map.isEmpty())
|
||||
return;
|
||||
|
||||
final SortedSet<String> hiddenStrings;
|
||||
final SortedSet<String> hiddenPrefixes;
|
||||
|
||||
String hide = (String) map.get(HIDDEN_ATTRIBUTES);
|
||||
if (hide != null) {
|
||||
if (hide.startsWith("="))
|
||||
hide = hide.substring(1);
|
||||
else
|
||||
hide += " " + DEFAULT_HIDDEN_ATTRIBUTES;
|
||||
hiddenStrings = new TreeSet<String>();
|
||||
hiddenPrefixes = new TreeSet<String>();
|
||||
parseHiddenAttributes(hide, hiddenStrings, hiddenPrefixes);
|
||||
} else {
|
||||
hide = DEFAULT_HIDDEN_ATTRIBUTES;
|
||||
synchronized (defaultHiddenStrings) {
|
||||
if (defaultHiddenStrings.isEmpty()) {
|
||||
parseHiddenAttributes(hide,
|
||||
defaultHiddenStrings,
|
||||
defaultHiddenPrefixes);
|
||||
}
|
||||
hiddenStrings = defaultHiddenStrings;
|
||||
hiddenPrefixes = defaultHiddenPrefixes;
|
||||
}
|
||||
}
|
||||
|
||||
/* Construct a string that is greater than any key in the map.
|
||||
Setting a string-to-match or a prefix-to-match to this string
|
||||
guarantees that we will never call next() on the corresponding
|
||||
iterator. */
|
||||
String sentinelKey = map.lastKey() + "X";
|
||||
Iterator<String> keyIterator = map.keySet().iterator();
|
||||
Iterator<String> stringIterator = hiddenStrings.iterator();
|
||||
Iterator<String> prefixIterator = hiddenPrefixes.iterator();
|
||||
|
||||
String nextString;
|
||||
if (stringIterator.hasNext())
|
||||
nextString = stringIterator.next();
|
||||
else
|
||||
nextString = sentinelKey;
|
||||
String nextPrefix;
|
||||
if (prefixIterator.hasNext())
|
||||
nextPrefix = prefixIterator.next();
|
||||
else
|
||||
nextPrefix = sentinelKey;
|
||||
|
||||
/* Read each key in sorted order and, if it matches a string
|
||||
or prefix, remove it. */
|
||||
keys:
|
||||
while (keyIterator.hasNext()) {
|
||||
String key = keyIterator.next();
|
||||
|
||||
/* Continue through string-match values until we find one
|
||||
that is either greater than the current key, or equal
|
||||
to it. In the latter case, remove the key. */
|
||||
int cmp = +1;
|
||||
while ((cmp = nextString.compareTo(key)) < 0) {
|
||||
if (stringIterator.hasNext())
|
||||
nextString = stringIterator.next();
|
||||
else
|
||||
nextString = sentinelKey;
|
||||
}
|
||||
if (cmp == 0) {
|
||||
keyIterator.remove();
|
||||
continue keys;
|
||||
}
|
||||
|
||||
/* Continue through the prefix values until we find one
|
||||
that is either greater than the current key, or a
|
||||
prefix of it. In the latter case, remove the key. */
|
||||
while (nextPrefix.compareTo(key) <= 0) {
|
||||
if (key.startsWith(nextPrefix)) {
|
||||
keyIterator.remove();
|
||||
continue keys;
|
||||
}
|
||||
if (prefixIterator.hasNext())
|
||||
nextPrefix = prefixIterator.next();
|
||||
else
|
||||
nextPrefix = sentinelKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void parseHiddenAttributes(String hide,
|
||||
SortedSet<String> hiddenStrings,
|
||||
SortedSet<String> hiddenPrefixes) {
|
||||
final StringTokenizer tok = new StringTokenizer(hide);
|
||||
while (tok.hasMoreTokens()) {
|
||||
String s = tok.nextToken();
|
||||
if (s.endsWith("*"))
|
||||
hiddenPrefixes.add(s.substring(0, s.length() - 1));
|
||||
else
|
||||
hiddenStrings.add(s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Name of the attribute that specifies the timeout to keep a
|
||||
* server side connection after answering last client request.
|
||||
* The default value is 120000 milliseconds.</p>
|
||||
*/
|
||||
public static final String SERVER_CONNECTION_TIMEOUT =
|
||||
"jmx.remote.x.server.connection.timeout";
|
||||
|
||||
/**
|
||||
* Returns the server side connection timeout.
|
||||
*/
|
||||
public static long getServerConnectionTimeout(Map<String, ?> env) {
|
||||
return getIntegerAttribute(env, SERVER_CONNECTION_TIMEOUT, 120000L,
|
||||
0, Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Name of the attribute that specifies the period in
|
||||
* millisecond for a client to check its connection. The default
|
||||
* value is 60000 milliseconds.</p>
|
||||
*/
|
||||
public static final String CLIENT_CONNECTION_CHECK_PERIOD =
|
||||
"jmx.remote.x.client.connection.check.period";
|
||||
|
||||
/**
|
||||
* Returns the client connection check period.
|
||||
*/
|
||||
public static long getConnectionCheckPeriod(Map<String, ?> env) {
|
||||
return getIntegerAttribute(env, CLIENT_CONNECTION_CHECK_PERIOD, 60000L,
|
||||
0, Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a boolean value from a string value retrieved from a
|
||||
* property in the given map.
|
||||
*
|
||||
* @param stringBoolean the string value that must be converted
|
||||
* into a boolean value.
|
||||
*
|
||||
* @return
|
||||
* <ul>
|
||||
* <li>{@code false} if {@code stringBoolean} is {@code null}</li>
|
||||
* <li>{@code false} if
|
||||
* {@code stringBoolean.equalsIgnoreCase("false")}
|
||||
* is {@code true}</li>
|
||||
* <li>{@code true} if
|
||||
* {@code stringBoolean.equalsIgnoreCase("true")}
|
||||
* is {@code true}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @throws IllegalArgumentException if
|
||||
* {@code ((String)env.get(prop)).equalsIgnoreCase("false")} and
|
||||
* {@code ((String)env.get(prop)).equalsIgnoreCase("true")} are
|
||||
* {@code false}.
|
||||
*/
|
||||
public static boolean computeBooleanFromString(String stringBoolean) {
|
||||
// returns a default value of 'false' if no property is found...
|
||||
return computeBooleanFromString(stringBoolean,false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a boolean value from a string value retrieved from a
|
||||
* property in the given map.
|
||||
*
|
||||
* @param stringBoolean the string value that must be converted
|
||||
* into a boolean value.
|
||||
* @param defaultValue a default value to return in case no property
|
||||
* was defined.
|
||||
*
|
||||
* @return
|
||||
* <ul>
|
||||
* <li>{@code defaultValue} if {@code stringBoolean}
|
||||
* is {@code null}</li>
|
||||
* <li>{@code false} if
|
||||
* {@code stringBoolean.equalsIgnoreCase("false")}
|
||||
* is {@code true}</li>
|
||||
* <li>{@code true} if
|
||||
* {@code stringBoolean.equalsIgnoreCase("true")}
|
||||
* is {@code true}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @throws IllegalArgumentException if
|
||||
* {@code ((String)env.get(prop)).equalsIgnoreCase("false")} and
|
||||
* {@code ((String)env.get(prop)).equalsIgnoreCase("true")} are
|
||||
* {@code false}.
|
||||
*/
|
||||
public static boolean computeBooleanFromString( String stringBoolean, boolean defaultValue) {
|
||||
if (stringBoolean == null)
|
||||
return defaultValue;
|
||||
else if (stringBoolean.equalsIgnoreCase("true"))
|
||||
return true;
|
||||
else if (stringBoolean.equalsIgnoreCase("false"))
|
||||
return false;
|
||||
else
|
||||
throw new IllegalArgumentException(
|
||||
"Property value must be \"true\" or \"false\" instead of \"" +
|
||||
stringBoolean + "\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a map into a valid hash table, i.e.
|
||||
* it removes all the 'null' values from the map.
|
||||
*/
|
||||
public static <K, V> Hashtable<K, V> mapToHashtable(Map<K, V> map) {
|
||||
HashMap<K, V> m = new HashMap<K, V>(map);
|
||||
if (m.containsKey(null)) m.remove(null);
|
||||
for (Iterator<?> i = m.values().iterator(); i.hasNext(); )
|
||||
if (i.next() == null) i.remove();
|
||||
return new Hashtable<K, V>(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Name of the attribute that specifies whether a connector server
|
||||
* should not prevent the VM from exiting
|
||||
*/
|
||||
public static final String JMX_SERVER_DAEMON = "jmx.remote.x.daemon";
|
||||
|
||||
/**
|
||||
* Returns true if {@value SERVER_DAEMON} is specified in the {@code env}
|
||||
* as a key and its value is a String and it is equal to true ignoring case.
|
||||
*
|
||||
* @param env
|
||||
* @return
|
||||
*/
|
||||
public static boolean isServerDaemon(Map<String, ?> env) {
|
||||
return (env != null) &&
|
||||
("true".equalsIgnoreCase((String)env.get(JMX_SERVER_DAEMON)));
|
||||
}
|
||||
|
||||
private static final class SinkOutputStream extends OutputStream {
|
||||
public void write(byte[] b, int off, int len) {}
|
||||
public void write(int b) {}
|
||||
}
|
||||
|
||||
private static final ClassLogger logger =
|
||||
new ClassLogger("javax.management.remote.misc", "EnvHelp");
|
||||
}
|
||||
51
jdkSrc/jdk8/com/sun/jmx/remote/util/OrderClassLoaders.java
Normal file
51
jdkSrc/jdk8/com/sun/jmx/remote/util/OrderClassLoaders.java
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import sun.reflect.misc.ReflectUtil;
|
||||
|
||||
public class OrderClassLoaders extends ClassLoader {
|
||||
public OrderClassLoaders(ClassLoader cl1, ClassLoader cl2) {
|
||||
super(cl1);
|
||||
|
||||
this.cl2 = cl2;
|
||||
}
|
||||
|
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||
ReflectUtil.checkPackageAccess(name);
|
||||
try {
|
||||
return super.loadClass(name, resolve);
|
||||
} catch (ClassNotFoundException cne) {
|
||||
if (cl2 != null) {
|
||||
return cl2.loadClass(name);
|
||||
} else {
|
||||
throw cne;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ClassLoader cl2;
|
||||
}
|
||||
Reference in New Issue
Block a user