5072267: A way to communicate client context such as locale to the JMX server
Support for client contexts and also for localization of descriptions Reviewed-by: dfuchs
This commit is contained in:
parent
cb4eb96188
commit
ab227cb671
@ -69,9 +69,9 @@ public class ServiceName {
|
|||||||
/**
|
/**
|
||||||
* The version of the JMX specification implemented by this product.
|
* The version of the JMX specification implemented by this product.
|
||||||
* <BR>
|
* <BR>
|
||||||
* The value is <CODE>1.4</CODE>.
|
* The value is <CODE>2.0</CODE>.
|
||||||
*/
|
*/
|
||||||
public static final String JMX_SPEC_VERSION = "1.4";
|
public static final String JMX_SPEC_VERSION = "2.0";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The vendor of the JMX specification implemented by this product.
|
* The vendor of the JMX specification implemented by this product.
|
||||||
|
@ -41,7 +41,7 @@ public class EventParams {
|
|||||||
|
|
||||||
@SuppressWarnings("cast") // cast for jdk 1.5
|
@SuppressWarnings("cast") // cast for jdk 1.5
|
||||||
public static long getLeaseTimeout() {
|
public static long getLeaseTimeout() {
|
||||||
long timeout = EventClient.DEFAULT_LEASE_TIMEOUT;
|
long timeout = EventClient.DEFAULT_REQUESTED_LEASE_TIME;
|
||||||
try {
|
try {
|
||||||
final GetPropertyAction act =
|
final GetPropertyAction act =
|
||||||
new GetPropertyAction(DEFAULT_LEASE_TIMEOUT);
|
new GetPropertyAction(DEFAULT_LEASE_TIMEOUT);
|
||||||
|
@ -29,6 +29,7 @@ import com.sun.jmx.remote.util.ClassLogger;
|
|||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.ThreadFactory;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -143,9 +144,10 @@ public class LeaseManager {
|
|||||||
private final Runnable callback;
|
private final Runnable callback;
|
||||||
private ScheduledFuture<?> scheduled; // If null, the lease has expired.
|
private ScheduledFuture<?> scheduled; // If null, the lease has expired.
|
||||||
|
|
||||||
|
private static final ThreadFactory threadFactory =
|
||||||
|
new DaemonThreadFactory("JMX LeaseManager %d");
|
||||||
private final ScheduledExecutorService executor
|
private final ScheduledExecutorService executor
|
||||||
= Executors.newScheduledThreadPool(1,
|
= Executors.newScheduledThreadPool(1, threadFactory);
|
||||||
new DaemonThreadFactory("JMX LeaseManager %d"));
|
|
||||||
|
|
||||||
private static final ClassLogger logger =
|
private static final ClassLogger logger =
|
||||||
new ClassLogger("javax.management.event", "LeaseManager");
|
new ClassLogger("javax.management.event", "LeaseManager");
|
||||||
|
@ -55,9 +55,19 @@ import javax.management.namespace.JMXNamespaces;
|
|||||||
import javax.management.namespace.MBeanServerSupport;
|
import javax.management.namespace.MBeanServerSupport;
|
||||||
import javax.management.remote.IdentityMBeanServerForwarder;
|
import javax.management.remote.IdentityMBeanServerForwarder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>An {@link MBeanServerForwarder} that simulates the existence of a
|
||||||
|
* given MBean. Requests for that MBean, call it X, are intercepted by the
|
||||||
|
* forwarder, and requests for any other MBean are forwarded to the next
|
||||||
|
* forwarder in the chain. Requests such as queryNames which can span both the
|
||||||
|
* X and other MBeans are handled by merging the results for X with the results
|
||||||
|
* from the next forwarder, unless the "visible" parameter is false, in which
|
||||||
|
* case X is invisible to such requests.</p>
|
||||||
|
*/
|
||||||
public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
|
public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
|
||||||
|
|
||||||
private final ObjectName mbeanName;
|
private final ObjectName mbeanName;
|
||||||
|
private final boolean visible;
|
||||||
private DynamicMBean mbean;
|
private DynamicMBean mbean;
|
||||||
|
|
||||||
private MBeanServer mbeanMBS = new MBeanServerSupport() {
|
private MBeanServer mbeanMBS = new MBeanServerSupport() {
|
||||||
@ -85,10 +95,20 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This will only be called if mbeanName has an empty domain.
|
||||||
|
// In that case a getAttribute (e.g.) of that name will have the
|
||||||
|
// domain replaced by MBeanServerSupport with the default domain,
|
||||||
|
// so we must be sure that the default domain is empty too.
|
||||||
|
@Override
|
||||||
|
public String getDefaultDomain() {
|
||||||
|
return mbeanName.getDomain();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public SingleMBeanForwarder(ObjectName mbeanName, DynamicMBean mbean) {
|
public SingleMBeanForwarder(
|
||||||
|
ObjectName mbeanName, DynamicMBean mbean, boolean visible) {
|
||||||
this.mbeanName = mbeanName;
|
this.mbeanName = mbeanName;
|
||||||
|
this.visible = visible;
|
||||||
setSingleMBean(mbean);
|
setSingleMBean(mbean);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,8 +233,10 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getDomains() {
|
public String[] getDomains() {
|
||||||
TreeSet<String> domainSet =
|
String[] domains = super.getDomains();
|
||||||
new TreeSet<String>(Arrays.asList(super.getDomains()));
|
if (!visible)
|
||||||
|
return domains;
|
||||||
|
TreeSet<String> domainSet = new TreeSet<String>(Arrays.asList(domains));
|
||||||
domainSet.add(mbeanName.getDomain());
|
domainSet.add(mbeanName.getDomain());
|
||||||
return domainSet.toArray(new String[domainSet.size()]);
|
return domainSet.toArray(new String[domainSet.size()]);
|
||||||
}
|
}
|
||||||
@ -222,7 +244,7 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
|
|||||||
@Override
|
@Override
|
||||||
public Integer getMBeanCount() {
|
public Integer getMBeanCount() {
|
||||||
Integer count = super.getMBeanCount();
|
Integer count = super.getMBeanCount();
|
||||||
if (!super.isRegistered(mbeanName))
|
if (visible && !super.isRegistered(mbeanName))
|
||||||
count++;
|
count++;
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
@ -284,7 +306,7 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
|
|||||||
*/
|
*/
|
||||||
private boolean applies(ObjectName pattern) {
|
private boolean applies(ObjectName pattern) {
|
||||||
// we know pattern is not null.
|
// we know pattern is not null.
|
||||||
if (!pattern.apply(mbeanName))
|
if (!visible || !pattern.apply(mbeanName))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
final String dompat = pattern.getDomain();
|
final String dompat = pattern.getDomain();
|
||||||
@ -306,10 +328,12 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
|
|||||||
@Override
|
@Override
|
||||||
public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
|
public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
|
||||||
Set<ObjectInstance> names = super.queryMBeans(name, query);
|
Set<ObjectInstance> names = super.queryMBeans(name, query);
|
||||||
if (name == null || applies(name) ) {
|
if (visible) {
|
||||||
// Don't assume mbs.queryNames returns a writable set.
|
if (name == null || applies(name) ) {
|
||||||
names = Util.cloneSet(names);
|
// Don't assume mbs.queryNames returns a writable set.
|
||||||
names.addAll(mbeanMBS.queryMBeans(name, query));
|
names = Util.cloneSet(names);
|
||||||
|
names.addAll(mbeanMBS.queryMBeans(name, query));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
@ -317,10 +341,12 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
|
|||||||
@Override
|
@Override
|
||||||
public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
|
public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
|
||||||
Set<ObjectName> names = super.queryNames(name, query);
|
Set<ObjectName> names = super.queryNames(name, query);
|
||||||
if (name == null || applies(name)) {
|
if (visible) {
|
||||||
// Don't assume mbs.queryNames returns a writable set.
|
if (name == null || applies(name)) {
|
||||||
names = Util.cloneSet(names);
|
// Don't assume mbs.queryNames returns a writable set.
|
||||||
names.addAll(mbeanMBS.queryNames(name, query));
|
names = Util.cloneSet(names);
|
||||||
|
names.addAll(mbeanMBS.queryNames(name, query));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,7 @@ public final class JmxMBeanServer
|
|||||||
* {@link javax.management.MBeanServerFactory#newMBeanServer(java.lang.String)}
|
* {@link javax.management.MBeanServerFactory#newMBeanServer(java.lang.String)}
|
||||||
* instead.
|
* instead.
|
||||||
* <p>
|
* <p>
|
||||||
* By default, {@link MBeanServerInterceptor} are disabled. Use
|
* By default, interceptors are disabled. Use
|
||||||
* {@link #JmxMBeanServer(java.lang.String,javax.management.MBeanServer,javax.management.MBeanServerDelegate,boolean)} to enable them.
|
* {@link #JmxMBeanServer(java.lang.String,javax.management.MBeanServer,javax.management.MBeanServerDelegate,boolean)} to enable them.
|
||||||
* </ul>
|
* </ul>
|
||||||
* @param domain The default domain name used by this MBeanServer.
|
* @param domain The default domain name used by this MBeanServer.
|
||||||
@ -239,7 +239,7 @@ public final class JmxMBeanServer
|
|||||||
this.mBeanServerDelegateObject = delegate;
|
this.mBeanServerDelegateObject = delegate;
|
||||||
this.outerShell = outer;
|
this.outerShell = outer;
|
||||||
|
|
||||||
final Repository repository = new Repository(domain,fairLock);
|
final Repository repository = new Repository(domain);
|
||||||
this.mbsInterceptor =
|
this.mbsInterceptor =
|
||||||
new NamespaceDispatchInterceptor(outer, delegate, instantiator,
|
new NamespaceDispatchInterceptor(outer, delegate, instantiator,
|
||||||
repository);
|
repository);
|
||||||
|
@ -1,354 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
|
|
||||||
* particular file as subject to the "Classpath" exception as provided
|
|
||||||
* by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
|
||||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
|
||||||
* have any questions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.sun.jmx.namespace;
|
|
||||||
|
|
||||||
import com.sun.jmx.defaults.JmxProperties;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.WeakHashMap;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.management.ListenerNotFoundException;
|
|
||||||
import javax.management.MBeanServerConnection;
|
|
||||||
import javax.management.NotificationFilter;
|
|
||||||
import javax.management.NotificationListener;
|
|
||||||
import javax.management.event.EventClient;
|
|
||||||
import javax.management.event.EventClientDelegateMBean;
|
|
||||||
import javax.management.namespace.JMXNamespace;
|
|
||||||
import javax.management.namespace.JMXNamespaces;
|
|
||||||
import javax.management.remote.JMXAddressable;
|
|
||||||
import javax.management.remote.JMXConnector;
|
|
||||||
import javax.management.remote.JMXServiceURL;
|
|
||||||
import javax.security.auth.Subject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A collection of methods that provide JMXConnector wrappers for
|
|
||||||
* JMXRemoteNamepaces underlying connectors.
|
|
||||||
* <p><b>
|
|
||||||
* This API is a Sun internal API and is subject to changes without notice.
|
|
||||||
* </b></p>
|
|
||||||
* @since 1.7
|
|
||||||
*/
|
|
||||||
public final class JMXNamespaceUtils {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A logger for this class.
|
|
||||||
**/
|
|
||||||
private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
|
|
||||||
|
|
||||||
|
|
||||||
private static <K,V> Map<K,V> newWeakHashMap() {
|
|
||||||
return new WeakHashMap<K,V>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** There are no instances of this class */
|
|
||||||
private JMXNamespaceUtils() {
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns un unmodifiable view of a map.
|
|
||||||
public static <K,V> Map<K,V> unmodifiableMap(Map<K,V> aMap) {
|
|
||||||
if (aMap == null || aMap.isEmpty())
|
|
||||||
return Collections.emptyMap();
|
|
||||||
return Collections.unmodifiableMap(aMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A base class that helps writing JMXConnectors that return
|
|
||||||
* MBeanServerConnection wrappers.
|
|
||||||
* This base class wraps an inner JMXConnector (the source), and preserve
|
|
||||||
* its caching policy. If a connection is cached in the source, its wrapper
|
|
||||||
* will be cached in this connector too.
|
|
||||||
* Author's note: rewriting this with java.lang.reflect.Proxy could be
|
|
||||||
* envisaged. It would avoid the combinatory sub-classing introduced by
|
|
||||||
* JMXAddressable.
|
|
||||||
* <p>
|
|
||||||
* Note: all the standard JMXConnector implementations are serializable.
|
|
||||||
* This implementation here is not. Should it be?
|
|
||||||
* I believe it must not be serializable unless it becomes
|
|
||||||
* part of a public API (either standard or officially exposed
|
|
||||||
* and supported in a documented com.sun package)
|
|
||||||
**/
|
|
||||||
static class JMXCachingConnector
|
|
||||||
implements JMXConnector {
|
|
||||||
|
|
||||||
// private static final long serialVersionUID = -2279076110599707875L;
|
|
||||||
|
|
||||||
final JMXConnector source;
|
|
||||||
|
|
||||||
// if this object is made serializable, then the variable below
|
|
||||||
// needs to become volatile transient and be lazyly-created...
|
|
||||||
private final
|
|
||||||
Map<MBeanServerConnection,MBeanServerConnection> connectionMap;
|
|
||||||
|
|
||||||
|
|
||||||
public JMXCachingConnector(JMXConnector source) {
|
|
||||||
this.source = checkNonNull(source, "source");
|
|
||||||
connectionMap = newWeakHashMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
private MBeanServerConnection
|
|
||||||
getCached(MBeanServerConnection inner) {
|
|
||||||
return connectionMap.get(inner);
|
|
||||||
}
|
|
||||||
|
|
||||||
private MBeanServerConnection putCached(final MBeanServerConnection inner,
|
|
||||||
final MBeanServerConnection wrapper) {
|
|
||||||
if (inner == wrapper) return wrapper;
|
|
||||||
synchronized (this) {
|
|
||||||
final MBeanServerConnection concurrent =
|
|
||||||
connectionMap.get(inner);
|
|
||||||
if (concurrent != null) return concurrent;
|
|
||||||
connectionMap.put(inner,wrapper);
|
|
||||||
}
|
|
||||||
return wrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addConnectionNotificationListener(NotificationListener
|
|
||||||
listener, NotificationFilter filter, Object handback) {
|
|
||||||
source.addConnectionNotificationListener(listener,filter,handback);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() throws IOException {
|
|
||||||
source.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void connect() throws IOException {
|
|
||||||
source.connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void connect(Map<String,?> env) throws IOException {
|
|
||||||
source.connect(env);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getConnectionId() throws IOException {
|
|
||||||
return source.getConnectionId();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Preserve caching policy of the underlying connector.
|
|
||||||
**/
|
|
||||||
public MBeanServerConnection
|
|
||||||
getMBeanServerConnection() throws IOException {
|
|
||||||
final MBeanServerConnection inner =
|
|
||||||
source.getMBeanServerConnection();
|
|
||||||
final MBeanServerConnection cached = getCached(inner);
|
|
||||||
if (cached != null) return cached;
|
|
||||||
final MBeanServerConnection wrapper = wrap(inner);
|
|
||||||
return putCached(inner,wrapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MBeanServerConnection
|
|
||||||
getMBeanServerConnection(Subject delegationSubject)
|
|
||||||
throws IOException {
|
|
||||||
final MBeanServerConnection wrapped =
|
|
||||||
source.getMBeanServerConnection(delegationSubject);
|
|
||||||
synchronized (this) {
|
|
||||||
final MBeanServerConnection cached = getCached(wrapped);
|
|
||||||
if (cached != null) return cached;
|
|
||||||
final MBeanServerConnection wrapper =
|
|
||||||
wrapWithSubject(wrapped,delegationSubject);
|
|
||||||
return putCached(wrapped,wrapper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeConnectionNotificationListener(
|
|
||||||
NotificationListener listener)
|
|
||||||
throws ListenerNotFoundException {
|
|
||||||
source.removeConnectionNotificationListener(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeConnectionNotificationListener(
|
|
||||||
NotificationListener l, NotificationFilter f,
|
|
||||||
Object handback) throws ListenerNotFoundException {
|
|
||||||
source.removeConnectionNotificationListener(l,f,handback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the method that subclass will redefine. This method
|
|
||||||
* is called by {@code this.getMBeanServerConnection()}.
|
|
||||||
* {@code inner} is the connection returned by
|
|
||||||
* {@code source.getMBeanServerConnection()}.
|
|
||||||
**/
|
|
||||||
protected MBeanServerConnection wrap(MBeanServerConnection inner)
|
|
||||||
throws IOException {
|
|
||||||
return inner;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subclass may also want to redefine this method.
|
|
||||||
* By default it calls wrap(inner). This method
|
|
||||||
* is called by {@code this.getMBeanServerConnection(Subject)}.
|
|
||||||
* {@code inner} is the connection returned by
|
|
||||||
* {@code source.getMBeanServerConnection(Subject)}.
|
|
||||||
**/
|
|
||||||
protected MBeanServerConnection wrapWithSubject(
|
|
||||||
MBeanServerConnection inner, Subject delegationSubject)
|
|
||||||
throws IOException {
|
|
||||||
return wrap(inner);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
if (source instanceof JMXAddressable) {
|
|
||||||
final JMXServiceURL address =
|
|
||||||
((JMXAddressable)source).getAddress();
|
|
||||||
if (address != null)
|
|
||||||
return address.toString();
|
|
||||||
}
|
|
||||||
return source.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name space connector can do 'cd'
|
|
||||||
**/
|
|
||||||
static class JMXNamespaceConnector extends JMXCachingConnector {
|
|
||||||
|
|
||||||
// private static final long serialVersionUID = -4813611540843020867L;
|
|
||||||
|
|
||||||
private final String toDir;
|
|
||||||
private final boolean closeable;
|
|
||||||
|
|
||||||
public JMXNamespaceConnector(JMXConnector source, String toDir,
|
|
||||||
boolean closeable) {
|
|
||||||
super(source);
|
|
||||||
this.toDir = toDir;
|
|
||||||
this.closeable = closeable;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
if (!closeable)
|
|
||||||
throw new UnsupportedOperationException("close");
|
|
||||||
else super.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected MBeanServerConnection wrap(MBeanServerConnection wrapped)
|
|
||||||
throws IOException {
|
|
||||||
if (LOG.isLoggable(Level.FINER))
|
|
||||||
LOG.finer("Creating name space proxy connection for source: "+
|
|
||||||
"namespace="+toDir);
|
|
||||||
return JMXNamespaces.narrowToNamespace(wrapped,toDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "JMXNamespaces.narrowToNamespace("+
|
|
||||||
super.toString()+
|
|
||||||
", \""+toDir+"\")";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static class JMXEventConnector extends JMXCachingConnector {
|
|
||||||
|
|
||||||
// private static final long serialVersionUID = 4742659236340242785L;
|
|
||||||
|
|
||||||
JMXEventConnector(JMXConnector wrapped) {
|
|
||||||
super(wrapped);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected MBeanServerConnection wrap(MBeanServerConnection inner)
|
|
||||||
throws IOException {
|
|
||||||
return EventClient.getEventClientConnection(inner);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "EventClient.withEventClient("+super.toString()+")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class JMXAddressableEventConnector extends JMXEventConnector
|
|
||||||
implements JMXAddressable {
|
|
||||||
|
|
||||||
// private static final long serialVersionUID = -9128520234812124712L;
|
|
||||||
|
|
||||||
JMXAddressableEventConnector(JMXConnector wrapped) {
|
|
||||||
super(wrapped);
|
|
||||||
}
|
|
||||||
|
|
||||||
public JMXServiceURL getAddress() {
|
|
||||||
return ((JMXAddressable)source).getAddress();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a connector whose MBeamServerConnection will point to the
|
|
||||||
* given sub name space inside the source connector.
|
|
||||||
* @see JMXNamespace
|
|
||||||
**/
|
|
||||||
public static JMXConnector cd(final JMXConnector source,
|
|
||||||
final String toNamespace,
|
|
||||||
final boolean closeable)
|
|
||||||
throws IOException {
|
|
||||||
|
|
||||||
checkNonNull(source, "JMXConnector");
|
|
||||||
|
|
||||||
if (toNamespace == null || toNamespace.equals(""))
|
|
||||||
return source;
|
|
||||||
|
|
||||||
return new JMXNamespaceConnector(source,toNamespace,closeable);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a JMX Connector that will use an {@link EventClient}
|
|
||||||
* to subscribe for notifications. If the server doesn't have
|
|
||||||
* an {@link EventClientDelegateMBean}, then the connector will
|
|
||||||
* use the legacy notification mechanism instead.
|
|
||||||
*
|
|
||||||
* @param source The underlying JMX Connector wrapped by the returned
|
|
||||||
* connector.
|
|
||||||
* @return A JMX Connector that will uses an {@link EventClient}, if
|
|
||||||
* available.
|
|
||||||
* @see EventClient#getEventClientConnection(MBeanServerConnection)
|
|
||||||
*/
|
|
||||||
public static JMXConnector withEventClient(final JMXConnector source) {
|
|
||||||
checkNonNull(source, "JMXConnector");
|
|
||||||
if (source instanceof JMXAddressable)
|
|
||||||
return new JMXAddressableEventConnector(source);
|
|
||||||
else
|
|
||||||
return new JMXEventConnector(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> T checkNonNull(T parameter, String name) {
|
|
||||||
if (parameter == null)
|
|
||||||
throw new IllegalArgumentException(name+" must not be null");
|
|
||||||
return parameter;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -49,11 +49,6 @@ public class ObjectNameRouter {
|
|||||||
final int tlen;
|
final int tlen;
|
||||||
final boolean identity;
|
final boolean identity;
|
||||||
|
|
||||||
|
|
||||||
public ObjectNameRouter(String targetDirName) {
|
|
||||||
this(targetDirName,null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Creates a new instance of ObjectNameRouter */
|
/** Creates a new instance of ObjectNameRouter */
|
||||||
public ObjectNameRouter(final String remove, final String add) {
|
public ObjectNameRouter(final String remove, final String add) {
|
||||||
this.targetPrefix = (remove==null?"":remove);
|
this.targetPrefix = (remove==null?"":remove);
|
||||||
@ -186,6 +181,4 @@ public class ObjectNameRouter {
|
|||||||
b.append(NAMESPACE_SEPARATOR);
|
b.append(NAMESPACE_SEPARATOR);
|
||||||
return b.toString();
|
return b.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,6 @@ import java.util.logging.Level;
|
|||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.management.MBeanServerConnection;
|
import javax.management.MBeanServerConnection;
|
||||||
import javax.management.namespace.JMXNamespaces;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,22 +56,14 @@ public class RoutingConnectionProxy
|
|||||||
private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
|
private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance of RoutingConnectionProxy
|
|
||||||
*/
|
|
||||||
public RoutingConnectionProxy(MBeanServerConnection source,
|
|
||||||
String sourceDir) {
|
|
||||||
this(source,sourceDir,"",false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of RoutingConnectionProxy
|
* Creates a new instance of RoutingConnectionProxy
|
||||||
*/
|
*/
|
||||||
public RoutingConnectionProxy(MBeanServerConnection source,
|
public RoutingConnectionProxy(MBeanServerConnection source,
|
||||||
String sourceDir,
|
String sourceDir,
|
||||||
String targetDir,
|
String targetDir,
|
||||||
boolean forwardsContext) {
|
boolean probe) {
|
||||||
super(source,sourceDir,targetDir,forwardsContext);
|
super(source, sourceDir, targetDir, probe);
|
||||||
|
|
||||||
if (LOG.isLoggable(Level.FINER))
|
if (LOG.isLoggable(Level.FINER))
|
||||||
LOG.finer("RoutingConnectionProxy for " + getSourceNamespace() +
|
LOG.finer("RoutingConnectionProxy for " + getSourceNamespace() +
|
||||||
@ -85,15 +76,13 @@ public class RoutingConnectionProxy
|
|||||||
final String sourceNs = getSourceNamespace();
|
final String sourceNs = getSourceNamespace();
|
||||||
String wrapped = String.valueOf(source());
|
String wrapped = String.valueOf(source());
|
||||||
if ("".equals(targetNs)) {
|
if ("".equals(targetNs)) {
|
||||||
if (forwardsContext)
|
|
||||||
wrapped = "ClientContext.withDynamicContext("+wrapped+")";
|
|
||||||
return "JMXNamespaces.narrowToNamespace("+
|
return "JMXNamespaces.narrowToNamespace("+
|
||||||
wrapped+", \""+
|
wrapped+", \""+
|
||||||
sourceNs+"\")";
|
sourceNs+"\")";
|
||||||
}
|
}
|
||||||
return this.getClass().getSimpleName()+"("+wrapped+", \""+
|
return this.getClass().getSimpleName()+"("+wrapped+", \""+
|
||||||
sourceNs+"\", \""+
|
sourceNs+"\", \""+
|
||||||
targetNs+"\", "+forwardsContext+")";
|
targetNs+"\")";
|
||||||
}
|
}
|
||||||
|
|
||||||
static final RoutingProxyFactory
|
static final RoutingProxyFactory
|
||||||
@ -102,22 +91,16 @@ public class RoutingConnectionProxy
|
|||||||
<MBeanServerConnection,RoutingConnectionProxy>() {
|
<MBeanServerConnection,RoutingConnectionProxy>() {
|
||||||
|
|
||||||
public RoutingConnectionProxy newInstance(MBeanServerConnection source,
|
public RoutingConnectionProxy newInstance(MBeanServerConnection source,
|
||||||
String sourcePath, String targetPath,
|
String sourcePath, String targetPath, boolean probe) {
|
||||||
boolean forwardsContext) {
|
|
||||||
return new RoutingConnectionProxy(source,sourcePath,
|
return new RoutingConnectionProxy(source,sourcePath,
|
||||||
targetPath,forwardsContext);
|
targetPath, probe);
|
||||||
}
|
|
||||||
|
|
||||||
public RoutingConnectionProxy newInstance(
|
|
||||||
MBeanServerConnection source, String sourcePath) {
|
|
||||||
return new RoutingConnectionProxy(source,sourcePath);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public static MBeanServerConnection cd(MBeanServerConnection source,
|
public static MBeanServerConnection cd(
|
||||||
String sourcePath) {
|
MBeanServerConnection source, String sourcePath, boolean probe) {
|
||||||
return RoutingProxy.cd(RoutingConnectionProxy.class, FACTORY,
|
return RoutingProxy.cd(RoutingConnectionProxy.class, FACTORY,
|
||||||
source, sourcePath);
|
source, sourcePath, probe);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ import java.io.IOException;
|
|||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.management.InstanceNotFoundException;
|
||||||
import javax.management.MBeanException;
|
import javax.management.MBeanException;
|
||||||
import javax.management.MBeanRegistrationException;
|
import javax.management.MBeanRegistrationException;
|
||||||
|
|
||||||
@ -90,17 +91,9 @@ import javax.management.namespace.JMXNamespaces;
|
|||||||
// targetNs=<encoded-context> // context must be removed from object name
|
// targetNs=<encoded-context> // context must be removed from object name
|
||||||
// sourceNs="" // nothing to add...
|
// sourceNs="" // nothing to add...
|
||||||
//
|
//
|
||||||
// RoutingProxies can also be used on the client side to implement
|
|
||||||
// "withClientContext" operations. In that case, the boolean parameter
|
|
||||||
// 'forwards context' is set to true, targetNs is "", and sourceNS may
|
|
||||||
// also be "". When forwardsContext is true, the RoutingProxy dynamically
|
|
||||||
// creates an ObjectNameRouter for each operation - in order to dynamically add
|
|
||||||
// the context attached to the thread to the routing ObjectName. This is
|
|
||||||
// performed in the getObjectNameRouter() method.
|
|
||||||
//
|
|
||||||
// Finally, in order to avoid too many layers of wrapping,
|
// Finally, in order to avoid too many layers of wrapping,
|
||||||
// RoutingConnectionProxy and RoutingServerProxy can be created through a
|
// RoutingConnectionProxy and RoutingServerProxy can be created through a
|
||||||
// factory method that can concatenate namespace pathes in order to
|
// factory method that can concatenate namespace paths in order to
|
||||||
// return a single RoutingProxy - rather than wrapping a RoutingProxy inside
|
// return a single RoutingProxy - rather than wrapping a RoutingProxy inside
|
||||||
// another RoutingProxy. See RoutingConnectionProxy.cd and
|
// another RoutingProxy. See RoutingConnectionProxy.cd and
|
||||||
// RoutingServerProxy.cd
|
// RoutingServerProxy.cd
|
||||||
@ -146,25 +139,27 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
|
|||||||
private final T source;
|
private final T source;
|
||||||
|
|
||||||
// The name space we're narrowing to (usually some name space in
|
// The name space we're narrowing to (usually some name space in
|
||||||
// the source MBeanServerConnection
|
// the source MBeanServerConnection), e.g. "a" for the namespace
|
||||||
|
// "a//". This is empty in the case of ClientContext described above.
|
||||||
private final String sourceNs;
|
private final String sourceNs;
|
||||||
|
|
||||||
// The name space we pretend to be mounted in (usually "")
|
// The name space we pretend to be mounted in. This is empty except
|
||||||
|
// in the case of ClientContext described above (where it will be
|
||||||
|
// something like "jmx.context//foo=bar".
|
||||||
private final String targetNs;
|
private final String targetNs;
|
||||||
|
|
||||||
// The name of the JMXNamespace that handles the source name space
|
// The name of the JMXNamespace that handles the source name space
|
||||||
private final ObjectName handlerName;
|
private final ObjectName handlerName;
|
||||||
private final ObjectNameRouter router;
|
private final ObjectNameRouter router;
|
||||||
final boolean forwardsContext;
|
|
||||||
private volatile String defaultDomain = null;
|
private volatile String defaultDomain = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of RoutingProxy
|
* Creates a new instance of RoutingProxy
|
||||||
*/
|
*/
|
||||||
protected RoutingProxy(T source,
|
protected RoutingProxy(T source,
|
||||||
String sourceNs,
|
String sourceNs,
|
||||||
String targetNs,
|
String targetNs,
|
||||||
boolean forwardsContext) {
|
boolean probe) {
|
||||||
if (source == null) throw new IllegalArgumentException("null");
|
if (source == null) throw new IllegalArgumentException("null");
|
||||||
this.sourceNs = JMXNamespaces.normalizeNamespaceName(sourceNs);
|
this.sourceNs = JMXNamespaces.normalizeNamespaceName(sourceNs);
|
||||||
|
|
||||||
@ -177,13 +172,17 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
|
|||||||
// System.err.println("sourceNs: "+sourceNs);
|
// System.err.println("sourceNs: "+sourceNs);
|
||||||
this.handlerName =
|
this.handlerName =
|
||||||
JMXNamespaces.getNamespaceObjectName(this.sourceNs);
|
JMXNamespaces.getNamespaceObjectName(this.sourceNs);
|
||||||
try {
|
if (probe) {
|
||||||
// System.err.println("handlerName: "+handlerName);
|
try {
|
||||||
if (!source.isRegistered(handlerName))
|
if (!source.isRegistered(handlerName)) {
|
||||||
throw new IllegalArgumentException(sourceNs +
|
InstanceNotFoundException infe =
|
||||||
": no such name space");
|
new InstanceNotFoundException(handlerName);
|
||||||
} catch (IOException x) {
|
throw new IllegalArgumentException(sourceNs +
|
||||||
throw new IllegalArgumentException("source stale: "+x,x);
|
": no such name space", infe);
|
||||||
|
}
|
||||||
|
} catch (IOException x) {
|
||||||
|
throw new IllegalArgumentException("source stale: "+x,x);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.source = source;
|
this.source = source;
|
||||||
@ -191,7 +190,6 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
|
|||||||
JMXNamespaces.normalizeNamespaceName(targetNs));
|
JMXNamespaces.normalizeNamespaceName(targetNs));
|
||||||
this.router =
|
this.router =
|
||||||
new ObjectNameRouter(this.targetNs,this.sourceNs);
|
new ObjectNameRouter(this.targetNs,this.sourceNs);
|
||||||
this.forwardsContext = forwardsContext;
|
|
||||||
|
|
||||||
if (LOG.isLoggable(Level.FINER))
|
if (LOG.isLoggable(Level.FINER))
|
||||||
LOG.finer("RoutingProxy for " + this.sourceNs + " created");
|
LOG.finer("RoutingProxy for " + this.sourceNs + " created");
|
||||||
@ -200,14 +198,6 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
|
|||||||
@Override
|
@Override
|
||||||
public T source() { return source; }
|
public T source() { return source; }
|
||||||
|
|
||||||
ObjectNameRouter getObjectNameRouter() {
|
|
||||||
// TODO: uncomment this when contexts are added
|
|
||||||
// if (forwardsContext)
|
|
||||||
// return ObjectNameRouter.wrapWithContext(router);
|
|
||||||
// else
|
|
||||||
return router;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ObjectName toSource(ObjectName targetName)
|
public ObjectName toSource(ObjectName targetName)
|
||||||
throws MalformedObjectNameException {
|
throws MalformedObjectNameException {
|
||||||
@ -222,8 +212,7 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
|
|||||||
if (defaultDomain != null)
|
if (defaultDomain != null)
|
||||||
targetName = targetName.withDomain(defaultDomain);
|
targetName = targetName.withDomain(defaultDomain);
|
||||||
}
|
}
|
||||||
final ObjectNameRouter r = getObjectNameRouter();
|
return router.toSourceContext(targetName,true);
|
||||||
return r.toSourceContext(targetName,true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -243,8 +232,7 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
|
|||||||
public ObjectName toTarget(ObjectName sourceName)
|
public ObjectName toTarget(ObjectName sourceName)
|
||||||
throws MalformedObjectNameException {
|
throws MalformedObjectNameException {
|
||||||
if (sourceName == null) return null;
|
if (sourceName == null) return null;
|
||||||
final ObjectNameRouter r = getObjectNameRouter();
|
return router.toTargetContext(sourceName,false);
|
||||||
return r.toTargetContext(sourceName,false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object getAttributeFromHandler(String attributeName)
|
private Object getAttributeFromHandler(String attributeName)
|
||||||
@ -357,11 +345,8 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
|
|||||||
// instance.
|
// instance.
|
||||||
static interface RoutingProxyFactory<T extends MBeanServerConnection,
|
static interface RoutingProxyFactory<T extends MBeanServerConnection,
|
||||||
R extends RoutingProxy<T>> {
|
R extends RoutingProxy<T>> {
|
||||||
R newInstance(T source,
|
public R newInstance(
|
||||||
String sourcePath, String targetPath,
|
T source, String sourcePath, String targetPath, boolean probe);
|
||||||
boolean forwardsContext);
|
|
||||||
R newInstance(T source,
|
|
||||||
String sourcePath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Performs a narrowDownToNamespace operation.
|
// Performs a narrowDownToNamespace operation.
|
||||||
@ -377,7 +362,7 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
|
|||||||
static <T extends MBeanServerConnection, R extends RoutingProxy<T>>
|
static <T extends MBeanServerConnection, R extends RoutingProxy<T>>
|
||||||
R cd(Class<R> routingProxyClass,
|
R cd(Class<R> routingProxyClass,
|
||||||
RoutingProxyFactory<T,R> factory,
|
RoutingProxyFactory<T,R> factory,
|
||||||
T source, String sourcePath) {
|
T source, String sourcePath, boolean probe) {
|
||||||
if (source == null) throw new IllegalArgumentException("null");
|
if (source == null) throw new IllegalArgumentException("null");
|
||||||
if (source.getClass().equals(routingProxyClass)) {
|
if (source.getClass().equals(routingProxyClass)) {
|
||||||
// cast is OK here, but findbugs complains unless we use class.cast
|
// cast is OK here, but findbugs complains unless we use class.cast
|
||||||
@ -400,14 +385,13 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
|
|||||||
final String path =
|
final String path =
|
||||||
JMXNamespaces.concat(other.getSourceNamespace(),
|
JMXNamespaces.concat(other.getSourceNamespace(),
|
||||||
sourcePath);
|
sourcePath);
|
||||||
return factory.newInstance(other.source(),path,"",
|
return factory.newInstance(other.source(), path, "", probe);
|
||||||
other.forwardsContext);
|
|
||||||
}
|
}
|
||||||
// Note: we could do possibly something here - but it would involve
|
// Note: we could do possibly something here - but it would involve
|
||||||
// removing part of targetDir, and possibly adding
|
// removing part of targetDir, and possibly adding
|
||||||
// something to sourcePath.
|
// something to sourcePath.
|
||||||
// Too complex to bother! => simply default to stacking...
|
// Too complex to bother! => simply default to stacking...
|
||||||
}
|
}
|
||||||
return factory.newInstance(source,sourcePath);
|
return factory.newInstance(source, sourcePath, "", probe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,6 @@ import javax.management.OperationsException;
|
|||||||
import javax.management.QueryExp;
|
import javax.management.QueryExp;
|
||||||
import javax.management.ReflectionException;
|
import javax.management.ReflectionException;
|
||||||
import javax.management.loading.ClassLoaderRepository;
|
import javax.management.loading.ClassLoaderRepository;
|
||||||
import javax.management.namespace.JMXNamespaces;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A RoutingServerProxy is an MBeanServer proxy that proxies a
|
* A RoutingServerProxy is an MBeanServer proxy that proxies a
|
||||||
@ -76,19 +75,11 @@ public class RoutingServerProxy
|
|||||||
extends RoutingProxy<MBeanServer>
|
extends RoutingProxy<MBeanServer>
|
||||||
implements MBeanServer {
|
implements MBeanServer {
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance of RoutingServerProxy
|
|
||||||
*/
|
|
||||||
public RoutingServerProxy(MBeanServer source,
|
|
||||||
String sourceNs) {
|
|
||||||
this(source,sourceNs,"",false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RoutingServerProxy(MBeanServer source,
|
public RoutingServerProxy(MBeanServer source,
|
||||||
String sourceNs,
|
String sourceNs,
|
||||||
String targetNs,
|
String targetNs,
|
||||||
boolean forwardsContext) {
|
boolean probe) {
|
||||||
super(source,sourceNs,targetNs,forwardsContext);
|
super(source, sourceNs, targetNs, probe);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -571,20 +562,15 @@ public class RoutingServerProxy
|
|||||||
FACTORY = new RoutingProxyFactory<MBeanServer,RoutingServerProxy>() {
|
FACTORY = new RoutingProxyFactory<MBeanServer,RoutingServerProxy>() {
|
||||||
|
|
||||||
public RoutingServerProxy newInstance(MBeanServer source,
|
public RoutingServerProxy newInstance(MBeanServer source,
|
||||||
String sourcePath, String targetPath,
|
String sourcePath, String targetPath, boolean probe) {
|
||||||
boolean forwardsContext) {
|
return new RoutingServerProxy(
|
||||||
return new RoutingServerProxy(source,sourcePath,
|
source, sourcePath, targetPath, probe);
|
||||||
targetPath,forwardsContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RoutingServerProxy newInstance(
|
|
||||||
MBeanServer source, String sourcePath) {
|
|
||||||
return new RoutingServerProxy(source,sourcePath);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public static MBeanServer cd(MBeanServer source, String sourcePath) {
|
public static MBeanServer cd(
|
||||||
|
MBeanServer source, String sourcePath, boolean probe) {
|
||||||
return RoutingProxy.cd(RoutingServerProxy.class, FACTORY,
|
return RoutingProxy.cd(RoutingServerProxy.class, FACTORY,
|
||||||
source, sourcePath);
|
source, sourcePath, probe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -430,13 +430,11 @@ public class EventClientConnection implements InvocationHandler,
|
|||||||
* The {@code EventClient} is created lazily, when it is needed
|
* The {@code EventClient} is created lazily, when it is needed
|
||||||
* for the first time. If null, a default factory will be used
|
* for the first time. If null, a default factory will be used
|
||||||
* (see {@link #createEventClient}).
|
* (see {@link #createEventClient}).
|
||||||
* @return the
|
* @return the MBeanServerConnection.
|
||||||
**/
|
**/
|
||||||
public static MBeanServerConnection getEventConnectionFor(
|
public static MBeanServerConnection getEventConnectionFor(
|
||||||
MBeanServerConnection connection,
|
MBeanServerConnection connection,
|
||||||
Callable<EventClient> eventClientFactory) {
|
Callable<EventClient> eventClientFactory) {
|
||||||
// if c already uses an EventClient no need to create a new one.
|
|
||||||
//
|
|
||||||
if (connection instanceof EventClientFactory
|
if (connection instanceof EventClientFactory
|
||||||
&& eventClientFactory != null)
|
&& eventClientFactory != null)
|
||||||
throw new IllegalArgumentException("connection already uses EventClient");
|
throw new IllegalArgumentException("connection already uses EventClient");
|
||||||
|
1091
jdk/src/share/classes/javax/management/ClientContext.java
Normal file
1091
jdk/src/share/classes/javax/management/ClientContext.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -35,8 +35,8 @@ import java.io.Serializable;
|
|||||||
// Javadoc imports:
|
// Javadoc imports:
|
||||||
import java.lang.management.MemoryUsage;
|
import java.lang.management.MemoryUsage;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
import javax.management.openmbean.CompositeData;
|
import javax.management.openmbean.CompositeData;
|
||||||
import javax.management.openmbean.MXBeanMappingFactory;
|
import javax.management.openmbean.MXBeanMappingFactory;
|
||||||
import javax.management.openmbean.OpenMBeanAttributeInfoSupport;
|
import javax.management.openmbean.OpenMBeanAttributeInfoSupport;
|
||||||
@ -118,19 +118,22 @@ import javax.management.openmbean.OpenType;
|
|||||||
* deprecation, for example {@code "1.3 Replaced by the Capacity
|
* deprecation, for example {@code "1.3 Replaced by the Capacity
|
||||||
* attribute"}.</td>
|
* attribute"}.</td>
|
||||||
*
|
*
|
||||||
* <tr id="descriptionResourceBundleBaseName">
|
* <tr><td id="descriptionResourceBundleBaseName"><i>descriptionResource<br>
|
||||||
* <td>descriptionResource<br>BundleBaseName</td><td>String</td><td>Any</td>
|
* BundleBaseName</i></td><td>String</td><td>Any</td>
|
||||||
*
|
*
|
||||||
* <td>The base name for the {@link ResourceBundle} in which the key given in
|
* <td>The base name for the {@link ResourceBundle} in which the key given in
|
||||||
* the {@code descriptionResourceKey} field can be found, for example
|
* the {@code descriptionResourceKey} field can be found, for example
|
||||||
* {@code "com.example.myapp.MBeanResources"}.</td>
|
* {@code "com.example.myapp.MBeanResources"}. See
|
||||||
|
* {@link MBeanInfo#localizeDescriptions MBeanInfo.localizeDescriptions}.</td>
|
||||||
*
|
*
|
||||||
* <tr id="descriptionResourceKey">
|
* <tr><td id="descriptionResourceKey"><i>descriptionResourceKey</i></td>
|
||||||
* <td>descriptionResourceKey</td><td>String</td><td>Any</td>
|
* <td>String</td><td>Any</td>
|
||||||
*
|
*
|
||||||
* <td>A resource key for the description of this element. In
|
* <td>A resource key for the description of this element. In
|
||||||
* conjunction with the {@code descriptionResourceBundleBaseName},
|
* conjunction with the {@code descriptionResourceBundleBaseName},
|
||||||
* this can be used to find a localized version of the description.</td>
|
* this can be used to find a localized version of the description.
|
||||||
|
* See {@link MBeanInfo#localizeDescriptions MBeanInfo.localizeDescriptions}.
|
||||||
|
* </td>
|
||||||
*
|
*
|
||||||
* <tr><td>enabled</td><td>String</td>
|
* <tr><td>enabled</td><td>String</td>
|
||||||
* <td>MBeanAttributeInfo<br>MBeanNotificationInfo<br>MBeanOperationInfo</td>
|
* <td>MBeanAttributeInfo<br>MBeanNotificationInfo<br>MBeanOperationInfo</td>
|
||||||
@ -157,11 +160,11 @@ import javax.management.openmbean.OpenType;
|
|||||||
* href="MBeanInfo.html#info-changed">{@code "jmx.mbean.info.changed"}</a>
|
* href="MBeanInfo.html#info-changed">{@code "jmx.mbean.info.changed"}</a>
|
||||||
* notification.</td>
|
* notification.</td>
|
||||||
*
|
*
|
||||||
* <tr><td>infoTimeout</td><td>String<br>Long</td><td>MBeanInfo</td>
|
* <tr id="infoTimeout"><td>infoTimeout</td><td>String<br>Long</td><td>MBeanInfo</td>
|
||||||
*
|
*
|
||||||
* <td id="infoTimeout">The time in milli-seconds that the MBeanInfo can
|
* <td>The time in milli-seconds that the MBeanInfo can reasonably be
|
||||||
* reasonably be expected to be unchanged. The value can be a {@code Long}
|
* expected to be unchanged. The value can be a {@code Long} or a
|
||||||
* or a decimal string. This provides a hint from a DynamicMBean or any
|
* decimal string. This provides a hint from a DynamicMBean or any
|
||||||
* MBean that does not define {@code immutableInfo} as {@code true}
|
* MBean that does not define {@code immutableInfo} as {@code true}
|
||||||
* that the MBeanInfo is not likely to change within this period and
|
* that the MBeanInfo is not likely to change within this period and
|
||||||
* therefore can be cached. When this field is missing or has the
|
* therefore can be cached. When this field is missing or has the
|
||||||
@ -185,6 +188,13 @@ import javax.management.openmbean.OpenType;
|
|||||||
* <td>Legal values for an attribute or parameter. See
|
* <td>Legal values for an attribute or parameter. See
|
||||||
* {@link javax.management.openmbean}.</td>
|
* {@link javax.management.openmbean}.</td>
|
||||||
*
|
*
|
||||||
|
* <tr id="locale"><td><i>locale</i></td>
|
||||||
|
* <td>String</td><td>Any</td>
|
||||||
|
*
|
||||||
|
* <td>The {@linkplain Locale locale} of the description in this
|
||||||
|
* {@code MBeanInfo}, {@code MBeanAttributeInfo}, etc, as returned
|
||||||
|
* by {@link Locale#toString()}.</td>
|
||||||
|
*
|
||||||
* <tr id="maxValue"><td><i>maxValue</i><td>Object</td>
|
* <tr id="maxValue"><td><i>maxValue</i><td>Object</td>
|
||||||
* <td>MBeanAttributeInfo<br>MBeanParameterInfo</td>
|
* <td>MBeanAttributeInfo<br>MBeanParameterInfo</td>
|
||||||
*
|
*
|
||||||
|
@ -30,6 +30,7 @@ import com.sun.jmx.mbeanserver.MBeanInjector;
|
|||||||
import com.sun.jmx.remote.util.ClassLogger;
|
import com.sun.jmx.remote.util.ClassLogger;
|
||||||
import java.beans.BeanInfo;
|
import java.beans.BeanInfo;
|
||||||
import java.beans.PropertyDescriptor;
|
import java.beans.PropertyDescriptor;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.lang.reflect.InvocationHandler;
|
import java.lang.reflect.InvocationHandler;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
@ -37,6 +38,7 @@ import java.lang.reflect.Method;
|
|||||||
import java.lang.reflect.Proxy;
|
import java.lang.reflect.Proxy;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
import javax.management.namespace.JMXNamespaces;
|
||||||
import javax.management.openmbean.MXBeanMappingFactory;
|
import javax.management.openmbean.MXBeanMappingFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -60,6 +62,21 @@ public class JMX {
|
|||||||
*/
|
*/
|
||||||
public static final String DEFAULT_VALUE_FIELD = "defaultValue";
|
public static final String DEFAULT_VALUE_FIELD = "defaultValue";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the <a
|
||||||
|
* href="Descriptor.html#descriptionResourceBundleBaseName">{@code
|
||||||
|
* descriptionResourceBundleBaseName}</a> field.
|
||||||
|
*/
|
||||||
|
public static final String DESCRIPTION_RESOURCE_BUNDLE_BASE_NAME_FIELD =
|
||||||
|
"descriptionResourceBundleBaseName";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the <a href="Descriptor.html#descriptionResourceKey">{@code
|
||||||
|
* descriptionResourceKey}</a> field.
|
||||||
|
*/
|
||||||
|
public static final String DESCRIPTION_RESOURCE_KEY_FIELD =
|
||||||
|
"descriptionResourceKey";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of the <a href="Descriptor.html#immutableInfo">{@code
|
* The name of the <a href="Descriptor.html#immutableInfo">{@code
|
||||||
* immutableInfo}</a> field.
|
* immutableInfo}</a> field.
|
||||||
@ -78,6 +95,12 @@ public class JMX {
|
|||||||
*/
|
*/
|
||||||
public static final String LEGAL_VALUES_FIELD = "legalValues";
|
public static final String LEGAL_VALUES_FIELD = "legalValues";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the <a href="Descriptor.html#locale">{@code locale}</a>
|
||||||
|
* field.
|
||||||
|
*/
|
||||||
|
public static final String LOCALE_FIELD = "locale";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of the <a href="Descriptor.html#maxValue">{@code
|
* The name of the <a href="Descriptor.html#maxValue">{@code
|
||||||
* maxValue}</a> field.
|
* maxValue}</a> field.
|
||||||
@ -120,13 +143,12 @@ public class JMX {
|
|||||||
* <p>Options to apply to an MBean proxy or to an instance of {@link
|
* <p>Options to apply to an MBean proxy or to an instance of {@link
|
||||||
* StandardMBean}.</p>
|
* StandardMBean}.</p>
|
||||||
*
|
*
|
||||||
* <p>For example, to specify a custom {@link MXBeanMappingFactory}
|
* <p>For example, to specify the "wrapped object visible" option for a
|
||||||
* for a {@code StandardMBean}, you might write this:</p>
|
* {@code StandardMBean}, you might write this:</p>
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* MXBeanMappingFactory factory = new MyMXBeanMappingFactory();
|
* StandardMBean.Options opts = new StandardMBean.Options();
|
||||||
* JMX.MBeanOptions opts = new JMX.MBeanOptions();
|
* opts.setWrappedObjectVisible(true);
|
||||||
* opts.setMXBeanMappingFactory(factory);
|
|
||||||
* StandardMBean mbean = new StandardMBean(impl, intf, opts);
|
* StandardMBean mbean = new StandardMBean(impl, intf, opts);
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
package javax.management;
|
package javax.management;
|
||||||
|
|
||||||
|
import com.sun.jmx.mbeanserver.Util;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.StreamCorruptedException;
|
import java.io.StreamCorruptedException;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
@ -37,6 +38,12 @@ import java.util.WeakHashMap;
|
|||||||
import java.security.AccessController;
|
import java.security.AccessController;
|
||||||
import java.security.PrivilegedAction;
|
import java.security.PrivilegedAction;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.MissingResourceException;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
import static javax.management.ImmutableDescriptor.nonNullDescriptor;
|
import static javax.management.ImmutableDescriptor.nonNullDescriptor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -290,6 +297,7 @@ public class MBeanInfo implements Cloneable, Serializable, DescriptorRead {
|
|||||||
* <p>Since this class is immutable, the clone method is chiefly of
|
* <p>Since this class is immutable, the clone method is chiefly of
|
||||||
* interest to subclasses.</p>
|
* interest to subclasses.</p>
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public Object clone () {
|
public Object clone () {
|
||||||
try {
|
try {
|
||||||
return super.clone() ;
|
return super.clone() ;
|
||||||
@ -474,6 +482,7 @@ public class MBeanInfo implements Cloneable, Serializable, DescriptorRead {
|
|||||||
return (Descriptor) nonNullDescriptor(descriptor).clone();
|
return (Descriptor) nonNullDescriptor(descriptor).clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return
|
return
|
||||||
getClass().getName() + "[" +
|
getClass().getName() + "[" +
|
||||||
@ -505,6 +514,7 @@ public class MBeanInfo implements Cloneable, Serializable, DescriptorRead {
|
|||||||
* @return true if and only if <code>o</code> is an MBeanInfo that is equal
|
* @return true if and only if <code>o</code> is an MBeanInfo that is equal
|
||||||
* to this one according to the rules above.
|
* to this one according to the rules above.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (o == this)
|
if (o == this)
|
||||||
return true;
|
return true;
|
||||||
@ -524,6 +534,7 @@ public class MBeanInfo implements Cloneable, Serializable, DescriptorRead {
|
|||||||
Arrays.equals(p.fastGetNotifications(), fastGetNotifications()));
|
Arrays.equals(p.fastGetNotifications(), fastGetNotifications()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
/* Since computing the hashCode is quite expensive, we cache it.
|
/* Since computing the hashCode is quite expensive, we cache it.
|
||||||
If by some terrible misfortune the computed value is 0, the
|
If by some terrible misfortune the computed value is 0, the
|
||||||
@ -747,4 +758,377 @@ public class MBeanInfo implements Cloneable, Serializable, DescriptorRead {
|
|||||||
throw new StreamCorruptedException("Got unexpected byte.");
|
throw new StreamCorruptedException("Got unexpected byte.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Return an {@code MBeanInfo} object that is the same as this one
|
||||||
|
* except that its descriptions are localized in the given locale.
|
||||||
|
* This means the text returned by {@link MBeanInfo#getDescription}
|
||||||
|
* (the description of the MBean itself), and the text returned by the
|
||||||
|
* {@link MBeanFeatureInfo#getDescription getDescription()} method
|
||||||
|
* for every {@linkplain MBeanAttributeInfo attribute}, {@linkplain
|
||||||
|
* MBeanOperationInfo operation}, {@linkplain MBeanConstructorInfo
|
||||||
|
* constructor}, and {@linkplain MBeanNotificationInfo notification}
|
||||||
|
* contained in the {@code MBeanInfo}.</p>
|
||||||
|
*
|
||||||
|
* <p>Here is how the description {@code this.getDescription()} is
|
||||||
|
* localized.</p>
|
||||||
|
*
|
||||||
|
* <p>First, if the {@linkplain #getDescriptor() descriptor}
|
||||||
|
* of this {@code MBeanInfo} contains a field <code><a
|
||||||
|
* href="Descriptor.html#locale">"locale"</a></code>, and the value of
|
||||||
|
* the field is the same as {@code locale.toString()}, then this {@code
|
||||||
|
* MBeanInfo} is returned. Otherwise, localization proceeds as follows,
|
||||||
|
* and the {@code "locale"} field in the returned {@code MBeanInfo} will
|
||||||
|
* be {@code locale.toString()}.
|
||||||
|
*
|
||||||
|
* <p>A <em>{@code className}</em> is determined. If this
|
||||||
|
* {@code MBeanInfo} contains a descriptor with the field
|
||||||
|
* <a href="Descriptor.html#interfaceClassName">{@code
|
||||||
|
* "interfaceClassName"}</a>, then the value of that field is the
|
||||||
|
* {@code className}. Otherwise, it is {@link #getClassName()}.
|
||||||
|
* Everything before the last period (.) in the {@code className} is
|
||||||
|
* the <em>{@code package}</em>, and everything after is the <em>{@code
|
||||||
|
* simpleClassName}</em>. (If there is no period, then the {@code package}
|
||||||
|
* is empty and the {@code simpleClassName} is the same as the {@code
|
||||||
|
* className}.)</p>
|
||||||
|
*
|
||||||
|
* <p>A <em>{@code resourceKey}</em> is determined. If this {@code
|
||||||
|
* MBeanInfo} contains a {@linkplain MBeanInfo#getDescriptor() descriptor}
|
||||||
|
* with a field {@link JMX#DESCRIPTION_RESOURCE_KEY_FIELD
|
||||||
|
* "descriptionResourceKey"}, the value of the field is
|
||||||
|
* the {@code resourceKey}. Otherwise, the {@code resourceKey} is {@code
|
||||||
|
* simpleClassName + ".mbean"}.</p>
|
||||||
|
*
|
||||||
|
* <p>A <em>{@code resourceBundleBaseName}</em> is determined. If
|
||||||
|
* this {@code MBeanInfo} contains a descriptor with a field {@link
|
||||||
|
* JMX#DESCRIPTION_RESOURCE_BUNDLE_BASE_NAME_FIELD
|
||||||
|
* "descriptionResourceBundleBaseName"}, the value of the field
|
||||||
|
* is the {@code resourceBundleBaseName}. Otherwise, the {@code
|
||||||
|
* resourceBundleBaseName} is {@code package + ".MBeanDescriptions"}.
|
||||||
|
*
|
||||||
|
* <p>Then, a {@link java.util.ResourceBundle ResourceBundle} is
|
||||||
|
* determined, using<br> {@link java.util.ResourceBundle#getBundle(String,
|
||||||
|
* Locale, ClassLoader) ResourceBundle.getBundle(resourceBundleBaseName,
|
||||||
|
* locale, loader)}. If this succeeds, and if {@link
|
||||||
|
* java.util.ResourceBundle#getString(String) getString(resourceKey)}
|
||||||
|
* returns a string, then that string is the localized description.
|
||||||
|
* Otherwise, the original description is unchanged.</p>
|
||||||
|
*
|
||||||
|
* <p>A localized description for an {@code MBeanAttributeInfo} is
|
||||||
|
* obtained similarly. The default {@code resourceBundleBaseName}
|
||||||
|
* is the same as above. The default description and the
|
||||||
|
* descriptor fields {@code "descriptionResourceKey"} and {@code
|
||||||
|
* "descriptionResourceBundleBaseName"} come from the {@code
|
||||||
|
* MBeanAttributeInfo} rather than the {@code MBeanInfo}. If the
|
||||||
|
* attribute's {@linkplain MBeanFeatureInfo#getName() name} is {@code
|
||||||
|
* Foo} then its default {@code resourceKey} is {@code simpleClassName +
|
||||||
|
* ".attribute.Foo"}.</p>
|
||||||
|
*
|
||||||
|
* <p>Similar rules apply for operations, constructors, and notifications.
|
||||||
|
* If the name of the operation, constructor, or notification is {@code
|
||||||
|
* Foo} then the default {@code resourceKey} is respectively {@code
|
||||||
|
* simpleClassName + ".operation.Foo"}, {@code simpleClassName +
|
||||||
|
* ".constructor.Foo"}, or {@code simpleClassName + ".notification.Foo"}.
|
||||||
|
* If two operations or constructors have the same name (overloading) then
|
||||||
|
* they have the same default {@code resourceKey}; if different localized
|
||||||
|
* descriptions are needed then a non-default key must be supplied using
|
||||||
|
* {@code "descriptionResourceKey"}.</p>
|
||||||
|
*
|
||||||
|
* <p>Similar rules also apply for descriptions of parameters ({@link
|
||||||
|
* MBeanParameterInfo}). The default {@code resourceKey} for a parameter
|
||||||
|
* whose {@linkplain MBeanFeatureInfo#getName() name} is {@code
|
||||||
|
* Bar} in an operation or constructor called {@code Foo} is {@code
|
||||||
|
* simpleClassName + ".operation.Foo.Bar"} or {@code simpleClassName +
|
||||||
|
* ".constructor.Foo.Bar"} respectively.</p>
|
||||||
|
*
|
||||||
|
* <h4>Example</h4>
|
||||||
|
*
|
||||||
|
* <p>Suppose you have an MBean defined by these two Java source files:</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* // ConfigurationMBean.java
|
||||||
|
* package com.example;
|
||||||
|
* public interface ConfigurationMBean {
|
||||||
|
* public String getName();
|
||||||
|
* public void save(String fileName);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Configuration.java
|
||||||
|
* package com.example;
|
||||||
|
* public class Configuration implements ConfigurationMBean {
|
||||||
|
* public Configuration(String defaultName) {
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>Then you could define the default descriptions for the MBean, by
|
||||||
|
* including a resource bundle called {@code com/example/MBeanDescriptions}
|
||||||
|
* with the compiled classes. Most often this is done by creating a file
|
||||||
|
* {@code MBeanDescriptions.properties} in the same directory as {@code
|
||||||
|
* ConfigurationMBean.java}. Make sure that this file is copied into the
|
||||||
|
* same place as the compiled classes; in typical build environments that
|
||||||
|
* will be true by default.</p>
|
||||||
|
*
|
||||||
|
* <p>The file {@code com/example/MBeanDescriptions.properties} might
|
||||||
|
* look like this:</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* # Description of the MBean
|
||||||
|
* ConfigurationMBean.mbean = Configuration manager
|
||||||
|
*
|
||||||
|
* # Description of the Name attribute
|
||||||
|
* ConfigurationMBean.attribute.Name = The name of the configuration
|
||||||
|
*
|
||||||
|
* # Description of the save operation
|
||||||
|
* ConfigurationMBean.operation.save = Save the configuration to a file
|
||||||
|
*
|
||||||
|
* # Description of the parameter to the save operation.
|
||||||
|
* # Parameter names from the original Java source are not available,
|
||||||
|
* # so the default names are p1, p2, etc. If the names were available,
|
||||||
|
* # this would be ConfigurationMBean.operation.save.fileName
|
||||||
|
* ConfigurationMBean.operation.save.p1 = The name of the file
|
||||||
|
*
|
||||||
|
* # Description of the constructor. The default name of a constructor is
|
||||||
|
* # its fully-qualified class name.
|
||||||
|
* ConfigurationMBean.constructor.com.example.Configuration = <!--
|
||||||
|
* -->Constructor with name of default file
|
||||||
|
* # Description of the constructor parameter.
|
||||||
|
* ConfigurationMBean.constructor.com.example.Configuration.p1 = <!--
|
||||||
|
* -->Name of the default file
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>Starting with this file, you could create descriptions for the French
|
||||||
|
* locale by creating {@code com/example/MBeanDescriptions_fr.properties}.
|
||||||
|
* The keys in this file are the same as before but the text has been
|
||||||
|
* translated:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* ConfigurationMBean.mbean = Gestionnaire de configuration
|
||||||
|
*
|
||||||
|
* ConfigurationMBean.attribute.Name = Le nom de la configuration
|
||||||
|
*
|
||||||
|
* ConfigurationMBean.operation.save = Sauvegarder la configuration <!--
|
||||||
|
* -->dans un fichier
|
||||||
|
*
|
||||||
|
* ConfigurationMBean.operation.save.p1 = Le nom du fichier
|
||||||
|
*
|
||||||
|
* ConfigurationMBean.constructor.com.example.Configuration = <!--
|
||||||
|
* -->Constructeur avec nom du fichier par défaut
|
||||||
|
* ConfigurationMBean.constructor.com.example.Configuration.p1 = <!--
|
||||||
|
* -->Nom du fichier par défaut
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>The descriptions in {@code MBeanDescriptions.properties} and
|
||||||
|
* {@code MBeanDescriptions_fr.properties} will only be consulted if
|
||||||
|
* {@code localizeDescriptions} is called, perhaps because the
|
||||||
|
* MBean Server has been wrapped by {@link
|
||||||
|
* ClientContext#newLocalizeMBeanInfoForwarder} or because the
|
||||||
|
* connector server has been created with the {@link
|
||||||
|
* javax.management.remote.JMXConnectorServer#LOCALIZE_MBEAN_INFO_FORWARDER
|
||||||
|
* LOCALIZE_MBEAN_INFO_FORWARDER} option. If you want descriptions
|
||||||
|
* even when there is no localization step, then you should consider
|
||||||
|
* using {@link Description @Description} annotations. Annotations
|
||||||
|
* provide descriptions by default but are overridden if {@code
|
||||||
|
* localizeDescriptions} is called.</p>
|
||||||
|
*
|
||||||
|
* @param locale the target locale for descriptions. Cannot be null.
|
||||||
|
*
|
||||||
|
* @param loader the {@code ClassLoader} to use for looking up resource
|
||||||
|
* bundles.
|
||||||
|
*
|
||||||
|
* @return an {@code MBeanInfo} with descriptions appropriately localized.
|
||||||
|
*
|
||||||
|
* @throws NullPointerException if {@code locale} is null.
|
||||||
|
*/
|
||||||
|
public MBeanInfo localizeDescriptions(Locale locale, ClassLoader loader) {
|
||||||
|
if (locale == null)
|
||||||
|
throw new NullPointerException("locale");
|
||||||
|
Descriptor d = getDescriptor();
|
||||||
|
String mbiLocaleString = (String) d.getFieldValue(JMX.LOCALE_FIELD);
|
||||||
|
if (locale.toString().equals(mbiLocaleString))
|
||||||
|
return this;
|
||||||
|
return new Rewriter(this, locale, loader).getMBeanInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Rewriter {
|
||||||
|
private final MBeanInfo mbi;
|
||||||
|
private final ClassLoader loader;
|
||||||
|
private final Locale locale;
|
||||||
|
private final String packageName;
|
||||||
|
private final String simpleClassNamePlusDot;
|
||||||
|
private ResourceBundle defaultBundle;
|
||||||
|
private boolean defaultBundleLoaded;
|
||||||
|
|
||||||
|
// ResourceBundle.getBundle throws NullPointerException
|
||||||
|
// if the loader is null, even though that is perfectly
|
||||||
|
// valid and means the bootstrap loader. So we work
|
||||||
|
// around with a ClassLoader that is equivalent to the
|
||||||
|
// bootstrap loader but is not null.
|
||||||
|
private static final ClassLoader bootstrapLoader =
|
||||||
|
new ClassLoader(null) {};
|
||||||
|
|
||||||
|
Rewriter(MBeanInfo mbi, Locale locale, ClassLoader loader) {
|
||||||
|
this.mbi = mbi;
|
||||||
|
this.locale = locale;
|
||||||
|
if (loader == null)
|
||||||
|
loader = bootstrapLoader;
|
||||||
|
this.loader = loader;
|
||||||
|
|
||||||
|
String intfName = (String)
|
||||||
|
mbi.getDescriptor().getFieldValue("interfaceClassName");
|
||||||
|
if (intfName == null)
|
||||||
|
intfName = mbi.getClassName();
|
||||||
|
int lastDot = intfName.lastIndexOf('.');
|
||||||
|
this.packageName = intfName.substring(0, lastDot + 1);
|
||||||
|
this.simpleClassNamePlusDot = intfName.substring(lastDot + 1) + ".";
|
||||||
|
// Inner classes show up as Outer$Inner so won't match the dot.
|
||||||
|
// When there is no dot, lastDot is -1,
|
||||||
|
// packageName is empty, and simpleClassNamePlusDot is intfName.
|
||||||
|
}
|
||||||
|
|
||||||
|
MBeanInfo getMBeanInfo() {
|
||||||
|
MBeanAttributeInfo[] mbais =
|
||||||
|
rewrite(mbi.getAttributes(), "attribute.");
|
||||||
|
MBeanOperationInfo[] mbois =
|
||||||
|
rewrite(mbi.getOperations(), "operation.");
|
||||||
|
MBeanConstructorInfo[] mbcis =
|
||||||
|
rewrite(mbi.getConstructors(), "constructor.");
|
||||||
|
MBeanNotificationInfo[] mbnis =
|
||||||
|
rewrite(mbi.getNotifications(), "notification.");
|
||||||
|
Descriptor d = mbi.getDescriptor();
|
||||||
|
d = changeLocale(d);
|
||||||
|
String description = getDescription(d, "mbean", "");
|
||||||
|
if (description == null)
|
||||||
|
description = mbi.getDescription();
|
||||||
|
return new MBeanInfo(
|
||||||
|
mbi.getClassName(), description,
|
||||||
|
mbais, mbcis, mbois, mbnis, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Descriptor changeLocale(Descriptor d) {
|
||||||
|
if (d.getFieldValue(JMX.LOCALE_FIELD) != null) {
|
||||||
|
Map<String, Object> map = new HashMap<String, Object>();
|
||||||
|
for (String field : d.getFieldNames())
|
||||||
|
map.put(field, d.getFieldValue(field));
|
||||||
|
map.remove(JMX.LOCALE_FIELD);
|
||||||
|
d = new ImmutableDescriptor(map);
|
||||||
|
}
|
||||||
|
return ImmutableDescriptor.union(
|
||||||
|
d, new ImmutableDescriptor(JMX.LOCALE_FIELD + "=" + locale));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDescription(
|
||||||
|
Descriptor d, String defaultPrefix, String defaultSuffix) {
|
||||||
|
ResourceBundle bundle = bundleFromDescriptor(d);
|
||||||
|
if (bundle == null)
|
||||||
|
return null;
|
||||||
|
String key =
|
||||||
|
(String) d.getFieldValue(JMX.DESCRIPTION_RESOURCE_KEY_FIELD);
|
||||||
|
if (key == null)
|
||||||
|
key = simpleClassNamePlusDot + defaultPrefix + defaultSuffix;
|
||||||
|
return descriptionFromResource(bundle, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends MBeanFeatureInfo> T[] rewrite(
|
||||||
|
T[] features, String resourcePrefix) {
|
||||||
|
for (int i = 0; i < features.length; i++) {
|
||||||
|
T feature = features[i];
|
||||||
|
Descriptor d = feature.getDescriptor();
|
||||||
|
String description =
|
||||||
|
getDescription(d, resourcePrefix, feature.getName());
|
||||||
|
if (description != null &&
|
||||||
|
!description.equals(feature.getDescription())) {
|
||||||
|
features[i] = setDescription(feature, description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return features;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends MBeanFeatureInfo> T setDescription(
|
||||||
|
T feature, String description) {
|
||||||
|
|
||||||
|
Object newf;
|
||||||
|
String name = feature.getName();
|
||||||
|
Descriptor d = feature.getDescriptor();
|
||||||
|
|
||||||
|
if (feature instanceof MBeanAttributeInfo) {
|
||||||
|
MBeanAttributeInfo mbai = (MBeanAttributeInfo) feature;
|
||||||
|
newf = new MBeanAttributeInfo(
|
||||||
|
name, mbai.getType(), description,
|
||||||
|
mbai.isReadable(), mbai.isWritable(), mbai.isIs(),
|
||||||
|
d);
|
||||||
|
} else if (feature instanceof MBeanOperationInfo) {
|
||||||
|
MBeanOperationInfo mboi = (MBeanOperationInfo) feature;
|
||||||
|
MBeanParameterInfo[] sig = rewrite(
|
||||||
|
mboi.getSignature(), "operation." + name + ".");
|
||||||
|
newf = new MBeanOperationInfo(
|
||||||
|
name, description, sig,
|
||||||
|
mboi.getReturnType(), mboi.getImpact(), d);
|
||||||
|
} else if (feature instanceof MBeanConstructorInfo) {
|
||||||
|
MBeanConstructorInfo mbci = (MBeanConstructorInfo) feature;
|
||||||
|
MBeanParameterInfo[] sig = rewrite(
|
||||||
|
mbci.getSignature(), "constructor." + name + ".");
|
||||||
|
newf = new MBeanConstructorInfo(
|
||||||
|
name, description, sig, d);
|
||||||
|
} else if (feature instanceof MBeanNotificationInfo) {
|
||||||
|
MBeanNotificationInfo mbni = (MBeanNotificationInfo) feature;
|
||||||
|
newf = new MBeanNotificationInfo(
|
||||||
|
mbni.getNotifTypes(), name, description, d);
|
||||||
|
} else if (feature instanceof MBeanParameterInfo) {
|
||||||
|
MBeanParameterInfo mbpi = (MBeanParameterInfo) feature;
|
||||||
|
newf = new MBeanParameterInfo(
|
||||||
|
name, mbpi.getType(), description, d);
|
||||||
|
} else {
|
||||||
|
logger().log(Level.FINE, "Unknown feature type: " +
|
||||||
|
feature.getClass());
|
||||||
|
newf = feature;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Util.<T>cast(newf);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResourceBundle bundleFromDescriptor(Descriptor d) {
|
||||||
|
String bundleName = (String) d.getFieldValue(
|
||||||
|
JMX.DESCRIPTION_RESOURCE_BUNDLE_BASE_NAME_FIELD);
|
||||||
|
|
||||||
|
if (bundleName != null)
|
||||||
|
return getBundle(bundleName);
|
||||||
|
|
||||||
|
if (defaultBundleLoaded)
|
||||||
|
return defaultBundle;
|
||||||
|
|
||||||
|
bundleName = packageName + "MBeanDescriptions";
|
||||||
|
defaultBundle = getBundle(bundleName);
|
||||||
|
defaultBundleLoaded = true;
|
||||||
|
return defaultBundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String descriptionFromResource(
|
||||||
|
ResourceBundle bundle, String key) {
|
||||||
|
try {
|
||||||
|
return bundle.getString(key);
|
||||||
|
} catch (MissingResourceException e) {
|
||||||
|
logger().log(Level.FINEST, "No resource for " + key, e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger().log(Level.FINE, "Bad resource for " + key, e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResourceBundle getBundle(String name) {
|
||||||
|
try {
|
||||||
|
return ResourceBundle.getBundle(name, locale, loader);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger().log(Level.FINE,
|
||||||
|
"Could not load ResourceBundle " + name, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Logger logger() {
|
||||||
|
return Logger.getLogger("javax.management.locale");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,15 +27,43 @@ package javax.management;
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a notification emitted by the MBean server through the MBeanServerDelegate MBean.
|
* Represents a notification emitted by the MBean Server through the MBeanServerDelegate MBean.
|
||||||
* The MBean Server emits the following types of notifications: MBean registration, MBean
|
* The MBean Server emits the following types of notifications: MBean registration, MBean
|
||||||
* de-registration.
|
* unregistration.
|
||||||
* <P>
|
* <P>
|
||||||
* To receive to MBeanServerNotifications, you need to be declared as listener to
|
* To receive MBeanServerNotifications, you need to register a listener with
|
||||||
* the {@link javax.management.MBeanServerDelegate javax.management.MBeanServerDelegate} MBean
|
* the {@link MBeanServerDelegate MBeanServerDelegate} MBean
|
||||||
* that represents the MBeanServer. The ObjectName of the MBeanServerDelegate is:
|
* that represents the MBeanServer. The ObjectName of the MBeanServerDelegate is
|
||||||
|
* {@link MBeanServerDelegate#DELEGATE_NAME}, which is
|
||||||
* <CODE>JMImplementation:type=MBeanServerDelegate</CODE>.
|
* <CODE>JMImplementation:type=MBeanServerDelegate</CODE>.
|
||||||
*
|
*
|
||||||
|
* <p>The following code prints a message every time an MBean is registered
|
||||||
|
* or unregistered in the MBean Server {@code mbeanServer}:</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* private static final NotificationListener printListener = new NotificationListener() {
|
||||||
|
* public void handleNotification(Notification n, Object handback) {
|
||||||
|
* if (!(n instanceof MBeanServerNotification)) {
|
||||||
|
* System.out.println("Ignored notification of class " + n.getClass().getName());
|
||||||
|
* return;
|
||||||
|
* }
|
||||||
|
* MBeanServerNotification mbsn = (MBeanServerNotification) n;
|
||||||
|
* String what;
|
||||||
|
* if (n.getType().equals(MBeanServerNotification.REGISTRATION_NOTIFICATION))
|
||||||
|
* what = "MBean registered";
|
||||||
|
* else if (n.getType().equals(MBeanServerNotification.UNREGISTRATION_NOTIFICATION))
|
||||||
|
* what = "MBean unregistered";
|
||||||
|
* else
|
||||||
|
* what = "Unknown type " + n.getType();
|
||||||
|
* System.out.println("Received MBean Server notification: " + what + ": " +
|
||||||
|
* mbsn.getMBeanName());
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* ...
|
||||||
|
* mbeanServer.addNotificationListener(
|
||||||
|
* MBeanServerDelegate.DELEGATE_NAME, printListener, null, null);
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* @since 1.5
|
* @since 1.5
|
||||||
*/
|
*/
|
||||||
public class MBeanServerNotification extends Notification {
|
public class MBeanServerNotification extends Notification {
|
||||||
|
@ -54,7 +54,7 @@ import com.sun.jmx.mbeanserver.GetPropertyAction;
|
|||||||
* @since 1.5
|
* @since 1.5
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("serial") // serialVersionUID is not constant
|
@SuppressWarnings("serial") // serialVersionUID is not constant
|
||||||
public class Notification extends EventObject {
|
public class Notification extends EventObject implements Cloneable {
|
||||||
|
|
||||||
// Serialization compatibility stuff:
|
// Serialization compatibility stuff:
|
||||||
// Two serial forms are supported in this class. The selected form depends
|
// Two serial forms are supported in this class. The selected form depends
|
||||||
@ -243,6 +243,26 @@ public class Notification extends EventObject {
|
|||||||
this.message = message ;
|
this.message = message ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates and returns a copy of this object. The copy is created as
|
||||||
|
* described for {@link Object#clone()}. This means, first, that the
|
||||||
|
* class of the object will be the same as the class of this object, and,
|
||||||
|
* second, that the copy is a "shallow copy". Fields of this notification
|
||||||
|
* are not themselves copied. In particular, the {@linkplain
|
||||||
|
* #getUserData user data} of the copy is the same object as the
|
||||||
|
* original.</p>
|
||||||
|
*
|
||||||
|
* @return a copy of this object.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object clone() {
|
||||||
|
try {
|
||||||
|
return super.clone();
|
||||||
|
} catch (CloneNotSupportedException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the source.
|
* Sets the source.
|
||||||
*
|
*
|
||||||
@ -285,8 +305,10 @@ public class Notification extends EventObject {
|
|||||||
/**
|
/**
|
||||||
* Get the notification type.
|
* Get the notification type.
|
||||||
*
|
*
|
||||||
* @return The notification type. It's a string expressed in a dot notation similar
|
* @return The notification type. It's a string expressed in a dot notation
|
||||||
* to Java properties. An example of a notification type is network.alarm.router .
|
* similar to Java properties. It is recommended that the notification type
|
||||||
|
* should follow the reverse-domain-name convention used by Java package
|
||||||
|
* names. An example of a notification type is com.example.alarm.router.
|
||||||
*/
|
*/
|
||||||
public String getType() {
|
public String getType() {
|
||||||
return type ;
|
return type ;
|
||||||
@ -317,14 +339,25 @@ public class Notification extends EventObject {
|
|||||||
/**
|
/**
|
||||||
* Get the notification message.
|
* Get the notification message.
|
||||||
*
|
*
|
||||||
* @return The message string of this notification object. It contains in a string,
|
* @return The message string of this notification object.
|
||||||
* which could be the explanation of the notification for displaying to a user
|
|
||||||
*
|
*
|
||||||
|
* @see #setMessage
|
||||||
*/
|
*/
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
return message ;
|
return message ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the notification message.
|
||||||
|
*
|
||||||
|
* @param message the new notification message.
|
||||||
|
*
|
||||||
|
* @see #getMessage
|
||||||
|
*/
|
||||||
|
public void setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the user data.
|
* Get the user data.
|
||||||
*
|
*
|
||||||
@ -355,6 +388,7 @@ public class Notification extends EventObject {
|
|||||||
*
|
*
|
||||||
* @return A String representation of this notification.
|
* @return A String representation of this notification.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return super.toString()+"[type="+type+"][message="+message+"]";
|
return super.toString()+"[type="+type+"][message="+message+"]";
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,6 @@ import com.sun.jmx.event.DaemonThreadFactory;
|
|||||||
import com.sun.jmx.event.LeaseRenewer;
|
import com.sun.jmx.event.LeaseRenewer;
|
||||||
import com.sun.jmx.event.ReceiverBuffer;
|
import com.sun.jmx.event.ReceiverBuffer;
|
||||||
import com.sun.jmx.event.RepeatedSingletonJob;
|
import com.sun.jmx.event.RepeatedSingletonJob;
|
||||||
import com.sun.jmx.namespace.JMXNamespaceUtils;
|
|
||||||
import com.sun.jmx.mbeanserver.PerThreadGroupPool;
|
import com.sun.jmx.mbeanserver.PerThreadGroupPool;
|
||||||
import com.sun.jmx.remote.util.ClassLogger;
|
import com.sun.jmx.remote.util.ClassLogger;
|
||||||
|
|
||||||
@ -58,7 +57,6 @@ import javax.management.NotificationBroadcasterSupport;
|
|||||||
import javax.management.NotificationFilter;
|
import javax.management.NotificationFilter;
|
||||||
import javax.management.NotificationListener;
|
import javax.management.NotificationListener;
|
||||||
import javax.management.ObjectName;
|
import javax.management.ObjectName;
|
||||||
import javax.management.remote.JMXConnector;
|
|
||||||
import javax.management.remote.NotificationResult;
|
import javax.management.remote.NotificationResult;
|
||||||
import javax.management.remote.TargetedNotification;
|
import javax.management.remote.TargetedNotification;
|
||||||
|
|
||||||
@ -129,11 +127,12 @@ public class EventClient implements EventConsumer, NotificationManager {
|
|||||||
public static final String NOTIFS_LOST = "jmx.event.service.notifs.lost";
|
public static final String NOTIFS_LOST = "jmx.event.service.notifs.lost";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default lease time, {@value}, in milliseconds.
|
* The default lease time that EventClient instances will request, in
|
||||||
|
* milliseconds. This value is {@value}.
|
||||||
*
|
*
|
||||||
* @see EventClientDelegateMBean#lease
|
* @see EventClientDelegateMBean#lease
|
||||||
*/
|
*/
|
||||||
public static final long DEFAULT_LEASE_TIMEOUT = 300000;
|
public static final long DEFAULT_REQUESTED_LEASE_TIME = 300000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Constructs a default {@code EventClient} object.</p>
|
* <p>Constructs a default {@code EventClient} object.</p>
|
||||||
@ -173,7 +172,7 @@ public class EventClient implements EventConsumer, NotificationManager {
|
|||||||
*/
|
*/
|
||||||
public EventClient(EventClientDelegateMBean delegate)
|
public EventClient(EventClientDelegateMBean delegate)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
this(delegate, null, null, null, DEFAULT_LEASE_TIMEOUT);
|
this(delegate, null, null, null, DEFAULT_REQUESTED_LEASE_TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -196,7 +195,7 @@ public class EventClient implements EventConsumer, NotificationManager {
|
|||||||
* If {@code null}, a default scheduler will be used.
|
* If {@code null}, a default scheduler will be used.
|
||||||
* @param requestedLeaseTime The lease time used to keep this client alive
|
* @param requestedLeaseTime The lease time used to keep this client alive
|
||||||
* in the {@link EventClientDelegateMBean}. A value of zero is equivalent
|
* in the {@link EventClientDelegateMBean}. A value of zero is equivalent
|
||||||
* to the {@linkplain #DEFAULT_LEASE_TIMEOUT default value}.
|
* to the {@linkplain #DEFAULT_REQUESTED_LEASE_TIME default value}.
|
||||||
*
|
*
|
||||||
* @throws IllegalArgumentException If {@code delegate} is null.
|
* @throws IllegalArgumentException If {@code delegate} is null.
|
||||||
* @throws IOException If an I/O error occurs when communicating with the
|
* @throws IOException If an I/O error occurs when communicating with the
|
||||||
@ -213,7 +212,7 @@ public class EventClient implements EventConsumer, NotificationManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (requestedLeaseTime == 0)
|
if (requestedLeaseTime == 0)
|
||||||
requestedLeaseTime = DEFAULT_LEASE_TIMEOUT;
|
requestedLeaseTime = DEFAULT_REQUESTED_LEASE_TIME;
|
||||||
else if (requestedLeaseTime < 0) {
|
else if (requestedLeaseTime < 0) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Negative lease time: " + requestedLeaseTime);
|
"Negative lease time: " + requestedLeaseTime);
|
||||||
@ -269,7 +268,13 @@ public class EventClient implements EventConsumer, NotificationManager {
|
|||||||
new ScheduledThreadPoolExecutor(20, daemonThreadFactory);
|
new ScheduledThreadPoolExecutor(20, daemonThreadFactory);
|
||||||
executor.setKeepAliveTime(1, TimeUnit.SECONDS);
|
executor.setKeepAliveTime(1, TimeUnit.SECONDS);
|
||||||
executor.allowCoreThreadTimeOut(true);
|
executor.allowCoreThreadTimeOut(true);
|
||||||
executor.setRemoveOnCancelPolicy(true);
|
if (setRemoveOnCancelPolicy != null) {
|
||||||
|
try {
|
||||||
|
setRemoveOnCancelPolicy.invoke(executor, true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.trace("setRemoveOnCancelPolicy", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
// By default, a ScheduledThreadPoolExecutor will keep jobs
|
// By default, a ScheduledThreadPoolExecutor will keep jobs
|
||||||
// in its queue even after they have been cancelled. They
|
// in its queue even after they have been cancelled. They
|
||||||
// will only be removed when their scheduled time arrives.
|
// will only be removed when their scheduled time arrives.
|
||||||
@ -277,12 +282,25 @@ public class EventClient implements EventConsumer, NotificationManager {
|
|||||||
// this EventClient, this can lead to a moderately large number
|
// this EventClient, this can lead to a moderately large number
|
||||||
// of objects remaining referenced until the renewal time
|
// of objects remaining referenced until the renewal time
|
||||||
// arrives. Hence the above call, which removes the job from
|
// arrives. Hence the above call, which removes the job from
|
||||||
// the queue as soon as it is cancelled.
|
// the queue as soon as it is cancelled. Since the call is
|
||||||
|
// new with JDK 7, we invoke it via reflection to make it
|
||||||
|
// easier to use this code on JDK 6.
|
||||||
return executor;
|
return executor;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return leaseRenewerThreadPool.getThreadPoolExecutor(create);
|
return leaseRenewerThreadPool.getThreadPoolExecutor(create);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Method setRemoveOnCancelPolicy;
|
||||||
|
static {
|
||||||
|
Method m;
|
||||||
|
try {
|
||||||
|
m = ScheduledThreadPoolExecutor.class.getMethod(
|
||||||
|
"setRemoveOnCancelPolicy", boolean.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
m = null;
|
||||||
|
}
|
||||||
|
setRemoveOnCancelPolicy = m;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1042,7 +1060,7 @@ public class EventClient implements EventConsumer, NotificationManager {
|
|||||||
final public EventClient call() throws Exception {
|
final public EventClient call() throws Exception {
|
||||||
EventClientDelegateMBean ecd = EventClientDelegate.getProxy(conn);
|
EventClientDelegateMBean ecd = EventClientDelegate.getProxy(conn);
|
||||||
return new EventClient(ecd, eventRelay, null, null,
|
return new EventClient(ecd, eventRelay, null, null,
|
||||||
DEFAULT_LEASE_TIMEOUT);
|
DEFAULT_REQUESTED_LEASE_TIME);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1080,24 +1098,6 @@ public class EventClient implements EventConsumer, NotificationManager {
|
|||||||
return clientId;
|
return clientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a JMX Connector that will use an {@link EventClient}
|
|
||||||
* to subscribe for notifications. If the server doesn't have
|
|
||||||
* an {@link EventClientDelegateMBean}, then the connector will
|
|
||||||
* use the legacy notification mechanism instead.
|
|
||||||
*
|
|
||||||
* @param wrapped The underlying JMX Connector wrapped by the returned
|
|
||||||
* connector.
|
|
||||||
*
|
|
||||||
* @return A JMX Connector that will uses an {@link EventClient}, if
|
|
||||||
* available.
|
|
||||||
*
|
|
||||||
* @see EventClient#getEventClientConnection(MBeanServerConnection)
|
|
||||||
*/
|
|
||||||
public static JMXConnector withEventClient(final JMXConnector wrapped) {
|
|
||||||
return JMXNamespaceUtils.withEventClient(wrapped);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final PerThreadGroupPool<ScheduledThreadPoolExecutor>
|
private static final PerThreadGroupPool<ScheduledThreadPoolExecutor>
|
||||||
leaseRenewerThreadPool = PerThreadGroupPool.make();
|
leaseRenewerThreadPool = PerThreadGroupPool.make();
|
||||||
}
|
}
|
||||||
|
@ -149,6 +149,7 @@ public class EventClientDelegate implements EventClientDelegateMBean {
|
|||||||
// of a setMBeanServer on some other forwarder later in the chain.
|
// of a setMBeanServer on some other forwarder later in the chain.
|
||||||
|
|
||||||
private static class Forwarder extends SingleMBeanForwarder {
|
private static class Forwarder extends SingleMBeanForwarder {
|
||||||
|
private MBeanServer loopMBS;
|
||||||
|
|
||||||
private static class UnsupportedInvocationHandler
|
private static class UnsupportedInvocationHandler
|
||||||
implements InvocationHandler {
|
implements InvocationHandler {
|
||||||
@ -173,7 +174,11 @@ public class EventClientDelegate implements EventClientDelegateMBean {
|
|||||||
private volatile boolean madeECD;
|
private volatile boolean madeECD;
|
||||||
|
|
||||||
Forwarder() {
|
Forwarder() {
|
||||||
super(OBJECT_NAME, makeUnsupportedECD());
|
super(OBJECT_NAME, makeUnsupportedECD(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void setLoopMBS(MBeanServer loopMBS) {
|
||||||
|
this.loopMBS = loopMBS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -186,7 +191,7 @@ public class EventClientDelegate implements EventClientDelegateMBean {
|
|||||||
AccessController.doPrivileged(
|
AccessController.doPrivileged(
|
||||||
new PrivilegedAction<EventClientDelegate>() {
|
new PrivilegedAction<EventClientDelegate>() {
|
||||||
public EventClientDelegate run() {
|
public EventClientDelegate run() {
|
||||||
return getEventClientDelegate(Forwarder.this);
|
return getEventClientDelegate(loopMBS);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
DynamicMBean mbean = new StandardMBean(
|
DynamicMBean mbean = new StandardMBean(
|
||||||
@ -208,11 +213,46 @@ public class EventClientDelegate implements EventClientDelegateMBean {
|
|||||||
* that are targeted for that MBean and handles them itself. All other
|
* that are targeted for that MBean and handles them itself. All other
|
||||||
* requests are forwarded to the next element in the forwarder chain.</p>
|
* requests are forwarded to the next element in the forwarder chain.</p>
|
||||||
*
|
*
|
||||||
|
* @param nextMBS the next {@code MBeanServer} in the chain of forwarders,
|
||||||
|
* which might be another {@code MBeanServerForwarder} or a plain {@code
|
||||||
|
* MBeanServer}. This is the object to which {@code MBeanServer} requests
|
||||||
|
* that do not concern the {@code EventClientDelegateMBean} are sent.
|
||||||
|
* It will be the value of {@link MBeanServerForwarder#getMBeanServer()
|
||||||
|
* getMBeanServer()} on the returned object, and can be changed with {@link
|
||||||
|
* MBeanServerForwarder#setMBeanServer setMBeanServer}. It can be null but
|
||||||
|
* must be set to a non-null value before any {@code MBeanServer} requests
|
||||||
|
* arrive.
|
||||||
|
*
|
||||||
|
* @param loopMBS the {@code MBeanServer} to which requests from the
|
||||||
|
* {@code EventClientDelegateMBean} should be sent. For example,
|
||||||
|
* when you invoke the {@link EventClientDelegateMBean#addListener
|
||||||
|
* addListener} operation on the {@code EventClientDelegateMBean}, it will
|
||||||
|
* result in a call to {@link
|
||||||
|
* MBeanServer#addNotificationListener(ObjectName, NotificationListener,
|
||||||
|
* NotificationFilter, Object) addNotificationListener} on this object.
|
||||||
|
* If this parameter is null, then these requests will be sent to the
|
||||||
|
* newly-created {@code MBeanServerForwarder}. Usually the parameter will
|
||||||
|
* either be null or will be the result of {@link
|
||||||
|
* javax.management.remote.JMXConnectorServer#getSystemMBeanServerForwarder()
|
||||||
|
* getSystemMBeanServerForwarder()} for the connector server in which
|
||||||
|
* this forwarder will be installed.
|
||||||
|
*
|
||||||
* @return a new {@code MBeanServerForwarder} that simulates the existence
|
* @return a new {@code MBeanServerForwarder} that simulates the existence
|
||||||
* of an {@code EventClientDelegateMBean}.
|
* of an {@code EventClientDelegateMBean}.
|
||||||
|
*
|
||||||
|
* @see javax.management.remote.JMXConnectorServer#installStandardForwarders
|
||||||
*/
|
*/
|
||||||
public static MBeanServerForwarder newForwarder() {
|
public static MBeanServerForwarder newForwarder(
|
||||||
return new Forwarder();
|
MBeanServer nextMBS, MBeanServer loopMBS) {
|
||||||
|
Forwarder mbsf = new Forwarder();
|
||||||
|
// We must setLoopMBS before setMBeanServer, because when we
|
||||||
|
// setMBeanServer that will call getEventClientDelegate(loopMBS).
|
||||||
|
if (loopMBS == null)
|
||||||
|
loopMBS = mbsf;
|
||||||
|
mbsf.setLoopMBS(loopMBS);
|
||||||
|
if (nextMBS != null)
|
||||||
|
mbsf.setMBeanServer(nextMBS);
|
||||||
|
return mbsf;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -437,10 +477,9 @@ public class EventClientDelegate implements EventClientDelegateMBean {
|
|||||||
// private classes
|
// private classes
|
||||||
// ------------------------------------
|
// ------------------------------------
|
||||||
private class ClientInfo {
|
private class ClientInfo {
|
||||||
String clientId;
|
final String clientId;
|
||||||
EventBuffer buffer;
|
final NotificationListener clientListener;
|
||||||
NotificationListener clientListener;
|
final Map<Integer, AddedListener> listenerInfoMap =
|
||||||
Map<Integer, AddedListener> listenerInfoMap =
|
|
||||||
new HashMap<Integer, AddedListener>();
|
new HashMap<Integer, AddedListener>();
|
||||||
|
|
||||||
ClientInfo(String clientId, EventForwarder forwarder) {
|
ClientInfo(String clientId, EventForwarder forwarder) {
|
||||||
@ -703,7 +742,8 @@ public class EventClientDelegate implements EventClientDelegateMBean {
|
|||||||
clientInfo = clientInfoMap.get(clientId);
|
clientInfo = clientInfoMap.get(clientId);
|
||||||
|
|
||||||
if (clientInfo == null) {
|
if (clientInfo == null) {
|
||||||
throw new EventClientNotFoundException("The client is not found.");
|
throw new EventClientNotFoundException(
|
||||||
|
"Client not found (id " + clientId + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
return clientInfo;
|
return clientInfo;
|
||||||
|
@ -51,7 +51,8 @@ import javax.management.remote.NotificationResult;
|
|||||||
* and the MBean Server, that will intercept accesses to the Event Client
|
* and the MBean Server, that will intercept accesses to the Event Client
|
||||||
* Delegate MBean and treat them as the real MBean would. This forwarder is
|
* Delegate MBean and treat them as the real MBean would. This forwarder is
|
||||||
* inserted by default with the standard RMI Connector Server, and can also
|
* inserted by default with the standard RMI Connector Server, and can also
|
||||||
* be created explicitly using {@link EventClientDelegate#newForwarder()}.
|
* be created explicitly using {@link EventClientDelegate#newForwarder
|
||||||
|
* EventClientDelegate.newForwarder}.
|
||||||
*
|
*
|
||||||
* <li><p>A variant on the above is to replace the MBean Server that is
|
* <li><p>A variant on the above is to replace the MBean Server that is
|
||||||
* used locally with a forwarder as described above. Since
|
* used locally with a forwarder as described above. Since
|
||||||
@ -61,9 +62,7 @@ import javax.management.remote.NotificationResult;
|
|||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); // or whatever
|
* MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); // or whatever
|
||||||
* MBeanServerForwarder mbsf = EventClientDelegate.newForwarder();
|
* mbs = EventClientDelegate.newForwarder(mbs, null);
|
||||||
* mbsf.setMBeanServer(mbs);
|
|
||||||
* mbs = mbsf;
|
|
||||||
* // now use mbs just as you did before, but it will have an EventClientDelegate
|
* // now use mbs just as you did before, but it will have an EventClientDelegate
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
|
@ -27,7 +27,6 @@ package javax.management.event;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.Executors; // for javadoc
|
import java.util.concurrent.Executors; // for javadoc
|
||||||
import java.util.concurrent.ScheduledFuture;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface is used to specify a way to receive
|
* This interface is used to specify a way to receive
|
||||||
|
@ -83,8 +83,8 @@
|
|||||||
* javax.management.event.EventClientDelegateMBean EventClientDelegateMBean}
|
* javax.management.event.EventClientDelegateMBean EventClientDelegateMBean}
|
||||||
* must be registered in the MBean Server, or the connector server must
|
* must be registered in the MBean Server, or the connector server must
|
||||||
* be configured to simulate the existence of this MBean, for example
|
* be configured to simulate the existence of this MBean, for example
|
||||||
* using {@link javax.management.event.EventClientDelegate#newForwarder()
|
* using {@link javax.management.event.EventClientDelegate#newForwarder
|
||||||
* EventClientDelegate.newForwarder()}. The standard RMI connector is so
|
* EventClientDelegate.newForwarder}. The standard RMI connector is so
|
||||||
* configured by default. The {@code EventClientDelegateMBean} documentation
|
* configured by default. The {@code EventClientDelegateMBean} documentation
|
||||||
* has further details.</p>
|
* has further details.</p>
|
||||||
*
|
*
|
||||||
|
@ -26,21 +26,19 @@
|
|||||||
package javax.management.namespace;
|
package javax.management.namespace;
|
||||||
|
|
||||||
import com.sun.jmx.defaults.JmxProperties;
|
import com.sun.jmx.defaults.JmxProperties;
|
||||||
import com.sun.jmx.namespace.JMXNamespaceUtils;
|
|
||||||
import com.sun.jmx.namespace.ObjectNameRouter;
|
import com.sun.jmx.namespace.ObjectNameRouter;
|
||||||
import com.sun.jmx.namespace.serial.RewritingProcessor;
|
import com.sun.jmx.namespace.serial.RewritingProcessor;
|
||||||
import com.sun.jmx.namespace.RoutingConnectionProxy;
|
import com.sun.jmx.namespace.RoutingConnectionProxy;
|
||||||
import com.sun.jmx.namespace.RoutingServerProxy;
|
import com.sun.jmx.namespace.RoutingServerProxy;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.management.InstanceNotFoundException;
|
||||||
import javax.management.MBeanServer;
|
import javax.management.MBeanServer;
|
||||||
import javax.management.MBeanServerConnection;
|
import javax.management.MBeanServerConnection;
|
||||||
import javax.management.MalformedObjectNameException;
|
import javax.management.MalformedObjectNameException;
|
||||||
import javax.management.ObjectName;
|
import javax.management.ObjectName;
|
||||||
import javax.management.remote.JMXConnector;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Static constants and utility methods to help work with
|
* Static constants and utility methods to help work with
|
||||||
@ -68,23 +66,6 @@ public class JMXNamespaces {
|
|||||||
NAMESPACE_SEPARATOR.length();
|
NAMESPACE_SEPARATOR.length();
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a connector connected to a sub name space exposed through
|
|
||||||
* the parent connector.
|
|
||||||
* @param parent the parent connector.
|
|
||||||
* @param namespace the {@linkplain javax.management.namespace name space}
|
|
||||||
* to which the returned connector is
|
|
||||||
* connected.
|
|
||||||
* @return A connector connected to a sub name space exposed through
|
|
||||||
* the parent connector.
|
|
||||||
**/
|
|
||||||
public static JMXConnector narrowToNamespace(final JMXConnector parent,
|
|
||||||
final String namespace)
|
|
||||||
throws IOException {
|
|
||||||
|
|
||||||
return JMXNamespaceUtils.cd(parent,namespace,true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@code MBeanServerConnection} proxy on a
|
* Creates a new {@code MBeanServerConnection} proxy on a
|
||||||
* {@linkplain javax.management.namespace sub name space}
|
* {@linkplain javax.management.namespace sub name space}
|
||||||
@ -96,15 +77,18 @@ public class JMXNamespaces {
|
|||||||
* name space} in which to narrow.
|
* name space} in which to narrow.
|
||||||
* @return A new {@code MBeanServerConnection} proxy that shows the content
|
* @return A new {@code MBeanServerConnection} proxy that shows the content
|
||||||
* of that name space.
|
* of that name space.
|
||||||
* @throws IllegalArgumentException if the name space does not exist, or
|
* @throws IllegalArgumentException if either argument is null,
|
||||||
* if a proxy for that name space cannot be created.
|
* or the name space does not exist, or if a proxy for that name space
|
||||||
|
* cannot be created. The {@linkplain Throwable#getCause() cause} of
|
||||||
|
* this exception will be an {@link InstanceNotFoundException} if and only
|
||||||
|
* if the name space is found not to exist.
|
||||||
*/
|
*/
|
||||||
public static MBeanServerConnection narrowToNamespace(
|
public static MBeanServerConnection narrowToNamespace(
|
||||||
MBeanServerConnection parent,
|
MBeanServerConnection parent,
|
||||||
String namespace) {
|
String namespace) {
|
||||||
if (LOG.isLoggable(Level.FINER))
|
if (LOG.isLoggable(Level.FINER))
|
||||||
LOG.finer("Making MBeanServerConnection for: " +namespace);
|
LOG.finer("Making MBeanServerConnection for: " +namespace);
|
||||||
return RoutingConnectionProxy.cd(parent,namespace);
|
return RoutingConnectionProxy.cd(parent, namespace, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -120,13 +104,15 @@ public class JMXNamespaces {
|
|||||||
* of that name space.
|
* of that name space.
|
||||||
* @throws IllegalArgumentException if either argument is null,
|
* @throws IllegalArgumentException if either argument is null,
|
||||||
* or the name space does not exist, or if a proxy for that name space
|
* or the name space does not exist, or if a proxy for that name space
|
||||||
* cannot be created.
|
* cannot be created. The {@linkplain Throwable#getCause() cause} of
|
||||||
|
* this exception will be an {@link InstanceNotFoundException} if and only
|
||||||
|
* if the name space is found not to exist.
|
||||||
*/
|
*/
|
||||||
public static MBeanServer narrowToNamespace(MBeanServer parent,
|
public static MBeanServer narrowToNamespace(MBeanServer parent,
|
||||||
String namespace) {
|
String namespace) {
|
||||||
if (LOG.isLoggable(Level.FINER))
|
if (LOG.isLoggable(Level.FINER))
|
||||||
LOG.finer("Making NamespaceServerProxy for: " +namespace);
|
LOG.finer("Making MBeanServer for: " +namespace);
|
||||||
return RoutingServerProxy.cd(parent,namespace);
|
return RoutingServerProxy.cd(parent, namespace, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -266,7 +252,7 @@ public class JMXNamespaces {
|
|||||||
ObjectNameRouter.normalizeNamespacePath(namespace,false,
|
ObjectNameRouter.normalizeNamespacePath(namespace,false,
|
||||||
true,false);
|
true,false);
|
||||||
try {
|
try {
|
||||||
// We could use Util.newObjectName here - but throwing an
|
// We could use ObjectName.valueOf here - but throwing an
|
||||||
// IllegalArgumentException that contains just the supplied
|
// IllegalArgumentException that contains just the supplied
|
||||||
// namespace instead of the whole ObjectName seems preferable.
|
// namespace instead of the whole ObjectName seems preferable.
|
||||||
return ObjectName.getInstance(sourcePath+
|
return ObjectName.getInstance(sourcePath+
|
||||||
|
@ -27,10 +27,10 @@ package javax.management.namespace;
|
|||||||
|
|
||||||
import com.sun.jmx.defaults.JmxProperties;
|
import com.sun.jmx.defaults.JmxProperties;
|
||||||
import com.sun.jmx.mbeanserver.Util;
|
import com.sun.jmx.mbeanserver.Util;
|
||||||
import com.sun.jmx.namespace.JMXNamespaceUtils;
|
|
||||||
import com.sun.jmx.remote.util.EnvHelp;
|
import com.sun.jmx.remote.util.EnvHelp;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
@ -39,6 +39,7 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import javax.management.AttributeChangeNotification;
|
import javax.management.AttributeChangeNotification;
|
||||||
|
|
||||||
|
import javax.management.ClientContext;
|
||||||
import javax.management.InstanceNotFoundException;
|
import javax.management.InstanceNotFoundException;
|
||||||
import javax.management.ListenerNotFoundException;
|
import javax.management.ListenerNotFoundException;
|
||||||
import javax.management.MBeanNotificationInfo;
|
import javax.management.MBeanNotificationInfo;
|
||||||
@ -220,17 +221,26 @@ public class JMXRemoteNamespace
|
|||||||
initParentOnce(this);
|
initParentOnce(this);
|
||||||
|
|
||||||
// URL must not be null.
|
// URL must not be null.
|
||||||
this.jmxURL = JMXNamespaceUtils.checkNonNull(sourceURL,"url");
|
if (sourceURL == null)
|
||||||
|
throw new IllegalArgumentException("Null URL");
|
||||||
|
this.jmxURL = sourceURL;
|
||||||
this.broadcaster =
|
this.broadcaster =
|
||||||
new NotificationBroadcasterSupport(connectNotification);
|
new NotificationBroadcasterSupport(connectNotification);
|
||||||
|
|
||||||
// handles options
|
// handles options
|
||||||
this.optionsMap = JMXNamespaceUtils.unmodifiableMap(optionsMap);
|
this.optionsMap = unmodifiableMap(optionsMap);
|
||||||
|
|
||||||
// handles (dis)connection events
|
// handles (dis)connection events
|
||||||
this.listener = new ConnectionListener();
|
this.listener = new ConnectionListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns un unmodifiable view of a map.
|
||||||
|
private static <K,V> Map<K,V> unmodifiableMap(Map<K,V> aMap) {
|
||||||
|
if (aMap == null || aMap.isEmpty())
|
||||||
|
return Collections.emptyMap();
|
||||||
|
return Collections.unmodifiableMap(aMap);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@code JMXServiceURL} that is (or will be) used to
|
* Returns the {@code JMXServiceURL} that is (or will be) used to
|
||||||
* connect to the remote name space. <p>
|
* connect to the remote name space. <p>
|
||||||
@ -483,106 +493,171 @@ public class JMXRemoteNamespace
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JMXConnector connect(JMXServiceURL url, Map<String,?> env)
|
private JMXConnector connect(JMXServiceURL url, Map<String,?> env)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
final JMXConnector c = newJMXConnector(jmxURL, env);
|
final JMXConnector c = newJMXConnector(url, env);
|
||||||
c.connect(env);
|
c.connect(env);
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new JMXConnector with the specified {@code url} and
|
* <p>Creates a new JMXConnector with the specified {@code url} and
|
||||||
* {@code env} options map.
|
* {@code env} options map. The default implementation of this method
|
||||||
* <p>
|
* returns {@link JMXConnectorFactory#newJMXConnector
|
||||||
* This method first calls {@link JMXConnectorFactory#newJMXConnector
|
* JMXConnectorFactory.newJMXConnector(jmxURL, env)}. Subclasses can
|
||||||
* JMXConnectorFactory.newJMXConnector(jmxURL, env)} to obtain a new
|
* override this method to customize behavior.</p>
|
||||||
* JMX connector, and returns that.
|
*
|
||||||
* </p>
|
|
||||||
* <p>
|
|
||||||
* A subclass of {@link JMXRemoteNamespace} can provide an implementation
|
|
||||||
* that connects to a sub namespace of the remote server by subclassing
|
|
||||||
* this class in the following way:
|
|
||||||
* <pre>
|
|
||||||
* class JMXRemoteSubNamespace extends JMXRemoteNamespace {
|
|
||||||
* private final String subnamespace;
|
|
||||||
* JMXRemoteSubNamespace(JMXServiceURL url,
|
|
||||||
* Map{@code <String,?>} env, String subnamespace) {
|
|
||||||
* super(url,options);
|
|
||||||
* this.subnamespace = subnamespace;
|
|
||||||
* }
|
|
||||||
* protected JMXConnector newJMXConnector(JMXServiceURL url,
|
|
||||||
* Map<String,?> env) throws IOException {
|
|
||||||
* final JMXConnector inner = super.newJMXConnector(url,env);
|
|
||||||
* return {@link JMXNamespaces#narrowToNamespace(JMXConnector,String)
|
|
||||||
* JMXNamespaces.narrowToNamespace(inner,subnamespace)};
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* </pre>
|
|
||||||
* </p>
|
|
||||||
* <p>
|
|
||||||
* Some connectors, like the JMXMP connector server defined by the
|
|
||||||
* version 1.2 of the JMX API may not have been upgraded to use the
|
|
||||||
* new {@linkplain javax.management.event Event Service} defined in this
|
|
||||||
* version of the JMX API.
|
|
||||||
* <p>
|
|
||||||
* In that case, and if the remote server to which this JMXRemoteNamespace
|
|
||||||
* connects also contains namespaces, it may be necessary to configure
|
|
||||||
* explicitly an {@linkplain
|
|
||||||
* javax.management.event.EventClientDelegate#newForwarder()
|
|
||||||
* Event Client Forwarder} on the remote server side, and to force the use
|
|
||||||
* of an {@link EventClient} on this client side.
|
|
||||||
* <br>
|
|
||||||
* A subclass of {@link JMXRemoteNamespace} can provide an implementation
|
|
||||||
* of {@code newJMXConnector} that will force notification subscriptions
|
|
||||||
* to flow through an {@link EventClient} over a legacy protocol by
|
|
||||||
* overriding this method in the following way:
|
|
||||||
* </p>
|
|
||||||
* <pre>
|
|
||||||
* class JMXRemoteEventClientNamespace extends JMXRemoteNamespace {
|
|
||||||
* JMXRemoteSubNamespaceConnector(JMXServiceURL url,
|
|
||||||
* Map<String,?> env) {
|
|
||||||
* super(url,options);
|
|
||||||
* }
|
|
||||||
* protected JMXConnector newJMXConnector(JMXServiceURL url,
|
|
||||||
* Map<String,?> env) throws IOException {
|
|
||||||
* final JMXConnector inner = super.newJMXConnector(url,env);
|
|
||||||
* return {@link EventClient#withEventClient(
|
|
||||||
* JMXConnector) EventClient.withEventClient(inner)};
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* </pre>
|
|
||||||
* <p>
|
|
||||||
* Note that the remote server also needs to provide an {@link
|
|
||||||
* javax.management.event.EventClientDelegateMBean}: only configuring
|
|
||||||
* the client side (this object) is not enough.<br>
|
|
||||||
* In summary, this technique should be used if the remote server
|
|
||||||
* supports JMX namespaces, but uses a JMX Connector Server whose
|
|
||||||
* implementation does not transparently use the new Event Service
|
|
||||||
* (as would be the case with the JMXMPConnectorServer implementation
|
|
||||||
* from the reference implementation of the JMX Remote API 1.0
|
|
||||||
* specification).
|
|
||||||
* </p>
|
|
||||||
* @param url The JMXServiceURL of the remote server.
|
* @param url The JMXServiceURL of the remote server.
|
||||||
* @param optionsMap An unmodifiable options map that will be passed to the
|
* @param optionsMap An options map that will be passed to the
|
||||||
* {@link JMXConnectorFactory} when {@linkplain
|
* {@link JMXConnectorFactory} when {@linkplain
|
||||||
* JMXConnectorFactory#newJMXConnector creating} the
|
* JMXConnectorFactory#newJMXConnector creating} the
|
||||||
* {@link JMXConnector} that can connect to the remote source
|
* {@link JMXConnector} that can connect to the remote source
|
||||||
* MBean Server.
|
* MBean Server.
|
||||||
* @return An unconnected JMXConnector to use to connect to the remote
|
* @return A JMXConnector to use to connect to the remote server
|
||||||
* server
|
* @throws IOException if the connector could not be created.
|
||||||
* @throws java.io.IOException if the connector could not be created.
|
|
||||||
* @see JMXConnectorFactory#newJMXConnector(javax.management.remote.JMXServiceURL, java.util.Map)
|
* @see JMXConnectorFactory#newJMXConnector(javax.management.remote.JMXServiceURL, java.util.Map)
|
||||||
* @see #JMXRemoteNamespace
|
* @see #JMXRemoteNamespace
|
||||||
*/
|
*/
|
||||||
protected JMXConnector newJMXConnector(JMXServiceURL url,
|
protected JMXConnector newJMXConnector(JMXServiceURL url,
|
||||||
Map<String,?> optionsMap) throws IOException {
|
Map<String,?> optionsMap) throws IOException {
|
||||||
final JMXConnector c =
|
return JMXConnectorFactory.newJMXConnector(jmxURL, optionsMap);
|
||||||
JMXConnectorFactory.newJMXConnector(jmxURL, optionsMap);
|
|
||||||
// TODO: uncomment this when contexts are added
|
|
||||||
// return ClientContext.withDynamicContext(c);
|
|
||||||
return c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Called when a new connection is established using {@link #connect}
|
||||||
|
* so that subclasses can customize the connection. The default
|
||||||
|
* implementation of this method effectively does the following:</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* MBeanServerConnection mbsc = {@link JMXConnector#getMBeanServerConnection()
|
||||||
|
* jmxc.getMBeanServerConnection()};
|
||||||
|
* try {
|
||||||
|
* return {@link ClientContext#withDynamicContext
|
||||||
|
* ClientContext.withDynamicContext(mbsc)};
|
||||||
|
* } catch (IllegalArgumentException e) {
|
||||||
|
* return mbsc;
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>In other words, it arranges for the client context to be forwarded
|
||||||
|
* to the remote MBean Server if the remote MBean Server supports contexts;
|
||||||
|
* otherwise it ignores the client context.</p>
|
||||||
|
*
|
||||||
|
* <h4>Example: connecting to a remote namespace</h4>
|
||||||
|
*
|
||||||
|
* <p>A subclass that wanted to narrow into a namespace of
|
||||||
|
* the remote MBeanServer might look like this:</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* class JMXRemoteSubNamespace extends JMXRemoteNamespace {
|
||||||
|
* private final String subnamespace;
|
||||||
|
*
|
||||||
|
* JMXRemoteSubNamespace(
|
||||||
|
* JMXServiceURL url, Map{@code <String, ?>} env, String subnamespace) {
|
||||||
|
* super(url, env);
|
||||||
|
* this.subnamespace = subnamespace;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* {@code @Override}
|
||||||
|
* protected MBeanServerConnection getMBeanServerConnection(
|
||||||
|
* JMXConnector jmxc) throws IOException {
|
||||||
|
* MBeanServerConnection mbsc = super.getMBeanServerConnection(jmxc);
|
||||||
|
* return {@link JMXNamespaces#narrowToNamespace(MBeanServerConnection,String)
|
||||||
|
* JMXNamespaces.narrowToNamespace(mbsc, subnamespace)};
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <h4>Example: using the Event Service for notifications</h4>
|
||||||
|
*
|
||||||
|
* <p>Some connectors may have been designed to work with an earlier
|
||||||
|
* version of the JMX API, and may not have been upgraded to use
|
||||||
|
* the {@linkplain javax.management.event Event Service} defined in
|
||||||
|
* this version of the JMX API. In that case, and if the remote
|
||||||
|
* server to which this JMXRemoteNamespace connects also contains
|
||||||
|
* namespaces, it may be necessary to configure explicitly an {@linkplain
|
||||||
|
* javax.management.event.EventClientDelegate#newForwarder Event Client
|
||||||
|
* Forwarder} on the remote server side, and to force the use of an {@link
|
||||||
|
* EventClient} on this client side.</p>
|
||||||
|
*
|
||||||
|
* <p>A subclass of {@link JMXRemoteNamespace} can provide an
|
||||||
|
* implementation of {@code getMBeanServerConnection} that will force
|
||||||
|
* notification subscriptions to flow through an {@link EventClient} over
|
||||||
|
* a legacy protocol. It can do so by overriding this method in the
|
||||||
|
* following way:</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* class JMXRemoteEventClientNamespace extends JMXRemoteNamespace {
|
||||||
|
* JMXRemoteEventClientNamespace(JMXServiceURL url, {@code Map<String,?>} env) {
|
||||||
|
* super(url, env);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* {@code @Override}
|
||||||
|
* protected MBeanServerConnection getMBeanServerConnection(JMXConnector jmxc)
|
||||||
|
* throws IOException {
|
||||||
|
* MBeanServerConnection mbsc = super.getMBeanServerConnection(jmxc);
|
||||||
|
* return EventClient.getEventClientConnection(mbsc);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Note that the remote server also needs to provide an {@link
|
||||||
|
* javax.management.event.EventClientDelegateMBean}: configuring only
|
||||||
|
* the client side (this object) is not enough.</p>
|
||||||
|
*
|
||||||
|
* <p>In summary, this technique should be used if the remote server
|
||||||
|
* supports JMX namespaces, but uses a JMX Connector Server whose
|
||||||
|
* implementation does not transparently use the new Event Service
|
||||||
|
* (as would be the case with the JMXMPConnectorServer implementation
|
||||||
|
* from the reference implementation of the JMX Remote API 1.0
|
||||||
|
* specification).</p>
|
||||||
|
*
|
||||||
|
* @param jmxc the newly-created {@code JMXConnector}.
|
||||||
|
*
|
||||||
|
* @return an {@code MBeanServerConnection} connected to the remote
|
||||||
|
* MBeanServer.
|
||||||
|
*
|
||||||
|
* @throws IOException if the connection cannot be made. If this method
|
||||||
|
* throws {@code IOException} then the calling {@link #connect()} method
|
||||||
|
* will also fail with an {@code IOException}.
|
||||||
|
*
|
||||||
|
* @see #connect
|
||||||
|
*/
|
||||||
|
protected MBeanServerConnection getMBeanServerConnection(JMXConnector jmxc)
|
||||||
|
throws IOException {
|
||||||
|
final MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
|
||||||
|
try {
|
||||||
|
return ClientContext.withDynamicContext(mbsc);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
LOG.log(Level.FINER, "ClientContext.withDynamicContext", e);
|
||||||
|
return mbsc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>The sequence of events when this method is called includes,
|
||||||
|
* effectively, the following code:</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* JMXServiceURL url = {@link #getJMXServiceURL getJMXServiceURL}();
|
||||||
|
* JMXConnector jmxc = {@link #newJMXConnector newJMXConnector}(url, env);
|
||||||
|
* jmxc.connect();
|
||||||
|
* MBeanServerConnection mbsc = {@link #getMBeanServerConnection(JMXConnector)
|
||||||
|
* getMBeanServerConnection}(jmxc);
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>Here, {@code env} is a {@code Map} containing the entries from the
|
||||||
|
* {@code optionsMap} that was passed to the {@linkplain #JMXRemoteNamespace
|
||||||
|
* constructor} or to the {@link #newJMXRemoteNamespace newJMXRemoteNamespace}
|
||||||
|
* factory method.</p>
|
||||||
|
*
|
||||||
|
* <p>Subclasses can customize connection behavior by overriding the
|
||||||
|
* {@code getJMXServiceURL}, {@code newJMXConnector}, or
|
||||||
|
* {@code getMBeanServerConnection} methods.</p>
|
||||||
|
*/
|
||||||
public void connect() throws IOException {
|
public void connect() throws IOException {
|
||||||
LOG.fine("connecting...");
|
LOG.fine("connecting...");
|
||||||
final Map<String,Object> env =
|
final Map<String,Object> env =
|
||||||
@ -590,7 +665,7 @@ public class JMXRemoteNamespace
|
|||||||
try {
|
try {
|
||||||
// XXX: We should probably document this...
|
// XXX: We should probably document this...
|
||||||
// This allows to specify a loader name - which will be
|
// This allows to specify a loader name - which will be
|
||||||
// retrieved from the paret MBeanServer.
|
// retrieved from the parent MBeanServer.
|
||||||
defaultClassLoader =
|
defaultClassLoader =
|
||||||
EnvHelp.resolveServerClassLoader(env,getMBeanServer());
|
EnvHelp.resolveServerClassLoader(env,getMBeanServer());
|
||||||
} catch (InstanceNotFoundException x) {
|
} catch (InstanceNotFoundException x) {
|
||||||
@ -604,7 +679,7 @@ public class JMXRemoteNamespace
|
|||||||
final JMXConnector aconn = connect(url,env);
|
final JMXConnector aconn = connect(url,env);
|
||||||
final MBeanServerConnection msc;
|
final MBeanServerConnection msc;
|
||||||
try {
|
try {
|
||||||
msc = aconn.getMBeanServerConnection();
|
msc = getMBeanServerConnection(aconn);
|
||||||
aconn.addConnectionNotificationListener(listener,null,aconn);
|
aconn.addConnectionNotificationListener(listener,null,aconn);
|
||||||
} catch (IOException io) {
|
} catch (IOException io) {
|
||||||
close(aconn);
|
close(aconn);
|
||||||
|
@ -322,10 +322,12 @@ public class JMXConnectorFactory {
|
|||||||
JMXConnectorProvider.class;
|
JMXConnectorProvider.class;
|
||||||
final String protocol = serviceURL.getProtocol();
|
final String protocol = serviceURL.getProtocol();
|
||||||
final String providerClassName = "ClientProvider";
|
final String providerClassName = "ClientProvider";
|
||||||
|
final JMXServiceURL providerURL = serviceURL;
|
||||||
|
|
||||||
JMXConnectorProvider provider =
|
JMXConnectorProvider provider = getProvider(providerURL, envcopy,
|
||||||
getProvider(serviceURL, envcopy, providerClassName,
|
providerClassName,
|
||||||
targetInterface, loader);
|
targetInterface,
|
||||||
|
loader);
|
||||||
|
|
||||||
IOException exception = null;
|
IOException exception = null;
|
||||||
if (provider == null) {
|
if (provider == null) {
|
||||||
@ -336,7 +338,7 @@ public class JMXConnectorFactory {
|
|||||||
if (loader != null) {
|
if (loader != null) {
|
||||||
try {
|
try {
|
||||||
JMXConnector connection =
|
JMXConnector connection =
|
||||||
getConnectorAsService(loader, serviceURL, envcopy);
|
getConnectorAsService(loader, providerURL, envcopy);
|
||||||
if (connection != null)
|
if (connection != null)
|
||||||
return connection;
|
return connection;
|
||||||
} catch (JMXProviderException e) {
|
} catch (JMXProviderException e) {
|
||||||
@ -345,8 +347,7 @@ public class JMXConnectorFactory {
|
|||||||
exception = e;
|
exception = e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
provider =
|
provider = getProvider(protocol, PROTOCOL_PROVIDER_DEFAULT_PACKAGE,
|
||||||
getProvider(protocol, PROTOCOL_PROVIDER_DEFAULT_PACKAGE,
|
|
||||||
JMXConnectorFactory.class.getClassLoader(),
|
JMXConnectorFactory.class.getClassLoader(),
|
||||||
providerClassName, targetInterface);
|
providerClassName, targetInterface);
|
||||||
}
|
}
|
||||||
@ -448,9 +449,10 @@ public class JMXConnectorFactory {
|
|||||||
getProviderIterator(JMXConnectorProvider.class, loader);
|
getProviderIterator(JMXConnectorProvider.class, loader);
|
||||||
JMXConnector connection;
|
JMXConnector connection;
|
||||||
IOException exception = null;
|
IOException exception = null;
|
||||||
while(providers.hasNext()) {
|
while (providers.hasNext()) {
|
||||||
|
JMXConnectorProvider provider = providers.next();
|
||||||
try {
|
try {
|
||||||
connection = providers.next().newJMXConnector(url, map);
|
connection = provider.newJMXConnector(url, map);
|
||||||
return connection;
|
return connection;
|
||||||
} catch (JMXProviderException e) {
|
} catch (JMXProviderException e) {
|
||||||
throw e;
|
throw e;
|
||||||
@ -553,4 +555,5 @@ public class JMXConnectorFactory {
|
|||||||
private static String protocol2package(String protocol) {
|
private static String protocol2package(String protocol) {
|
||||||
return protocol.replace('+', '.').replace('-', '_');
|
return protocol.replace('+', '.').replace('-', '_');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
|
import javax.management.ClientContext;
|
||||||
import javax.management.MBeanInfo; // for javadoc
|
import javax.management.MBeanInfo; // for javadoc
|
||||||
import javax.management.MBeanNotificationInfo;
|
import javax.management.MBeanNotificationInfo;
|
||||||
import javax.management.MBeanRegistration;
|
import javax.management.MBeanRegistration;
|
||||||
@ -101,6 +102,56 @@ public abstract class JMXConnectorServer
|
|||||||
public static final String DELEGATE_TO_EVENT_SERVICE =
|
public static final String DELEGATE_TO_EVENT_SERVICE =
|
||||||
"jmx.remote.delegate.event.service";
|
"jmx.remote.delegate.event.service";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Name of the attribute that specifies whether this connector
|
||||||
|
* server allows clients to communicate a context with each request.
|
||||||
|
* The value associated with this attribute, if any, must be a string
|
||||||
|
* that is equal to {@code "true"} or {@code "false"}, ignoring case.
|
||||||
|
* If it is {@code "true"}, then the connector server will simulate
|
||||||
|
* a namespace {@code jmx.context//}, as described in
|
||||||
|
* {@link ClientContext#newContextForwarder}. This namespace is needed
|
||||||
|
* for {@link ClientContext#withContext ClientContext.withContext} to
|
||||||
|
* function correctly.</p>
|
||||||
|
*
|
||||||
|
* <p>Not all connector servers will understand this attribute, but the
|
||||||
|
* standard {@linkplain javax.management.remote.rmi.RMIConnectorServer
|
||||||
|
* RMI Connector Server} does. For a connector server that understands
|
||||||
|
* this attribute, the default value is {@code "true"}.</p>
|
||||||
|
*
|
||||||
|
* @since 1.7
|
||||||
|
*/
|
||||||
|
public static final String CONTEXT_FORWARDER =
|
||||||
|
"jmx.remote.context.forwarder";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Name of the attribute that specifies whether this connector server
|
||||||
|
* localizes the descriptions in the {@link MBeanInfo} object returned by
|
||||||
|
* {@link MBeanServer#getMBeanInfo MBeanServer.getMBeanInfo}, based on the
|
||||||
|
* locale communicated by the client.</p>
|
||||||
|
*
|
||||||
|
* <p>The value associated with this attribute, if any, must be a string
|
||||||
|
* that is equal to {@code "true"} or {@code "false"}, ignoring case.
|
||||||
|
* If it is {@code "true"}, then the connector server will localize
|
||||||
|
* {@code MBeanInfo} descriptions as specified in {@link
|
||||||
|
* ClientContext#newLocalizeMBeanInfoForwarder}.</p>
|
||||||
|
*
|
||||||
|
* <p>Not all connector servers will understand this attribute, but the
|
||||||
|
* standard {@linkplain javax.management.remote.rmi.RMIConnectorServer
|
||||||
|
* RMI Connector Server} does. For a connector server that understands
|
||||||
|
* this attribute, the default value is {@code "false"}.</p>
|
||||||
|
*
|
||||||
|
* <p>Because localization requires the client to be able to communicate
|
||||||
|
* its locale, it does not make sense to specify this attribute as
|
||||||
|
* {@code "true"} if {@link #CONTEXT_FORWARDER} is not also {@code "true"}.
|
||||||
|
* For a connector server that understands these attributes, specifying
|
||||||
|
* this inconsistent combination will result in an {@link
|
||||||
|
* IllegalArgumentException}.</p>
|
||||||
|
*
|
||||||
|
* @since 1.7
|
||||||
|
*/
|
||||||
|
public static final String LOCALIZE_MBEAN_INFO_FORWARDER =
|
||||||
|
"jmx.remote.localize.mbean.info";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Name of the attribute that specifies whether this connector
|
* <p>Name of the attribute that specifies whether this connector
|
||||||
* server simulates the existence of the {@link EventClientDelegate}
|
* server simulates the existence of the {@link EventClientDelegate}
|
||||||
@ -155,7 +206,7 @@ public abstract class JMXConnectorServer
|
|||||||
* to, or null if it is not yet attached to an MBean server.
|
* to, or null if it is not yet attached to an MBean server.
|
||||||
*
|
*
|
||||||
* @see #setMBeanServerForwarder
|
* @see #setMBeanServerForwarder
|
||||||
* @see #getSystemMBeanServer
|
* @see #getSystemMBeanServerForwarder
|
||||||
*/
|
*/
|
||||||
public synchronized MBeanServer getMBeanServer() {
|
public synchronized MBeanServer getMBeanServer() {
|
||||||
return userMBeanServer;
|
return userMBeanServer;
|
||||||
@ -176,30 +227,36 @@ public abstract class JMXConnectorServer
|
|||||||
* this method, the first occurrence in the chain of an object that is
|
* this method, the first occurrence in the chain of an object that is
|
||||||
* {@linkplain Object#equals equal} to {@code mbsf} will have been
|
* {@linkplain Object#equals equal} to {@code mbsf} will have been
|
||||||
* removed.</p>
|
* removed.</p>
|
||||||
|
*
|
||||||
* @param mbsf the forwarder to remove
|
* @param mbsf the forwarder to remove
|
||||||
|
*
|
||||||
* @throws NoSuchElementException if there is no occurrence of {@code mbsf}
|
* @throws NoSuchElementException if there is no occurrence of {@code mbsf}
|
||||||
* in the chain.
|
* in the chain.
|
||||||
* @throws IllegalArgumentException if {@code mbsf} is null.
|
* @throws IllegalArgumentException if {@code mbsf} is null or is the
|
||||||
|
* {@linkplain #getSystemMBeanServerForwarder() system forwarder}.
|
||||||
|
*
|
||||||
|
* @since 1.7
|
||||||
*/
|
*/
|
||||||
public synchronized void removeMBeanServerForwarder(MBeanServerForwarder mbsf) {
|
public synchronized void removeMBeanServerForwarder(MBeanServerForwarder mbsf) {
|
||||||
if (mbsf == null)
|
if (mbsf == null)
|
||||||
throw new IllegalArgumentException("Invalid null argument: mbsf");
|
throw new IllegalArgumentException("Invalid null argument: mbsf");
|
||||||
|
if (systemMBeanServerForwarder.equals(mbsf))
|
||||||
|
throw new IllegalArgumentException("Cannot remove system forwarder");
|
||||||
|
|
||||||
MBeanServerForwarder prev = null;
|
MBeanServerForwarder prev = systemMBeanServerForwarder;
|
||||||
MBeanServer curr = systemMBeanServer;
|
MBeanServer curr;
|
||||||
while (curr instanceof MBeanServerForwarder && !mbsf.equals(curr)) {
|
while (true) {
|
||||||
prev = (MBeanServerForwarder) curr;
|
|
||||||
curr = prev.getMBeanServer();
|
curr = prev.getMBeanServer();
|
||||||
|
if (mbsf.equals(curr))
|
||||||
|
break;
|
||||||
|
if (curr instanceof MBeanServerForwarder)
|
||||||
|
prev = (MBeanServerForwarder) curr;
|
||||||
|
else
|
||||||
|
throw new NoSuchElementException("MBeanServerForwarder not in chain");
|
||||||
}
|
}
|
||||||
if (!(curr instanceof MBeanServerForwarder))
|
MBeanServer next = mbsf.getMBeanServer();
|
||||||
throw new NoSuchElementException("MBeanServerForwarder not in chain");
|
prev.setMBeanServer(next);
|
||||||
MBeanServerForwarder deleted = (MBeanServerForwarder) curr;
|
if (userMBeanServer == mbsf)
|
||||||
MBeanServer next = deleted.getMBeanServer();
|
|
||||||
if (prev != null)
|
|
||||||
prev.setMBeanServer(next);
|
|
||||||
if (systemMBeanServer == deleted)
|
|
||||||
systemMBeanServer = next;
|
|
||||||
if (userMBeanServer == deleted)
|
|
||||||
userMBeanServer = next;
|
userMBeanServer = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,66 +266,63 @@ public abstract class JMXConnectorServer
|
|||||||
* the systemMBeanServer and userMBeanServer field declarations.
|
* the systemMBeanServer and userMBeanServer field declarations.
|
||||||
*/
|
*/
|
||||||
private void insertUserMBeanServer(MBeanServer mbs) {
|
private void insertUserMBeanServer(MBeanServer mbs) {
|
||||||
MBeanServerForwarder lastSystemMBSF = null;
|
MBeanServerForwarder lastSystemMBSF = systemMBeanServerForwarder;
|
||||||
for (MBeanServer mbsi = systemMBeanServer;
|
while (true) {
|
||||||
mbsi != userMBeanServer;
|
MBeanServer mbsi = lastSystemMBSF.getMBeanServer();
|
||||||
mbsi = lastSystemMBSF.getMBeanServer()) {
|
if (mbsi == userMBeanServer)
|
||||||
|
break;
|
||||||
lastSystemMBSF = (MBeanServerForwarder) mbsi;
|
lastSystemMBSF = (MBeanServerForwarder) mbsi;
|
||||||
}
|
}
|
||||||
userMBeanServer = mbs;
|
userMBeanServer = mbs;
|
||||||
if (lastSystemMBSF == null)
|
lastSystemMBSF.setMBeanServer(mbs);
|
||||||
systemMBeanServer = mbs;
|
|
||||||
else
|
|
||||||
lastSystemMBSF.setMBeanServer(mbs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Returns the first item in the chain of system and then user
|
* <p>Returns the first item in the chain of system and then user
|
||||||
* forwarders. In the simplest case, a {@code JMXConnectorServer}
|
* forwarders. There is a chain of {@link MBeanServerForwarder}s between
|
||||||
* is connected directly to an {@code MBeanServer}. But there can
|
* a {@code JMXConnectorServer} and its {@code MBeanServer}. This chain
|
||||||
* also be a chain of {@link MBeanServerForwarder}s between the two.
|
* consists of two sub-chains: first the <em>system chain</em> and then
|
||||||
* This chain consists of two sub-chains: first the <em>system chain</em>
|
* the <em>user chain</em>. Incoming requests are given to the first
|
||||||
* and then the <em>user chain</em>. Incoming requests are given to the
|
* forwarder in the system chain. Each forwarder can handle a request
|
||||||
* first forwarder in the system chain. Each forwarder can handle
|
* itself, or more usually forward it to the next forwarder, perhaps with
|
||||||
* a request itself, or more usually forward it to the next forwarder,
|
* some extra behavior such as logging or security checking before or after
|
||||||
* perhaps with some extra behavior such as logging or security
|
* the forwarding. The last forwarder in the system chain is followed by
|
||||||
* checking before or after the forwarding. The last forwarder in
|
* the first forwarder in the user chain.</p>
|
||||||
* the system chain is followed by the first forwarder in the user
|
|
||||||
* chain.</p>
|
|
||||||
*
|
*
|
||||||
* <p>The <em>system chain</em> is usually
|
* <p>The object returned by this method is the first forwarder in the
|
||||||
* defined by a connector server based on the environment Map;
|
* system chain. For a given {@code JMXConnectorServer}, this method
|
||||||
* see {@link JMXConnectorServerFactory#newJMXConnectorServer}. Allowing the
|
* always returns the same object, which simply forwards every request
|
||||||
* connector server to define its forwarders in this way ensures that
|
* to the next object in the chain.</p>
|
||||||
* they are in the correct order - some forwarders need to be inserted
|
*
|
||||||
* before others for correct behavior. It is possible to modify the
|
* <p>Not all connector servers support a system chain of forwarders,
|
||||||
* system chain, for example using {@link #setSystemMBeanServerForwarder} or
|
* although the standard {@linkplain
|
||||||
* {@link #removeMBeanServerForwarder}, but in that case the system
|
* javax.management.remote.rmi.RMIConnectorServer RMI connector
|
||||||
* chain is no longer guaranteed to be correct.</p>
|
* server} does. For those that do not, this method will throw {@code
|
||||||
|
* UnsupportedOperationException}. All
|
||||||
|
* connector servers do support a user chain of forwarders.</p>
|
||||||
|
*
|
||||||
|
* <p>The <em>system chain</em> is usually defined by a
|
||||||
|
* connector server based on the environment Map; see {@link
|
||||||
|
* JMXConnectorServerFactory#newJMXConnectorServer
|
||||||
|
* JMXConnectorServerFactory.newJMXConnectorServer}. Allowing
|
||||||
|
* the connector server to define its forwarders in this way
|
||||||
|
* ensures that they are in the correct order - some forwarders
|
||||||
|
* need to be inserted before others for correct behavior. It is
|
||||||
|
* possible to modify the system chain, for example using {@code
|
||||||
|
* connectorServer.getSystemMBeanServerForwarder().setMBeanServer(mbsf)} or
|
||||||
|
* {@link #removeMBeanServerForwarder removeMBeanServerForwarder}, but in
|
||||||
|
* that case the system chain is no longer guaranteed to be correct.</p>
|
||||||
*
|
*
|
||||||
* <p>The <em>user chain</em> is defined by calling {@link
|
* <p>The <em>user chain</em> is defined by calling {@link
|
||||||
* #setMBeanServerForwarder} to insert forwarders at the head of the user
|
* #setMBeanServerForwarder setMBeanServerForwarder} to insert forwarders
|
||||||
* chain.</p>
|
* at the head of the user chain.</p>
|
||||||
*
|
|
||||||
* <p>If there are no forwarders in either chain, then both
|
|
||||||
* {@link #getMBeanServer()} and {@code getSystemMBeanServer()} will
|
|
||||||
* return the {@code MBeanServer} for this connector server. If there
|
|
||||||
* are forwarders in the user chain but not the system chain, then
|
|
||||||
* both methods will return the first forwarder in the user chain.
|
|
||||||
* If there are forwarders in the system chain but not the user chain,
|
|
||||||
* then {@code getSystemMBeanServer()} will return the first forwarder
|
|
||||||
* in the system chain, and {@code getMBeanServer()} will return the
|
|
||||||
* {@code MBeanServer} for this connector server. Finally, if there
|
|
||||||
* are forwarders in each chain then {@code getSystemMBeanServer()}
|
|
||||||
* will return the first forwarder in the system chain, and {@code
|
|
||||||
* getMBeanServer()} will return the first forwarder in the user chain.</p>
|
|
||||||
*
|
*
|
||||||
* <p>This code illustrates how the chains can be traversed:</p>
|
* <p>This code illustrates how the chains can be traversed:</p>
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* JMXConnectorServer cs;
|
* JMXConnectorServer cs;
|
||||||
* System.out.println("system chain:");
|
* System.out.println("system chain:");
|
||||||
* MBeanServer mbs = cs.getSystemMBeanServer();
|
* MBeanServer mbs = cs.getSystemMBeanServerForwarder();
|
||||||
* while (true) {
|
* while (true) {
|
||||||
* if (mbs == cs.getMBeanServer())
|
* if (mbs == cs.getMBeanServer())
|
||||||
* System.out.println("user chain:");
|
* System.out.println("user chain:");
|
||||||
@ -281,65 +335,40 @@ public abstract class JMXConnectorServer
|
|||||||
* System.out.println("--MBean Server");
|
* System.out.println("--MBean Server");
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
|
* <h4>Note for connector server implementors</h4>
|
||||||
|
*
|
||||||
|
* <p>Existing connector server implementations can be updated to support
|
||||||
|
* a system chain of forwarders as follows:</p>
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li><p>Override the {@link #supportsSystemMBeanServerForwarder()}
|
||||||
|
* method so that it returns true.</p>
|
||||||
|
*
|
||||||
|
* <li><p>Call {@link #installStandardForwarders} from the constructor of
|
||||||
|
* the connector server.</p>
|
||||||
|
*
|
||||||
|
* <li><p>Direct incoming requests to the result of {@link
|
||||||
|
* #getSystemMBeanServerForwarder()} instead of the result of {@link
|
||||||
|
* #getMBeanServer()}.</p>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
* @return the first item in the system chain of forwarders.
|
* @return the first item in the system chain of forwarders.
|
||||||
*
|
*
|
||||||
* @see #setSystemMBeanServerForwarder
|
* @throws UnsupportedOperationException if {@link
|
||||||
|
* #supportsSystemMBeanServerForwarder} returns false.
|
||||||
|
*
|
||||||
|
* @see #supportsSystemMBeanServerForwarder
|
||||||
|
* @see #setMBeanServerForwarder
|
||||||
|
*
|
||||||
|
* @since 1.7
|
||||||
*/
|
*/
|
||||||
public synchronized MBeanServer getSystemMBeanServer() {
|
public MBeanServerForwarder getSystemMBeanServerForwarder() {
|
||||||
return systemMBeanServer;
|
if (!supportsSystemMBeanServerForwarder()) {
|
||||||
}
|
throw new UnsupportedOperationException(
|
||||||
|
"System MBeanServerForwarder not supported by this " +
|
||||||
/**
|
"connector server");
|
||||||
* <p>Inserts an object that intercepts requests for the MBean server
|
}
|
||||||
* that arrive through this connector server. This object will be
|
return systemMBeanServerForwarder;
|
||||||
* supplied as the <code>MBeanServer</code> for any new connection
|
|
||||||
* created by this connector server. Existing connections are
|
|
||||||
* unaffected.</p>
|
|
||||||
*
|
|
||||||
* <p>This method can be called more than once with different
|
|
||||||
* {@link MBeanServerForwarder} objects. The result is a chain
|
|
||||||
* of forwarders. The last forwarder added is the first in the chain.</p>
|
|
||||||
*
|
|
||||||
* <p>This method modifies the system chain of {@link MBeanServerForwarder}s.
|
|
||||||
* Usually user code should change the user chain instead, via
|
|
||||||
* {@link #setMBeanServerForwarder}.</p>
|
|
||||||
*
|
|
||||||
* <p>Not all connector servers support a system chain of forwarders.
|
|
||||||
* Calling this method on a connector server that does not will produce an
|
|
||||||
* {@link UnsupportedOperationException}.</p>
|
|
||||||
*
|
|
||||||
* <p>Suppose {@code mbs} is the result of {@link #getSystemMBeanServer()}
|
|
||||||
* before calling this method. If {@code mbs} is not null, then
|
|
||||||
* {@code mbsf.setMBeanServer(mbs)} will be called. If doing so
|
|
||||||
* produces an exception, this method throws the same exception without
|
|
||||||
* any other effect. If {@code mbs} is null, or if the call to
|
|
||||||
* {@code mbsf.setMBeanServer(mbs)} succeeds, then this method will
|
|
||||||
* return normally and {@code getSystemMBeanServer()} will then return
|
|
||||||
* {@code mbsf}.</p>
|
|
||||||
*
|
|
||||||
* <p>The result of {@link #getMBeanServer()} is unchanged by this method.</p>
|
|
||||||
*
|
|
||||||
* @param mbsf the new <code>MBeanServerForwarder</code>.
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException if the call to {@link
|
|
||||||
* MBeanServerForwarder#setMBeanServer mbsf.setMBeanServer} fails
|
|
||||||
* with <code>IllegalArgumentException</code>, or if
|
|
||||||
* <code>mbsf</code> is null.
|
|
||||||
*
|
|
||||||
* @throws UnsupportedOperationException if
|
|
||||||
* {@link #supportsSystemMBeanServerForwarder} returns false.
|
|
||||||
*
|
|
||||||
* @see #getSystemMBeanServer()
|
|
||||||
*/
|
|
||||||
public synchronized void setSystemMBeanServerForwarder(
|
|
||||||
MBeanServerForwarder mbsf) {
|
|
||||||
if (mbsf == null)
|
|
||||||
throw new IllegalArgumentException("Invalid null argument: mbsf");
|
|
||||||
mustSupportSystemMBSF();
|
|
||||||
|
|
||||||
if (systemMBeanServer != null)
|
|
||||||
mbsf.setMBeanServer(systemMBeanServer);
|
|
||||||
systemMBeanServer = mbsf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -350,19 +379,13 @@ public abstract class JMXConnectorServer
|
|||||||
*
|
*
|
||||||
* @return true if this connector server supports the system chain of
|
* @return true if this connector server supports the system chain of
|
||||||
* forwarders.
|
* forwarders.
|
||||||
|
*
|
||||||
|
* @since 1.7
|
||||||
*/
|
*/
|
||||||
public boolean supportsSystemMBeanServerForwarder() {
|
public boolean supportsSystemMBeanServerForwarder() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mustSupportSystemMBSF() {
|
|
||||||
if (!supportsSystemMBeanServerForwarder()) {
|
|
||||||
throw new UnsupportedOperationException(
|
|
||||||
"System MBeanServerForwarder not supported by this " +
|
|
||||||
"connector server");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Install {@link MBeanServerForwarder}s in the system chain
|
* <p>Install {@link MBeanServerForwarder}s in the system chain
|
||||||
* based on the attributes in the given {@code Map}. A connector
|
* based on the attributes in the given {@code Map}. A connector
|
||||||
@ -374,34 +397,90 @@ public abstract class JMXConnectorServer
|
|||||||
* <ul>
|
* <ul>
|
||||||
*
|
*
|
||||||
* <li>If {@link #EVENT_CLIENT_DELEGATE_FORWARDER} is absent, or is
|
* <li>If {@link #EVENT_CLIENT_DELEGATE_FORWARDER} is absent, or is
|
||||||
* present with the value {@code "true"}, then a forwarder with the
|
* present with the value {@code "true"}, then a forwarder
|
||||||
* functionality of {@link EventClientDelegate#newForwarder} is inserted
|
* equivalent to {@link EventClientDelegate#newForwarder
|
||||||
* at the start of the system chain.</li>
|
* EventClientDelegate.newForwarder}{@code (sysMBSF.getMBeanServer(),
|
||||||
|
* sysMBSF)} is inserted at the start of the system chain,
|
||||||
|
* where {@code sysMBSF} is the object returned by {@link
|
||||||
|
* #getSystemMBeanServerForwarder()}. </li>
|
||||||
|
*
|
||||||
|
* <li>If {@link #LOCALIZE_MBEAN_INFO_FORWARDER} is present with the
|
||||||
|
* value {@code "true"}, then a forwarder equivalent to
|
||||||
|
* {@link ClientContext#newLocalizeMBeanInfoForwarder
|
||||||
|
* ClientContext.newLocalizeMBeanInfoForwarder}{@code
|
||||||
|
* (sysMBSF.getMBeanServer())} is inserted at the start of the system
|
||||||
|
* chain.</li>
|
||||||
|
*
|
||||||
|
* <li>If {@link #CONTEXT_FORWARDER} is absent, or is present with
|
||||||
|
* the value {@code "true"}, then a forwarder equivalent to
|
||||||
|
* {@link ClientContext#newContextForwarder
|
||||||
|
* ClientContext.newContextForwarder}{@code (sysMSBF.getMBeanServer(),
|
||||||
|
* sysMBSF)} is inserted at the tart of the system chain.</li>
|
||||||
*
|
*
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* <p>For {@code EVENT_CLIENT_DELEGATE_FORWARDER}, if the
|
* <p>For {@code EVENT_CLIENT_DELEGATE_FORWARDER} and {@code
|
||||||
* attribute is absent from the {@code Map} and a system property
|
* CONTEXT_FORWARDER}, if the attribute is absent from the {@code
|
||||||
* of the same name is defined, then the value of the system
|
* Map} and a system property of the same name is defined, then
|
||||||
* property is used as if it were in the {@code Map}.
|
* the value of the system property is used as if it were in the
|
||||||
|
* {@code Map}.
|
||||||
|
*
|
||||||
|
* <p>Since each forwarder is inserted at the start of the chain,
|
||||||
|
* the final order of the forwarders is the <b>reverse</b> of the order
|
||||||
|
* above. This is important, because the {@code
|
||||||
|
* LOCALIZE_MBEAN_INFO_FORWARDER} can only work if the {@code
|
||||||
|
* CONTEXT_FORWARDER} has already installed the remote client's locale
|
||||||
|
* in the {@linkplain ClientContext#getContext context} of the current
|
||||||
|
* thread.</p>
|
||||||
*
|
*
|
||||||
* <p>Attributes in {@code env} that are not listed above are ignored
|
* <p>Attributes in {@code env} that are not listed above are ignored
|
||||||
* by this method.</p>
|
* by this method.</p>
|
||||||
*
|
*
|
||||||
* @throws UnsupportedOperationException if {@link
|
* @throws UnsupportedOperationException if {@link
|
||||||
* #supportsSystemMBeanServerForwarder} is false.
|
* #supportsSystemMBeanServerForwarder} is false.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if the relevant attributes in {@code env} are
|
||||||
|
* inconsistent, for example if {@link #LOCALIZE_MBEAN_INFO_FORWARDER} is
|
||||||
|
* {@code "true"} but {@link #CONTEXT_FORWARDER} is {@code "false"}; or
|
||||||
|
* if one of the attributes has an illegal value.
|
||||||
|
*
|
||||||
|
* @since 1.7
|
||||||
*/
|
*/
|
||||||
protected void installStandardForwarders(Map<String, ?> env) {
|
protected void installStandardForwarders(Map<String, ?> env) {
|
||||||
mustSupportSystemMBSF();
|
MBeanServerForwarder sysMBSF = getSystemMBeanServerForwarder();
|
||||||
|
|
||||||
// Remember that forwarders must be added in reverse order!
|
// Remember that forwarders must be added in reverse order!
|
||||||
|
|
||||||
boolean ecd = EnvHelp.computeBooleanFromString(
|
boolean ecd = EnvHelp.computeBooleanFromString(
|
||||||
env, EVENT_CLIENT_DELEGATE_FORWARDER, false, true);
|
env, EVENT_CLIENT_DELEGATE_FORWARDER, false, true);
|
||||||
|
boolean localize = EnvHelp.computeBooleanFromString(
|
||||||
|
env, LOCALIZE_MBEAN_INFO_FORWARDER, false, false);
|
||||||
|
boolean context = EnvHelp.computeBooleanFromString(
|
||||||
|
env, CONTEXT_FORWARDER, false, true);
|
||||||
|
|
||||||
|
if (localize && !context) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Inconsistent environment parameters: " +
|
||||||
|
LOCALIZE_MBEAN_INFO_FORWARDER + "=\"true\" requires " +
|
||||||
|
CONTEXT_FORWARDER + "=\"true\"");
|
||||||
|
}
|
||||||
|
|
||||||
if (ecd) {
|
if (ecd) {
|
||||||
MBeanServerForwarder mbsf = EventClientDelegate.newForwarder();
|
MBeanServerForwarder mbsf = EventClientDelegate.newForwarder(
|
||||||
setSystemMBeanServerForwarder(mbsf);
|
sysMBSF.getMBeanServer(), sysMBSF);
|
||||||
|
sysMBSF.setMBeanServer(mbsf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localize) {
|
||||||
|
MBeanServerForwarder mbsf = ClientContext.newLocalizeMBeanInfoForwarder(
|
||||||
|
sysMBSF.getMBeanServer());
|
||||||
|
sysMBSF.setMBeanServer(mbsf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context) {
|
||||||
|
MBeanServerForwarder mbsf = ClientContext.newContextForwarder(
|
||||||
|
sysMBSF.getMBeanServer(), sysMBSF);
|
||||||
|
sysMBSF.setMBeanServer(mbsf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -473,6 +552,7 @@ public abstract class JMXConnectorServer
|
|||||||
*
|
*
|
||||||
* @return the array of possible notifications.
|
* @return the array of possible notifications.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public MBeanNotificationInfo[] getNotificationInfo() {
|
public MBeanNotificationInfo[] getNotificationInfo() {
|
||||||
final String[] types = {
|
final String[] types = {
|
||||||
JMXConnectionNotification.OPENED,
|
JMXConnectionNotification.OPENED,
|
||||||
@ -684,30 +764,29 @@ public abstract class JMXConnectorServer
|
|||||||
* Fields describing the chains of forwarders (MBeanServerForwarders).
|
* Fields describing the chains of forwarders (MBeanServerForwarders).
|
||||||
* In the general case, the forwarders look something like this:
|
* In the general case, the forwarders look something like this:
|
||||||
*
|
*
|
||||||
* systemMBeanServer userMBeanServer
|
* userMBeanServer
|
||||||
* | |
|
* |
|
||||||
* v v
|
* v
|
||||||
* mbsf1 -> mbsf2 -> mbsf3 -> mbsf4 -> mbsf5 -> mbs
|
* systemMBeanServerForwarder -> mbsf2 -> mbsf3 -> mbsf4 -> mbsf5 -> mbs
|
||||||
*
|
*
|
||||||
* Here, each mbsfi is an MBeanServerForwarder, and the arrows
|
* Here, each mbsfi is an MBeanServerForwarder, and the arrows
|
||||||
* illustrate its getMBeanServer() method. The last MBeanServerForwarder
|
* illustrate its getMBeanServer() method. The last MBeanServerForwarder
|
||||||
* can point to an MBeanServer that is not instanceof MBeanServerForwarder,
|
* can point to an MBeanServer that is not instanceof MBeanServerForwarder,
|
||||||
* here mbs.
|
* here mbs.
|
||||||
*
|
*
|
||||||
* Initially, the chain can be empty if this JMXConnectorServer was
|
* The system chain is never empty because it always has at least
|
||||||
* constructed without an MBeanServer. In this case, both systemMBS
|
* systemMBeanServerForwarder. Initially, the user chain can be empty if
|
||||||
* and userMBS will be null. If there is initially an MBeanServer,
|
* this JMXConnectorServer was constructed without an MBeanServer. In
|
||||||
* then both systemMBS and userMBS will point to it.
|
* this case, userMBS will be null. If there is initially an MBeanServer,
|
||||||
|
* userMBS will point to it.
|
||||||
*
|
*
|
||||||
* Whenever userMBS is changed, the system chain must be updated. If there
|
* Whenever userMBS is changed, the system chain must be updated. Before
|
||||||
* are forwarders in the system chain (between systemMBS and userMBS in the
|
* the update, the last forwarder in the system chain points to the old
|
||||||
* picture above), then the last one must point to the old value of userMBS
|
* value of userMBS (possibly null). It must be updated to point to
|
||||||
* (possibly null). It must be updated to point to the new value. If there
|
* the new value. The invariant is that starting from systemMBSF and
|
||||||
* are no forwarders in the system chain, then systemMBS must be updated to
|
* repeatedly calling MBSF.getMBeanServer() you will end up at userMBS.
|
||||||
* the new value of userMBS. The invariant is that starting from systemMBS
|
* The implication is that you will not see any MBeanServer object on the
|
||||||
* and repeatedly calling MBSF.getMBeanServer() you will end up at
|
* way that is not also an MBeanServerForwarder.
|
||||||
* userMBS. The implication is that you will not see any MBeanServer
|
|
||||||
* object on the way that is not also an MBeanServerForwarder.
|
|
||||||
*
|
*
|
||||||
* The method insertUserMBeanServer contains the logic to change userMBS
|
* The method insertUserMBeanServer contains the logic to change userMBS
|
||||||
* and adjust the system chain appropriately.
|
* and adjust the system chain appropriately.
|
||||||
@ -716,7 +795,7 @@ public abstract class JMXConnectorServer
|
|||||||
* MBeanServer, then userMBS becomes that MBeanServer, and the system
|
* MBeanServer, then userMBS becomes that MBeanServer, and the system
|
||||||
* chain must be updated as just described.
|
* chain must be updated as just described.
|
||||||
*
|
*
|
||||||
* When systemMBS is updated, there is no effect on userMBS. The system
|
* When systemMBSF is updated, there is no effect on userMBS. The system
|
||||||
* chain may contain forwarders even though the user chain is empty
|
* chain may contain forwarders even though the user chain is empty
|
||||||
* (there is no MBeanServer). In that case an attempt to forward an
|
* (there is no MBeanServer). In that case an attempt to forward an
|
||||||
* incoming request through the chain will fall off the end and fail with a
|
* incoming request through the chain will fall off the end and fail with a
|
||||||
@ -726,7 +805,8 @@ public abstract class JMXConnectorServer
|
|||||||
|
|
||||||
private MBeanServer userMBeanServer;
|
private MBeanServer userMBeanServer;
|
||||||
|
|
||||||
private MBeanServer systemMBeanServer;
|
private final MBeanServerForwarder systemMBeanServerForwarder =
|
||||||
|
new IdentityMBeanServerForwarder();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name used to registered this server in an MBeanServer.
|
* The name used to registered this server in an MBeanServer.
|
||||||
|
@ -132,7 +132,7 @@ public interface JMXConnectorServerMBean {
|
|||||||
*
|
*
|
||||||
* <p>A connector server may support two chains of forwarders,
|
* <p>A connector server may support two chains of forwarders,
|
||||||
* a system chain and a user chain. See {@link
|
* a system chain and a user chain. See {@link
|
||||||
* JMXConnectorServer#setSystemMBeanServerForwarder} for details.</p>
|
* JMXConnectorServer#getSystemMBeanServerForwarder} for details.</p>
|
||||||
*
|
*
|
||||||
* @param mbsf the new <code>MBeanServerForwarder</code>.
|
* @param mbsf the new <code>MBeanServerForwarder</code>.
|
||||||
*
|
*
|
||||||
@ -141,7 +141,7 @@ public interface JMXConnectorServerMBean {
|
|||||||
* with <code>IllegalArgumentException</code>. This includes the
|
* with <code>IllegalArgumentException</code>. This includes the
|
||||||
* case where <code>mbsf</code> is null.
|
* case where <code>mbsf</code> is null.
|
||||||
*
|
*
|
||||||
* @see JMXConnectorServer#setSystemMBeanServerForwarder
|
* @see JMXConnectorServer#getSystemMBeanServerForwarder
|
||||||
*/
|
*/
|
||||||
public void setMBeanServerForwarder(MBeanServerForwarder mbsf);
|
public void setMBeanServerForwarder(MBeanServerForwarder mbsf);
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -383,7 +383,7 @@ public class RMIConnectorServer extends JMXConnectorServer {
|
|||||||
try {
|
try {
|
||||||
if (tracing) logger.trace("start", "setting default class loader");
|
if (tracing) logger.trace("start", "setting default class loader");
|
||||||
defaultClassLoader = EnvHelp.resolveServerClassLoader(
|
defaultClassLoader = EnvHelp.resolveServerClassLoader(
|
||||||
attributes, getSystemMBeanServer());
|
attributes, getSystemMBeanServerForwarder());
|
||||||
} catch (InstanceNotFoundException infc) {
|
} catch (InstanceNotFoundException infc) {
|
||||||
IllegalArgumentException x = new
|
IllegalArgumentException x = new
|
||||||
IllegalArgumentException("ClassLoader not found: "+infc);
|
IllegalArgumentException("ClassLoader not found: "+infc);
|
||||||
@ -398,7 +398,7 @@ public class RMIConnectorServer extends JMXConnectorServer {
|
|||||||
else
|
else
|
||||||
rmiServer = newServer();
|
rmiServer = newServer();
|
||||||
|
|
||||||
rmiServer.setMBeanServer(getSystemMBeanServer());
|
rmiServer.setMBeanServer(getSystemMBeanServerForwarder());
|
||||||
rmiServer.setDefaultClassLoader(defaultClassLoader);
|
rmiServer.setDefaultClassLoader(defaultClassLoader);
|
||||||
rmiServer.setRMIConnectorServer(this);
|
rmiServer.setRMIConnectorServer(this);
|
||||||
rmiServer.export();
|
rmiServer.export();
|
||||||
@ -592,31 +592,6 @@ public class RMIConnectorServer extends JMXConnectorServer {
|
|||||||
return Collections.unmodifiableMap(map);
|
return Collections.unmodifiableMap(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void setMBeanServerForwarder(MBeanServerForwarder mbsf) {
|
|
||||||
MBeanServer oldSMBS = getSystemMBeanServer();
|
|
||||||
super.setMBeanServerForwarder(mbsf);
|
|
||||||
if (oldSMBS != getSystemMBeanServer())
|
|
||||||
updateMBeanServer();
|
|
||||||
// If the system chain of MBeanServerForwarders is not empty, then
|
|
||||||
// there is no need to call rmiServerImpl.setMBeanServer, because
|
|
||||||
// it is pointing to the head of the system chain and that has not
|
|
||||||
// changed. (The *end* of the system chain will have been changed
|
|
||||||
// to point to mbsf.)
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateMBeanServer() {
|
|
||||||
if (rmiServerImpl != null)
|
|
||||||
rmiServerImpl.setMBeanServer(getSystemMBeanServer());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void setSystemMBeanServerForwarder(
|
|
||||||
MBeanServerForwarder mbsf) {
|
|
||||||
super.setSystemMBeanServerForwarder(mbsf);
|
|
||||||
updateMBeanServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
* @return true, since this connector server does support a system chain
|
* @return true, since this connector server does support a system chain
|
||||||
@ -631,16 +606,19 @@ public class RMIConnectorServer extends JMXConnectorServer {
|
|||||||
here so that they are accessible to other classes in this package
|
here so that they are accessible to other classes in this package
|
||||||
even though they have protected access. */
|
even though they have protected access. */
|
||||||
|
|
||||||
|
@Override
|
||||||
protected void connectionOpened(String connectionId, String message,
|
protected void connectionOpened(String connectionId, String message,
|
||||||
Object userData) {
|
Object userData) {
|
||||||
super.connectionOpened(connectionId, message, userData);
|
super.connectionOpened(connectionId, message, userData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected void connectionClosed(String connectionId, String message,
|
protected void connectionClosed(String connectionId, String message,
|
||||||
Object userData) {
|
Object userData) {
|
||||||
super.connectionClosed(connectionId, message, userData);
|
super.connectionClosed(connectionId, message, userData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected void connectionFailed(String connectionId, String message,
|
protected void connectionFailed(String connectionId, String message,
|
||||||
Object userData) {
|
Object userData) {
|
||||||
super.connectionFailed(connectionId, message, userData);
|
super.connectionFailed(connectionId, message, userData);
|
||||||
|
@ -39,7 +39,8 @@ import javax.management.*;
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
This test checks that annotations produce Descriptor entries as
|
This test checks that annotations produce Descriptor entries as
|
||||||
specified in javax.management.DescriptorKey. It does two things:
|
specified in javax.management.DescriptorKey and javax.management.DescriptorField.
|
||||||
|
It does the following:
|
||||||
|
|
||||||
- An annotation consisting of an int and a String, each with an
|
- An annotation consisting of an int and a String, each with an
|
||||||
appropriate @DescriptorKey annotation, is placed on every program
|
appropriate @DescriptorKey annotation, is placed on every program
|
||||||
@ -61,6 +62,10 @@ import javax.management.*;
|
|||||||
The test checks that in each case the corresponding Descriptor
|
The test checks that in each case the corresponding Descriptor
|
||||||
appears in the appropriate place inside the MBean's MBeanInfo.
|
appears in the appropriate place inside the MBean's MBeanInfo.
|
||||||
|
|
||||||
|
- A @DescriptorFields annotation defining two fields is placed in the
|
||||||
|
same places and again the test checks that the two fields appear
|
||||||
|
in the corresponding MBean*Info objects.
|
||||||
|
|
||||||
- An annotation consisting of enough other types to ensure coverage
|
- An annotation consisting of enough other types to ensure coverage
|
||||||
is placed on a getter. The test checks that the generated
|
is placed on a getter. The test checks that the generated
|
||||||
MBeanAttributeInfo contains the corresponding Descriptor. The tested
|
MBeanAttributeInfo contains the corresponding Descriptor. The tested
|
||||||
@ -78,12 +83,6 @@ import javax.management.*;
|
|||||||
public class AnnotationTest {
|
public class AnnotationTest {
|
||||||
private static String failed = null;
|
private static String failed = null;
|
||||||
|
|
||||||
// @Retention(RetentionPolicy.RUNTIME) @Inherited
|
|
||||||
// @Target(ElementType.METHOD)
|
|
||||||
// public static @interface DescriptorKey {
|
|
||||||
// String value();
|
|
||||||
// }
|
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public static @interface Pair {
|
public static @interface Pair {
|
||||||
@DescriptorKey("x")
|
@DescriptorKey("x")
|
||||||
@ -112,11 +111,12 @@ public class AnnotationTest {
|
|||||||
boolean[] booleanArrayValue();
|
boolean[] booleanArrayValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We use the annotation @Pair(x = 3, y = "foo") everywhere, and this is
|
/* We use the annotations @Pair(x = 3, y = "foo")
|
||||||
the Descriptor that it should produce: */
|
and @DescriptorFields({"foo=bar", "baz="}) everywhere, and this is
|
||||||
|
the Descriptor that they should produce: */
|
||||||
private static Descriptor expectedDescriptor =
|
private static Descriptor expectedDescriptor =
|
||||||
new ImmutableDescriptor(new String[] {"x", "y"},
|
new ImmutableDescriptor(new String[] {"x", "y", "foo", "baz"},
|
||||||
new Object[] {3, "foo"});
|
new Object[] {3, "foo", "bar", ""});
|
||||||
|
|
||||||
private static Descriptor expectedFullDescriptor =
|
private static Descriptor expectedFullDescriptor =
|
||||||
new ImmutableDescriptor(new String[] {
|
new ImmutableDescriptor(new String[] {
|
||||||
@ -136,8 +136,10 @@ public class AnnotationTest {
|
|||||||
});
|
});
|
||||||
|
|
||||||
@Pair(x = 3, y = "foo")
|
@Pair(x = 3, y = "foo")
|
||||||
|
@DescriptorFields({"foo=bar", "baz="})
|
||||||
public static interface ThingMBean {
|
public static interface ThingMBean {
|
||||||
@Pair(x = 3, y = "foo")
|
@Pair(x = 3, y = "foo")
|
||||||
|
@DescriptorFields({"foo=bar", "baz="})
|
||||||
@Full(classValue=Full.class,
|
@Full(classValue=Full.class,
|
||||||
enumValue=RetentionPolicy.RUNTIME,
|
enumValue=RetentionPolicy.RUNTIME,
|
||||||
booleanValue=false,
|
booleanValue=false,
|
||||||
@ -149,32 +151,47 @@ public class AnnotationTest {
|
|||||||
int getReadOnly();
|
int getReadOnly();
|
||||||
|
|
||||||
@Pair(x = 3, y = "foo")
|
@Pair(x = 3, y = "foo")
|
||||||
|
@DescriptorFields({"foo=bar", "baz="})
|
||||||
void setWriteOnly(int x);
|
void setWriteOnly(int x);
|
||||||
|
|
||||||
@Pair(x = 3, y = "foo")
|
@Pair(x = 3, y = "foo")
|
||||||
|
@DescriptorFields({"foo=bar", "baz="})
|
||||||
int getReadWrite1();
|
int getReadWrite1();
|
||||||
void setReadWrite1(int x);
|
void setReadWrite1(int x);
|
||||||
|
|
||||||
@Pair(x = 3, y = "foo")
|
@Pair(x = 3, y = "foo")
|
||||||
|
@DescriptorFields({"foo=bar", "baz="})
|
||||||
int getReadWrite2();
|
int getReadWrite2();
|
||||||
@Pair(x = 3, y = "foo")
|
@Pair(x = 3, y = "foo")
|
||||||
|
@DescriptorFields({"foo=bar", "baz="})
|
||||||
void setReadWrite2(int x);
|
void setReadWrite2(int x);
|
||||||
|
|
||||||
int getReadWrite3();
|
int getReadWrite3();
|
||||||
@Pair(x = 3, y = "foo")
|
@Pair(x = 3, y = "foo")
|
||||||
|
@DescriptorFields({"foo=bar", "baz="})
|
||||||
void setReadWrite3(int x);
|
void setReadWrite3(int x);
|
||||||
|
|
||||||
@Pair(x = 3, y = "foo")
|
@Pair(x = 3, y = "foo")
|
||||||
int operation(@Pair(x = 3, y = "foo") int p1,
|
@DescriptorFields({"foo=bar", "baz="})
|
||||||
@Pair(x = 3, y = "foo") int p2);
|
int operation(@Pair(x = 3, y = "foo")
|
||||||
|
@DescriptorFields({"foo=bar", "baz="})
|
||||||
|
int p1,
|
||||||
|
@Pair(x = 3, y = "foo")
|
||||||
|
@DescriptorFields({"foo=bar", "baz="})
|
||||||
|
int p2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Thing implements ThingMBean {
|
public static class Thing implements ThingMBean {
|
||||||
@Pair(x = 3, y = "foo")
|
@Pair(x = 3, y = "foo")
|
||||||
|
@DescriptorFields({"foo=bar", "baz="})
|
||||||
public Thing() {}
|
public Thing() {}
|
||||||
|
|
||||||
@Pair(x = 3, y = "foo")
|
@Pair(x = 3, y = "foo")
|
||||||
public Thing(@Pair(x = 3, y = "foo") int p1) {}
|
@DescriptorFields({"foo=bar", "baz="})
|
||||||
|
public Thing(
|
||||||
|
@Pair(x = 3, y = "foo")
|
||||||
|
@DescriptorFields({"foo=bar", "baz="})
|
||||||
|
int p1) {}
|
||||||
|
|
||||||
public int getReadOnly() {return 0;}
|
public int getReadOnly() {return 0;}
|
||||||
|
|
||||||
@ -193,14 +210,20 @@ public class AnnotationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Pair(x = 3, y = "foo")
|
@Pair(x = 3, y = "foo")
|
||||||
|
@DescriptorFields({"foo=bar", "baz="})
|
||||||
public static interface ThingMXBean extends ThingMBean {}
|
public static interface ThingMXBean extends ThingMBean {}
|
||||||
|
|
||||||
public static class ThingImpl implements ThingMXBean {
|
public static class ThingImpl implements ThingMXBean {
|
||||||
@Pair(x = 3, y = "foo")
|
@Pair(x = 3, y = "foo")
|
||||||
|
@DescriptorFields({"foo=bar", "baz="})
|
||||||
public ThingImpl() {}
|
public ThingImpl() {}
|
||||||
|
|
||||||
@Pair(x = 3, y = "foo")
|
@Pair(x = 3, y = "foo")
|
||||||
public ThingImpl(@Pair(x = 3, y = "foo") int p1) {}
|
@DescriptorFields({"foo=bar", "baz="})
|
||||||
|
public ThingImpl(
|
||||||
|
@Pair(x = 3, y = "foo")
|
||||||
|
@DescriptorFields({"foo=bar", "baz="})
|
||||||
|
int p1) {}
|
||||||
|
|
||||||
public int getReadOnly() {return 0;}
|
public int getReadOnly() {return 0;}
|
||||||
|
|
||||||
@ -218,6 +241,79 @@ public class AnnotationTest {
|
|||||||
public int operation(int p1, int p2) {return 0;}
|
public int operation(int p1, int p2) {return 0;}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public static @interface DefaultTest {
|
||||||
|
@DescriptorKey(value = "string1", omitIfDefault = true)
|
||||||
|
String string1() default "";
|
||||||
|
@DescriptorKey(value = "string2", omitIfDefault = true)
|
||||||
|
String string2() default "tiddly pom";
|
||||||
|
@DescriptorKey(value = "int", omitIfDefault = true)
|
||||||
|
int intx() default 23;
|
||||||
|
@DescriptorKey(value = "intarray1", omitIfDefault = true)
|
||||||
|
int[] intArray1() default {};
|
||||||
|
@DescriptorKey(value = "intarray2", omitIfDefault = true)
|
||||||
|
int[] intArray2() default {1, 2};
|
||||||
|
@DescriptorKey(value = "stringarray1", omitIfDefault = true)
|
||||||
|
String[] stringArray1() default {};
|
||||||
|
@DescriptorKey(value = "stringarray2", omitIfDefault = true)
|
||||||
|
String[] stringArray2() default {"foo", "bar"};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public static @interface Expect {
|
||||||
|
String[] value() default {};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface DefaultMBean {
|
||||||
|
@DefaultTest
|
||||||
|
@Expect()
|
||||||
|
public void a();
|
||||||
|
|
||||||
|
@DefaultTest(string1="")
|
||||||
|
@Expect()
|
||||||
|
public void b();
|
||||||
|
|
||||||
|
@DefaultTest(string1="nondefault")
|
||||||
|
@Expect("string1=nondefault")
|
||||||
|
public void c();
|
||||||
|
|
||||||
|
@DefaultTest(string2="tiddly pom")
|
||||||
|
@Expect()
|
||||||
|
public void d();
|
||||||
|
|
||||||
|
@DefaultTest(intx=23)
|
||||||
|
@Expect()
|
||||||
|
public void e();
|
||||||
|
|
||||||
|
@DefaultTest(intx=34)
|
||||||
|
@Expect("int=34")
|
||||||
|
public void f();
|
||||||
|
|
||||||
|
@DefaultTest(intArray1={})
|
||||||
|
@Expect()
|
||||||
|
public void g();
|
||||||
|
|
||||||
|
@DefaultTest(intArray1={2,3})
|
||||||
|
@Expect("intarray1=[2, 3]")
|
||||||
|
public void h();
|
||||||
|
|
||||||
|
@DefaultTest(intArray2={})
|
||||||
|
@Expect("intarray2=[]")
|
||||||
|
public void i();
|
||||||
|
|
||||||
|
@DefaultTest(stringArray1={})
|
||||||
|
@Expect()
|
||||||
|
public void j();
|
||||||
|
|
||||||
|
@DefaultTest(stringArray1={"foo"})
|
||||||
|
@Expect("stringarray1=[foo]")
|
||||||
|
public void k();
|
||||||
|
|
||||||
|
@DefaultTest(stringArray2={})
|
||||||
|
@Expect("stringarray2=[]")
|
||||||
|
public void l();
|
||||||
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
System.out.println("Testing that annotations are correctly " +
|
System.out.println("Testing that annotations are correctly " +
|
||||||
"reflected in Descriptor entries");
|
"reflected in Descriptor entries");
|
||||||
@ -225,20 +321,62 @@ public class AnnotationTest {
|
|||||||
MBeanServer mbs =
|
MBeanServer mbs =
|
||||||
java.lang.management.ManagementFactory.getPlatformMBeanServer();
|
java.lang.management.ManagementFactory.getPlatformMBeanServer();
|
||||||
ObjectName on = new ObjectName("a:b=c");
|
ObjectName on = new ObjectName("a:b=c");
|
||||||
|
|
||||||
Thing thing = new Thing();
|
Thing thing = new Thing();
|
||||||
mbs.registerMBean(thing, on);
|
mbs.registerMBean(thing, on);
|
||||||
check(mbs, on);
|
check(mbs, on);
|
||||||
mbs.unregisterMBean(on);
|
mbs.unregisterMBean(on);
|
||||||
|
|
||||||
ThingImpl thingImpl = new ThingImpl();
|
ThingImpl thingImpl = new ThingImpl();
|
||||||
mbs.registerMBean(thingImpl, on);
|
mbs.registerMBean(thingImpl, on);
|
||||||
|
Descriptor d = mbs.getMBeanInfo(on).getDescriptor();
|
||||||
|
if (!d.getFieldValue("mxbean").equals("true")) {
|
||||||
|
System.out.println("NOT OK: expected MXBean");
|
||||||
|
failed = "Expected MXBean";
|
||||||
|
}
|
||||||
check(mbs, on);
|
check(mbs, on);
|
||||||
|
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("Testing that omitIfDefault works");
|
||||||
|
DefaultMBean defaultImpl = (DefaultMBean) Proxy.newProxyInstance(
|
||||||
|
DefaultMBean.class.getClassLoader(),
|
||||||
|
new Class<?>[] {DefaultMBean.class},
|
||||||
|
new InvocationHandler(){
|
||||||
|
public Object invoke(Object proxy, Method method, Object[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
DynamicMBean mbean = new StandardMBean(defaultImpl, DefaultMBean.class);
|
||||||
|
MBeanOperationInfo[] ops = mbean.getMBeanInfo().getOperations();
|
||||||
|
for (MBeanOperationInfo op : ops) {
|
||||||
|
String name = op.getName();
|
||||||
|
Expect expect =
|
||||||
|
DefaultMBean.class.getMethod(name).getAnnotation(Expect.class);
|
||||||
|
Descriptor opd = op.getDescriptor();
|
||||||
|
List<String> fields = new ArrayList<String>();
|
||||||
|
for (String fieldName : opd.getFieldNames()) {
|
||||||
|
Object value = opd.getFieldValue(fieldName);
|
||||||
|
String s = Arrays.deepToString(new Object[] {value});
|
||||||
|
s = s.substring(1, s.length() - 1);
|
||||||
|
fields.add(fieldName + "=" + s);
|
||||||
|
}
|
||||||
|
Descriptor opds = new ImmutableDescriptor(fields.toArray(new String[0]));
|
||||||
|
Descriptor expd = new ImmutableDescriptor(expect.value());
|
||||||
|
if (opds.equals(expd))
|
||||||
|
System.out.println("OK: op " + name + ": " + opds);
|
||||||
|
else {
|
||||||
|
String failure = "Bad descriptor for op " + name + ": " +
|
||||||
|
"expected " + expd + ", got " + opds;
|
||||||
|
System.out.println("NOT OK: " + failure);
|
||||||
|
failed = failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
|
||||||
if (failed == null)
|
if (failed == null)
|
||||||
System.out.println("Test passed");
|
System.out.println("Test passed");
|
||||||
else if (true)
|
|
||||||
throw new Exception("TEST FAILED: " + failed);
|
|
||||||
else
|
else
|
||||||
System.out.println("Test disabled until 6221321 implemented");
|
throw new Exception("TEST FAILED: " + failed);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void check(MBeanServer mbs, ObjectName on) throws Exception {
|
private static void check(MBeanServer mbs, ObjectName on) throws Exception {
|
||||||
@ -295,151 +433,4 @@ public class AnnotationTest {
|
|||||||
for (DescriptorRead x : xx)
|
for (DescriptorRead x : xx)
|
||||||
check(x);
|
check(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class AnnotatedMBean extends StandardMBean {
|
|
||||||
<T> AnnotatedMBean(T resource, Class<T> interfaceClass, boolean mx) {
|
|
||||||
super(resource, interfaceClass, mx);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String[] attrPrefixes = {"get", "set", "is"};
|
|
||||||
|
|
||||||
protected void cacheMBeanInfo(MBeanInfo info) {
|
|
||||||
MBeanAttributeInfo[] attrs = info.getAttributes();
|
|
||||||
MBeanOperationInfo[] ops = info.getOperations();
|
|
||||||
|
|
||||||
for (int i = 0; i < attrs.length; i++) {
|
|
||||||
MBeanAttributeInfo attr = attrs[i];
|
|
||||||
String name = attr.getName();
|
|
||||||
Descriptor d = attr.getDescriptor();
|
|
||||||
Method m;
|
|
||||||
if ((m = getMethod("get" + name)) != null)
|
|
||||||
d = ImmutableDescriptor.union(d, descriptorFor(m));
|
|
||||||
if (attr.getType().equals("boolean") &&
|
|
||||||
(m = getMethod("is" + name)) != null)
|
|
||||||
d = ImmutableDescriptor.union(d, descriptorFor(m));
|
|
||||||
if ((m = getMethod("set" + name, attr)) != null)
|
|
||||||
d = ImmutableDescriptor.union(d, descriptorFor(m));
|
|
||||||
if (!d.equals(attr.getDescriptor())) {
|
|
||||||
attrs[i] =
|
|
||||||
new MBeanAttributeInfo(name, attr.getType(),
|
|
||||||
attr.getDescription(), attr.isReadable(),
|
|
||||||
attr.isWritable(), attr.isIs(), d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < ops.length; i++) {
|
|
||||||
MBeanOperationInfo op = ops[i];
|
|
||||||
String name = op.getName();
|
|
||||||
Descriptor d = op.getDescriptor();
|
|
||||||
MBeanParameterInfo[] params = op.getSignature();
|
|
||||||
Method m = getMethod(name, params);
|
|
||||||
if (m != null) {
|
|
||||||
d = ImmutableDescriptor.union(d, descriptorFor(m));
|
|
||||||
Annotation[][] annots = m.getParameterAnnotations();
|
|
||||||
for (int pi = 0; pi < params.length; pi++) {
|
|
||||||
MBeanParameterInfo param = params[pi];
|
|
||||||
Descriptor pd =
|
|
||||||
ImmutableDescriptor.union(param.getDescriptor(),
|
|
||||||
descriptorFor(annots[pi]));
|
|
||||||
params[pi] = new MBeanParameterInfo(param.getName(),
|
|
||||||
param.getType(), param.getDescription(), pd);
|
|
||||||
}
|
|
||||||
op = new MBeanOperationInfo(op.getName(),
|
|
||||||
op.getDescription(), params, op.getReturnType(),
|
|
||||||
op.getImpact(), d);
|
|
||||||
if (!ops[i].equals(op))
|
|
||||||
ops[i] = op;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Descriptor id = descriptorFor(getMBeanInterface());
|
|
||||||
info = new MBeanInfo(info.getClassName(), info.getDescription(),
|
|
||||||
attrs, info.getConstructors(), ops, info.getNotifications(),
|
|
||||||
ImmutableDescriptor.union(id, info.getDescriptor()));
|
|
||||||
super.cacheMBeanInfo(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Descriptor descriptorFor(AnnotatedElement x) {
|
|
||||||
Annotation[] annots = x.getAnnotations();
|
|
||||||
return descriptorFor(annots);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Descriptor descriptorFor(Annotation[] annots) {
|
|
||||||
if (annots.length == 0)
|
|
||||||
return ImmutableDescriptor.EMPTY_DESCRIPTOR;
|
|
||||||
Map<String, Object> descriptorMap = new HashMap<String, Object>();
|
|
||||||
for (Annotation a : annots) {
|
|
||||||
Class<? extends Annotation> c = a.annotationType();
|
|
||||||
Method[] elements = c.getMethods();
|
|
||||||
for (Method element : elements) {
|
|
||||||
DescriptorKey key =
|
|
||||||
element.getAnnotation(DescriptorKey.class);
|
|
||||||
if (key != null) {
|
|
||||||
String name = key.value();
|
|
||||||
Object value;
|
|
||||||
try {
|
|
||||||
value = element.invoke(a);
|
|
||||||
} catch (Exception e) {
|
|
||||||
// we don't expect this
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
Object oldValue = descriptorMap.put(name, value);
|
|
||||||
if (oldValue != null && !oldValue.equals(value)) {
|
|
||||||
final String msg =
|
|
||||||
"Inconsistent values for descriptor field " +
|
|
||||||
name + " from annotations: " + value + " :: " +
|
|
||||||
oldValue;
|
|
||||||
throw new IllegalArgumentException(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (descriptorMap.isEmpty())
|
|
||||||
return ImmutableDescriptor.EMPTY_DESCRIPTOR;
|
|
||||||
else
|
|
||||||
return new ImmutableDescriptor(descriptorMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Method getMethod(String name, MBeanFeatureInfo... params) {
|
|
||||||
Class<?> intf = getMBeanInterface();
|
|
||||||
ClassLoader loader = intf.getClassLoader();
|
|
||||||
Class[] classes = new Class[params.length];
|
|
||||||
for (int i = 0; i < params.length; i++) {
|
|
||||||
MBeanFeatureInfo param = params[i];
|
|
||||||
Descriptor d = param.getDescriptor();
|
|
||||||
String type = (String) d.getFieldValue("originalType");
|
|
||||||
if (type == null) {
|
|
||||||
if (param instanceof MBeanAttributeInfo)
|
|
||||||
type = ((MBeanAttributeInfo) param).getType();
|
|
||||||
else
|
|
||||||
type = ((MBeanParameterInfo) param).getType();
|
|
||||||
}
|
|
||||||
Class<?> c = primitives.get(type);
|
|
||||||
if (c == null) {
|
|
||||||
try {
|
|
||||||
c = Class.forName(type, false, loader);
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
classes[i] = c;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return intf.getMethod(name, classes);
|
|
||||||
} catch (Exception e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Map<String, Class<?>> primitives =
|
|
||||||
new HashMap<String, Class<?>>();
|
|
||||||
static {
|
|
||||||
for (Class<?> c :
|
|
||||||
new Class[] {boolean.class, byte.class, short.class,
|
|
||||||
int.class, long.class, float.class,
|
|
||||||
double.class, char.class, void.class}) {
|
|
||||||
primitives.put(c.getName(), c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
103
jdk/test/javax/management/context/ContextForwarderTest.java
Normal file
103
jdk/test/javax/management/context/ContextForwarderTest.java
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 Sun Microsystems, Inc. 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.
|
||||||
|
*
|
||||||
|
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||||
|
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||||
|
* have any questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @bug 5072267
|
||||||
|
* @summary Test that a context forwarder can be created and then installed.
|
||||||
|
* @author Eamonn McManus
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* The specific thing we're testing for is that the forwarder can be created
|
||||||
|
* with a null "next", and then installed with a real "next". An earlier
|
||||||
|
* defect meant that in this case the simulated jmx.context// namespace had a
|
||||||
|
* null handler that never changed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import javax.management.ClientContext;
|
||||||
|
import javax.management.MBeanServer;
|
||||||
|
import javax.management.MBeanServerConnection;
|
||||||
|
import javax.management.MBeanServerFactory;
|
||||||
|
import javax.management.ObjectName;
|
||||||
|
import javax.management.remote.JMXConnector;
|
||||||
|
import javax.management.remote.JMXConnectorFactory;
|
||||||
|
import javax.management.remote.JMXConnectorServer;
|
||||||
|
import javax.management.remote.JMXConnectorServerFactory;
|
||||||
|
import javax.management.remote.JMXServiceURL;
|
||||||
|
import javax.management.remote.MBeanServerForwarder;
|
||||||
|
|
||||||
|
public class ContextForwarderTest {
|
||||||
|
private static String failure;
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
|
||||||
|
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://");
|
||||||
|
Map<String, String> env = new HashMap<String, String>();
|
||||||
|
env.put(JMXConnectorServer.CONTEXT_FORWARDER, "false");
|
||||||
|
JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(
|
||||||
|
url, env, mbs);
|
||||||
|
MBeanServerForwarder sysMBSF = cs.getSystemMBeanServerForwarder();
|
||||||
|
MBeanServerForwarder mbsf = ClientContext.newContextForwarder(mbs, sysMBSF);
|
||||||
|
sysMBSF.setMBeanServer(mbsf);
|
||||||
|
|
||||||
|
int localCount = mbs.getMBeanCount();
|
||||||
|
|
||||||
|
cs.start();
|
||||||
|
try {
|
||||||
|
JMXConnector cc = JMXConnectorFactory.connect(cs.getAddress());
|
||||||
|
MBeanServerConnection mbsc = cc.getMBeanServerConnection();
|
||||||
|
mbsc = ClientContext.withContext(mbsc, "foo", "bar");
|
||||||
|
int contextCount = mbsc.getMBeanCount();
|
||||||
|
if (localCount + 1 != contextCount) {
|
||||||
|
fail("Local MBean count %d, context MBean count %d",
|
||||||
|
localCount, contextCount);
|
||||||
|
}
|
||||||
|
Set<ObjectName> localNames =
|
||||||
|
new TreeSet<ObjectName>(mbs.queryNames(null, null));
|
||||||
|
ObjectName contextNamespaceObjectName =
|
||||||
|
new ObjectName(ClientContext.NAMESPACE + "//:type=JMXNamespace");
|
||||||
|
if (!localNames.add(contextNamespaceObjectName))
|
||||||
|
fail("Local names already contained context namespace handler");
|
||||||
|
Set<ObjectName> contextNames = mbsc.queryNames(null, null);
|
||||||
|
if (!localNames.equals(contextNames)) {
|
||||||
|
fail("Name set differs locally and in context: " +
|
||||||
|
"local: %s; context: %s", localNames, contextNames);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
cs.stop();
|
||||||
|
}
|
||||||
|
if (failure != null)
|
||||||
|
throw new Exception("TEST FAILED: " + failure);
|
||||||
|
else
|
||||||
|
System.out.println("TEST PASSED");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void fail(String msg, Object... params) {
|
||||||
|
failure = String.format(msg, params);
|
||||||
|
System.out.println("FAIL: " + failure);
|
||||||
|
}
|
||||||
|
}
|
534
jdk/test/javax/management/context/ContextTest.java
Normal file
534
jdk/test/javax/management/context/ContextTest.java
Normal file
@ -0,0 +1,534 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc. 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.
|
||||||
|
*
|
||||||
|
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||||
|
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||||
|
* have any questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test ContextTest
|
||||||
|
* @bug 5072267
|
||||||
|
* @summary Test client contexts.
|
||||||
|
* @author Eamonn McManus
|
||||||
|
* TODO: Try registering with a null name replaced by preRegister (for example
|
||||||
|
* from the MLet class) and see if it now works.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import javax.management.Attribute;
|
||||||
|
import javax.management.AttributeList;
|
||||||
|
import javax.management.ClientContext;
|
||||||
|
import javax.management.DynamicMBean;
|
||||||
|
import javax.management.JMX;
|
||||||
|
import javax.management.ListenerNotFoundException;
|
||||||
|
import javax.management.MBeanNotificationInfo;
|
||||||
|
import javax.management.MBeanRegistration;
|
||||||
|
import javax.management.MBeanServer;
|
||||||
|
import javax.management.MBeanServerDelegate;
|
||||||
|
import javax.management.Notification;
|
||||||
|
import javax.management.NotificationBroadcasterSupport;
|
||||||
|
import javax.management.NotificationFilter;
|
||||||
|
import javax.management.NotificationListener;
|
||||||
|
import javax.management.ObjectInstance;
|
||||||
|
import javax.management.ObjectName;
|
||||||
|
import javax.management.StandardMBean;
|
||||||
|
import javax.management.loading.MLet;
|
||||||
|
import javax.management.namespace.JMXNamespace;
|
||||||
|
|
||||||
|
import javax.management.remote.MBeanServerForwarder;
|
||||||
|
import static java.util.Collections.emptyMap;
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
|
|
||||||
|
public class ContextTest {
|
||||||
|
private static String failure;
|
||||||
|
private static final Map<String, String> emptyContext = emptyMap();
|
||||||
|
|
||||||
|
public static interface ShowContextMBean {
|
||||||
|
public Map<String, String> getContext();
|
||||||
|
public Map<String, String> getCreationContext();
|
||||||
|
public Set<String> getCalledOps();
|
||||||
|
public String getThing();
|
||||||
|
public void setThing(String x);
|
||||||
|
public int add(int x, int y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ShowContext
|
||||||
|
extends NotificationBroadcasterSupport
|
||||||
|
implements ShowContextMBean, MBeanRegistration {
|
||||||
|
private final Map<String, String> creationContext;
|
||||||
|
private final Set<String> calledOps = new HashSet<String>();
|
||||||
|
|
||||||
|
public ShowContext() {
|
||||||
|
creationContext = getContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getContext() {
|
||||||
|
return ClientContext.getContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getCreationContext() {
|
||||||
|
return creationContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getCalledOps() {
|
||||||
|
return calledOps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getThing() {
|
||||||
|
return "x";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setThing(String x) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public int add(int x, int y) {
|
||||||
|
return x + y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectName preRegister(MBeanServer server, ObjectName name) {
|
||||||
|
assertEquals("preRegister context", creationContext, getContext());
|
||||||
|
calledOps.add("preRegister");
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void postRegister(Boolean registrationDone) {
|
||||||
|
assertEquals("postRegister context", creationContext, getContext());
|
||||||
|
calledOps.add("postRegister");
|
||||||
|
}
|
||||||
|
|
||||||
|
// The condition checked here is not guaranteed universally true,
|
||||||
|
// but is true every time we unregister an instance of this MBean
|
||||||
|
// in this test.
|
||||||
|
public void preDeregister() throws Exception {
|
||||||
|
assertEquals("preDeregister context", creationContext, getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void postDeregister() {
|
||||||
|
assertEquals("postDeregister context", creationContext, getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same remark as for preDeregister
|
||||||
|
@Override
|
||||||
|
public MBeanNotificationInfo[] getNotificationInfo() {
|
||||||
|
calledOps.add("getNotificationInfo");
|
||||||
|
return super.getNotificationInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addNotificationListener(
|
||||||
|
NotificationListener listener, NotificationFilter filter, Object handback) {
|
||||||
|
calledOps.add("addNotificationListener");
|
||||||
|
super.addNotificationListener(listener, filter, handback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeNotificationListener(
|
||||||
|
NotificationListener listener)
|
||||||
|
throws ListenerNotFoundException {
|
||||||
|
calledOps.add("removeNL1");
|
||||||
|
super.removeNotificationListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeNotificationListener(
|
||||||
|
NotificationListener listener, NotificationFilter filter, Object handback)
|
||||||
|
throws ListenerNotFoundException {
|
||||||
|
calledOps.add("removeNL3");
|
||||||
|
super.removeNotificationListener(listener, filter, handback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LogRecord {
|
||||||
|
final String op;
|
||||||
|
final Object[] params;
|
||||||
|
final Map<String, String> context;
|
||||||
|
LogRecord(String op, Object[] params, Map<String, String> context) {
|
||||||
|
this.op = op;
|
||||||
|
this.params = params;
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return op + Arrays.deepToString(params) + " " + context;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* InvocationHandler that forwards all methods to a contained object
|
||||||
|
* but also records each forwarded method. This allows us to check
|
||||||
|
* that the appropriate methods were called with the appropriate
|
||||||
|
* parameters. It's similar to what's typically available in
|
||||||
|
* Mock Object frameworks.
|
||||||
|
*/
|
||||||
|
private static class LogIH implements InvocationHandler {
|
||||||
|
private final Object wrapped;
|
||||||
|
Queue<LogRecord> log = new LinkedList<LogRecord>();
|
||||||
|
|
||||||
|
LogIH(Object wrapped) {
|
||||||
|
this.wrapped = wrapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object invoke(Object proxy, Method method, Object[] args)
|
||||||
|
throws Throwable {
|
||||||
|
if (method.getDeclaringClass() != Object.class) {
|
||||||
|
LogRecord lr =
|
||||||
|
new LogRecord(
|
||||||
|
method.getName(), args, ClientContext.getContext());
|
||||||
|
log.add(lr);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return method.invoke(wrapped, args);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
throw e.getCause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> T newSnoop(Class<T> wrappedClass, LogIH logIH) {
|
||||||
|
return wrappedClass.cast(Proxy.newProxyInstance(
|
||||||
|
wrappedClass.getClassLoader(),
|
||||||
|
new Class<?>[] {wrappedClass},
|
||||||
|
logIH));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
|
||||||
|
System.out.println(mbs.queryNames(null, null));
|
||||||
|
ObjectName name = new ObjectName("a:b=c");
|
||||||
|
mbs.registerMBean(new ShowContext(), name);
|
||||||
|
final ShowContextMBean show =
|
||||||
|
JMX.newMBeanProxy(mbs, name, ShowContextMBean.class);
|
||||||
|
|
||||||
|
// Test local setting and getting within the MBeanServer
|
||||||
|
|
||||||
|
assertEquals("initial context", emptyContext, show.getContext());
|
||||||
|
ClientContext.doWithContext(singletonMap("foo", "bar"), new Callable<Void>() {
|
||||||
|
public Void call() {
|
||||||
|
assertEquals("context in doWithContext",
|
||||||
|
singletonMap("foo", "bar"), show.getContext());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assertEquals("initial context after doWithContext",
|
||||||
|
emptyContext, show.getContext());
|
||||||
|
String got = ClientContext.doWithContext(
|
||||||
|
singletonMap("foo", "baz"), new Callable<String>() {
|
||||||
|
public String call() {
|
||||||
|
return ClientContext.getContext().get("foo");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assertEquals("value extracted from context", "baz", got);
|
||||||
|
|
||||||
|
Map<String, String> combined = ClientContext.doWithContext(
|
||||||
|
singletonMap("foo", "baz"), new Callable<Map<String, String>>() {
|
||||||
|
public Map<String, String> call() throws Exception {
|
||||||
|
return ClientContext.doWithContext(
|
||||||
|
singletonMap("fred", "jim"),
|
||||||
|
new Callable<Map<String, String>>() {
|
||||||
|
public Map<String, String> call() {
|
||||||
|
return ClientContext.getContext();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assertEquals("nested doWithContext context",
|
||||||
|
singletonMap("fred", "jim"), combined);
|
||||||
|
|
||||||
|
final String ugh = "a!\u00c9//*=:\"% ";
|
||||||
|
ClientContext.doWithContext(singletonMap(ugh, ugh), new Callable<Void>() {
|
||||||
|
public Void call() {
|
||||||
|
assertEquals("context with tricky encoding",
|
||||||
|
singletonMap(ugh, ugh), show.getContext());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Map<String, String> ughMap = new TreeMap<String, String>();
|
||||||
|
ughMap.put(ugh, ugh);
|
||||||
|
ughMap.put("fred", "jim");
|
||||||
|
// Since this is a TreeMap and "fred" is greater than ugh (which begins
|
||||||
|
// with "a"), we will see the encoding of ugh first in the output string.
|
||||||
|
String encoded = ClientContext.encode(ughMap);
|
||||||
|
String expectedUghCoding = "a%21%C3%89%2F%2F%2A%3D%3A%22%25+";
|
||||||
|
String expectedUghMapCoding =
|
||||||
|
ClientContext.NAMESPACE + "//" + expectedUghCoding + "=" +
|
||||||
|
expectedUghCoding + ";fred=jim";
|
||||||
|
assertEquals("hairy context encoded as string",
|
||||||
|
expectedUghMapCoding, encoded);
|
||||||
|
|
||||||
|
// Wrap the MBeanServer with a context MBSF so we can test withContext.
|
||||||
|
// Also check the simulated namespace directly.
|
||||||
|
|
||||||
|
LogIH mbsIH = new LogIH(mbs);
|
||||||
|
MBeanServer snoopMBS = newSnoop(MBeanServer.class, mbsIH);
|
||||||
|
MBeanServerForwarder ctxMBS =
|
||||||
|
ClientContext.newContextForwarder(snoopMBS, null);
|
||||||
|
|
||||||
|
// The MBSF returned by ClientContext is actually a compound of two
|
||||||
|
// forwarders, but that is supposed to be hidden by changing the
|
||||||
|
// behaviour of get/setMBeanServer. Check that it is indeed so.
|
||||||
|
assertEquals("next MBS of context forwarder",
|
||||||
|
snoopMBS, ctxMBS.getMBeanServer());
|
||||||
|
// If the above assertion fails you may get a confusing message
|
||||||
|
// because the toString() of the two objects is likely to be the same
|
||||||
|
// so it will look as if they should be equal.
|
||||||
|
ctxMBS.setMBeanServer(null);
|
||||||
|
assertEquals("next MBS of context forwarder after setting it null",
|
||||||
|
null, ctxMBS.getMBeanServer());
|
||||||
|
ctxMBS.setMBeanServer(snoopMBS);
|
||||||
|
|
||||||
|
// The MBSF should look the same as the original MBeanServer except
|
||||||
|
// that it has the JMXNamespace for the simulated namespace.
|
||||||
|
|
||||||
|
Set<ObjectName> origNames = mbs.queryNames(null, null);
|
||||||
|
Set<ObjectName> mbsfNames = ctxMBS.queryNames(null, null);
|
||||||
|
assertEquals("number of MBeans returned by queryNames within forwarder",
|
||||||
|
origNames.size() + 1, mbsfNames.size());
|
||||||
|
assertEquals("MBeanCount within forwarder",
|
||||||
|
mbsfNames.size(), ctxMBS.getMBeanCount());
|
||||||
|
assertCalled(mbsIH, "queryNames", emptyContext);
|
||||||
|
assertCalled(mbsIH, "getMBeanCount", emptyContext);
|
||||||
|
|
||||||
|
ObjectName ctxNamespaceName = new ObjectName(
|
||||||
|
ClientContext.NAMESPACE + "//:" + JMXNamespace.TYPE_ASSIGNMENT);
|
||||||
|
origNames.add(ctxNamespaceName);
|
||||||
|
assertEquals("MBeans within forwarder", origNames, mbsfNames);
|
||||||
|
Set<String> domains = new HashSet<String>(Arrays.asList(ctxMBS.getDomains()));
|
||||||
|
assertEquals("domains include context namespace MBean",
|
||||||
|
true, domains.contains(ClientContext.NAMESPACE + "//"));
|
||||||
|
assertCalled(mbsIH, "getDomains", emptyContext);
|
||||||
|
|
||||||
|
// Now test ClientContext.withContext.
|
||||||
|
|
||||||
|
MBeanServer ughMBS = ClientContext.withContext(ctxMBS, ugh, ugh);
|
||||||
|
|
||||||
|
ShowContextMBean ughshow =
|
||||||
|
JMX.newMBeanProxy(ughMBS, name, ShowContextMBean.class);
|
||||||
|
Map<String, String> ughCtx = ughshow.getContext();
|
||||||
|
Map<String, String> ughExpect = singletonMap(ugh, ugh);
|
||||||
|
assertEquals("context seen by MBean accessed within namespace",
|
||||||
|
ughExpect, ughCtx);
|
||||||
|
assertCalled(mbsIH, "getAttribute", ughExpect, name, "Context");
|
||||||
|
|
||||||
|
MBeanServer cmbs = ClientContext.withContext(
|
||||||
|
ctxMBS, "mickey", "mouse");
|
||||||
|
ShowContextMBean cshow =
|
||||||
|
JMX.newMBeanProxy(cmbs, name, ShowContextMBean.class);
|
||||||
|
assertEquals("context seen by MBean accessed within namespace",
|
||||||
|
singletonMap("mickey", "mouse"), cshow.getContext());
|
||||||
|
|
||||||
|
MBeanServer ccmbs = ClientContext.withContext(
|
||||||
|
cmbs, "donald", "duck");
|
||||||
|
ShowContextMBean ccshow =
|
||||||
|
JMX.newMBeanProxy(ccmbs, name, ShowContextMBean.class);
|
||||||
|
Map<String, String> disney = new HashMap<String, String>();
|
||||||
|
disney.put("mickey", "mouse");
|
||||||
|
disney.put("donald", "duck");
|
||||||
|
assertEquals("context seen by MBean in nested namespace",
|
||||||
|
disney, ccshow.getContext());
|
||||||
|
|
||||||
|
// Test that all MBS ops produce reasonable results
|
||||||
|
|
||||||
|
ObjectName logger = new ObjectName("a:type=Logger");
|
||||||
|
DynamicMBean showMBean =
|
||||||
|
new StandardMBean(new ShowContext(), ShowContextMBean.class);
|
||||||
|
LogIH mbeanLogIH = new LogIH(showMBean);
|
||||||
|
DynamicMBean logMBean = newSnoop(DynamicMBean.class, mbeanLogIH);
|
||||||
|
ObjectInstance loggerOI = ccmbs.registerMBean(logMBean, logger);
|
||||||
|
assertEquals("ObjectName returned by createMBean",
|
||||||
|
logger, loggerOI.getObjectName());
|
||||||
|
|
||||||
|
// We get an getMBeanInfo call to determine the className in the
|
||||||
|
// ObjectInstance to return from registerMBean.
|
||||||
|
assertCalled(mbeanLogIH, "getMBeanInfo", disney);
|
||||||
|
|
||||||
|
ccmbs.getAttribute(logger, "Thing");
|
||||||
|
assertCalled(mbeanLogIH, "getAttribute", disney);
|
||||||
|
|
||||||
|
ccmbs.getAttributes(logger, new String[] {"Thing", "Context"});
|
||||||
|
assertCalled(mbeanLogIH, "getAttributes", disney);
|
||||||
|
|
||||||
|
ccmbs.setAttribute(logger, new Attribute("Thing", "bar"));
|
||||||
|
assertCalled(mbeanLogIH, "setAttribute", disney);
|
||||||
|
|
||||||
|
ccmbs.setAttributes(logger, new AttributeList(
|
||||||
|
Arrays.asList(new Attribute("Thing", "baz"))));
|
||||||
|
assertCalled(mbeanLogIH, "setAttributes", disney);
|
||||||
|
|
||||||
|
ccmbs.getMBeanInfo(logger);
|
||||||
|
assertCalled(mbeanLogIH, "getMBeanInfo", disney);
|
||||||
|
|
||||||
|
Set<ObjectName> names = ccmbs.queryNames(null, null);
|
||||||
|
Set<ObjectName> expectedNames = new HashSet<ObjectName>(
|
||||||
|
Collections.singleton(MBeanServerDelegate.DELEGATE_NAME));
|
||||||
|
assertEquals("context namespace query includes expected names",
|
||||||
|
true, names.containsAll(expectedNames));
|
||||||
|
|
||||||
|
Set<ObjectName> nsNames = ccmbs.queryNames(new ObjectName("*//:*"), null);
|
||||||
|
Set<ObjectName> expectedNsNames = new HashSet<ObjectName>(
|
||||||
|
Arrays.asList(
|
||||||
|
new ObjectName(ClientContext.NAMESPACE +
|
||||||
|
ObjectName.NAMESPACE_SEPARATOR + ":" +
|
||||||
|
JMXNamespace.TYPE_ASSIGNMENT)));
|
||||||
|
assertEquals("context namespace query includes namespace MBean",
|
||||||
|
true, nsNames.containsAll(expectedNsNames));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Set<ObjectInstance> insts = ccmbs.queryMBeans(
|
||||||
|
MBeanServerDelegate.DELEGATE_NAME, null);
|
||||||
|
assertEquals("size of set from MBeanServerDelegate query", 1, insts.size());
|
||||||
|
assertEquals("ObjectName from MBeanServerDelegate query",
|
||||||
|
MBeanServerDelegate.DELEGATE_NAME,
|
||||||
|
insts.iterator().next().getObjectName());
|
||||||
|
|
||||||
|
ObjectName createdName = new ObjectName("a:type=Created");
|
||||||
|
ObjectInstance createdOI =
|
||||||
|
ccmbs.createMBean(ShowContext.class.getName(), createdName);
|
||||||
|
assertEquals("class name from createMBean",
|
||||||
|
ShowContext.class.getName(), createdOI.getClassName());
|
||||||
|
assertEquals("ObjectName from createMBean",
|
||||||
|
createdName, createdOI.getObjectName());
|
||||||
|
assertEquals("context within createMBean",
|
||||||
|
disney, ccmbs.getAttribute(createdName, "CreationContext"));
|
||||||
|
|
||||||
|
NotificationListener nothingListener = new NotificationListener() {
|
||||||
|
public void handleNotification(Notification n, Object h) {}
|
||||||
|
};
|
||||||
|
ccmbs.addNotificationListener(createdName, nothingListener, null, null);
|
||||||
|
ccmbs.removeNotificationListener(createdName, nothingListener, null, null);
|
||||||
|
ccmbs.addNotificationListener(createdName, nothingListener, null, null);
|
||||||
|
ccmbs.removeNotificationListener(createdName, nothingListener);
|
||||||
|
Set<String> expectedOps = new HashSet<String>(Arrays.asList(
|
||||||
|
"preRegister", "postRegister", "addNotificationListener",
|
||||||
|
"removeNL1", "removeNL3", "getNotificationInfo"));
|
||||||
|
assertEquals("operations called on MBean",
|
||||||
|
expectedOps, ccmbs.getAttribute(createdName, "CalledOps"));
|
||||||
|
|
||||||
|
assertEquals("ClassLoader for MBean",
|
||||||
|
ShowContext.class.getClassLoader(),
|
||||||
|
ccmbs.getClassLoaderFor(createdName));
|
||||||
|
|
||||||
|
assertEquals("isRegistered", true, ccmbs.isRegistered(createdName));
|
||||||
|
assertEquals("isInstanceOf", true, ccmbs.isInstanceOf(createdName,
|
||||||
|
ShowContext.class.getName()));
|
||||||
|
assertEquals("isInstanceOf", false, ccmbs.isInstanceOf(createdName,
|
||||||
|
DynamicMBean.class.getName()));
|
||||||
|
ccmbs.unregisterMBean(createdName);
|
||||||
|
assertEquals("isRegistered after unregister",
|
||||||
|
false, ccmbs.isRegistered(createdName));
|
||||||
|
|
||||||
|
MLet mlet = new MLet();
|
||||||
|
ObjectName defaultMLetName = new ObjectName("DefaultDomain:type=MLet");
|
||||||
|
|
||||||
|
ccmbs.registerMBean(mlet, defaultMLetName);
|
||||||
|
|
||||||
|
assertEquals("getClassLoader", mlet, ccmbs.getClassLoader(defaultMLetName));
|
||||||
|
|
||||||
|
assertEquals("number of MBean operations", 0, mbeanLogIH.log.size());
|
||||||
|
|
||||||
|
// Test that contexts still work when we can't combine two encoded contexts.
|
||||||
|
// Here, we wrap cmbs (mickey=mouse) so that ccmbs2 (donald=duck) cannot
|
||||||
|
// see that it already contains a context and therefore cannot combine
|
||||||
|
// into mickey=mouse;donald=duck. We don't actually use the snoop
|
||||||
|
// capabilities of the returned object -- we just want an opaque
|
||||||
|
// MBeanServer wrapper
|
||||||
|
MBeanServer cmbs2 = newSnoop(MBeanServer.class, new LogIH(cmbs));
|
||||||
|
MBeanServer ccmbs2 = ClientContext.withContext(cmbs2, "donald", "duck");
|
||||||
|
assertEquals("context when combination is impossible",
|
||||||
|
disney, ccmbs2.getAttribute(name, "Context"));
|
||||||
|
|
||||||
|
// Test failure cases of ClientContext.encode
|
||||||
|
final List<Map<String, String>> badEncodeArgs =
|
||||||
|
Arrays.asList(
|
||||||
|
null,
|
||||||
|
Collections.<String,String>singletonMap(null, "foo"),
|
||||||
|
Collections.<String,String>singletonMap("foo", null));
|
||||||
|
for (Map<String, String> bad : badEncodeArgs) {
|
||||||
|
try {
|
||||||
|
String oops = ClientContext.encode(bad);
|
||||||
|
failed("ClientContext.encode(" + bad + ") should have failed: "
|
||||||
|
+ oops);
|
||||||
|
} catch (Exception e) {
|
||||||
|
assertEquals("Exception for ClientContext.encode(" + bad + ")",
|
||||||
|
IllegalArgumentException.class, e.getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ADD NEW TESTS HERE ^^^
|
||||||
|
|
||||||
|
if (failure != null)
|
||||||
|
throw new Exception(failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertEquals(String what, Object x, Object y) {
|
||||||
|
if (!equal(x, y))
|
||||||
|
failed(what + ": expected " + string(x) + "; got " + string(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean equal(Object x, Object y) {
|
||||||
|
if (x == y)
|
||||||
|
return true;
|
||||||
|
if (x == null || y == null)
|
||||||
|
return false;
|
||||||
|
if (x.getClass().isArray())
|
||||||
|
return Arrays.deepEquals(new Object[] {x}, new Object[] {y});
|
||||||
|
return x.equals(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String string(Object x) {
|
||||||
|
String s = Arrays.deepToString(new Object[] {x});
|
||||||
|
return s.substring(1, s.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertCalled(
|
||||||
|
LogIH logIH, String op, Map<String, String> expectedContext) {
|
||||||
|
assertCalled(logIH, op, expectedContext, (Object[]) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertCalled(
|
||||||
|
LogIH logIH, String op, Map<String, String> expectedContext,
|
||||||
|
Object... params) {
|
||||||
|
LogRecord lr = logIH.log.remove();
|
||||||
|
assertEquals("called operation", op, lr.op);
|
||||||
|
if (params != null)
|
||||||
|
assertEquals("operation parameters", params, lr.params);
|
||||||
|
assertEquals("operation context", expectedContext, lr.context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void failed(String why) {
|
||||||
|
failure = why;
|
||||||
|
new Throwable("FAILED: " + why).printStackTrace(System.out);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,328 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 Sun Microsystems, Inc. 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.
|
||||||
|
*
|
||||||
|
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||||
|
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||||
|
* have any questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @bug 5072267
|
||||||
|
* @summary Test that an MBean can handle localized Notification messages.
|
||||||
|
* @author Eamonn McManus
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.ListResourceBundle;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.MissingResourceException;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import javax.management.ClientContext;
|
||||||
|
import javax.management.JMX;
|
||||||
|
import javax.management.ListenerNotFoundException;
|
||||||
|
import javax.management.MBeanNotificationInfo;
|
||||||
|
import javax.management.MBeanServer;
|
||||||
|
import javax.management.MBeanServerConnection;
|
||||||
|
import javax.management.MBeanServerFactory;
|
||||||
|
import javax.management.Notification;
|
||||||
|
import javax.management.NotificationBroadcasterSupport;
|
||||||
|
import javax.management.NotificationEmitter;
|
||||||
|
import javax.management.NotificationFilter;
|
||||||
|
import javax.management.NotificationListener;
|
||||||
|
import javax.management.ObjectName;
|
||||||
|
import javax.management.SendNotification;
|
||||||
|
import javax.management.remote.JMXConnector;
|
||||||
|
import javax.management.remote.JMXConnectorFactory;
|
||||||
|
import javax.management.remote.JMXConnectorServer;
|
||||||
|
import javax.management.remote.JMXConnectorServerFactory;
|
||||||
|
import javax.management.remote.JMXServiceURL;
|
||||||
|
|
||||||
|
public class LocaleAwareBroadcasterTest {
|
||||||
|
static final ObjectName mbeanName = ObjectName.valueOf("d:type=LocaleAware");
|
||||||
|
|
||||||
|
static final String
|
||||||
|
messageKey = "broken.window",
|
||||||
|
defaultMessage = "broken window",
|
||||||
|
frenchMessage = "fen\u00eatre bris\u00e9e",
|
||||||
|
irishMessage = "fuinneog briste";
|
||||||
|
|
||||||
|
public static class Bundle extends ListResourceBundle {
|
||||||
|
@Override
|
||||||
|
protected Object[][] getContents() {
|
||||||
|
return new Object[][] {
|
||||||
|
{messageKey, defaultMessage},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Bundle_fr extends ListResourceBundle {
|
||||||
|
@Override
|
||||||
|
protected Object[][] getContents() {
|
||||||
|
return new Object[][] {
|
||||||
|
{messageKey, frenchMessage},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Bundle_ga extends ListResourceBundle {
|
||||||
|
@Override
|
||||||
|
protected Object[][] getContents() {
|
||||||
|
return new Object[][] {
|
||||||
|
{messageKey, irishMessage},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static volatile String failure;
|
||||||
|
|
||||||
|
public static interface LocaleAwareMBean {
|
||||||
|
public void sendNotification(Notification n);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LocaleAware
|
||||||
|
implements LocaleAwareMBean, NotificationEmitter, SendNotification {
|
||||||
|
|
||||||
|
private final ConcurrentMap<Locale, NotificationBroadcasterSupport>
|
||||||
|
localeToEmitter = newConcurrentMap();
|
||||||
|
|
||||||
|
public void sendNotification(Notification n) {
|
||||||
|
for (Map.Entry<Locale, NotificationBroadcasterSupport> entry :
|
||||||
|
localeToEmitter.entrySet()) {
|
||||||
|
Notification localizedNotif =
|
||||||
|
localizeNotification(n, entry.getKey());
|
||||||
|
entry.getValue().sendNotification(localizedNotif);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addNotificationListener(
|
||||||
|
NotificationListener listener,
|
||||||
|
NotificationFilter filter,
|
||||||
|
Object handback)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
Locale locale = ClientContext.getLocale();
|
||||||
|
NotificationBroadcasterSupport broadcaster;
|
||||||
|
broadcaster = localeToEmitter.get(locale);
|
||||||
|
if (broadcaster == null) {
|
||||||
|
broadcaster = new NotificationBroadcasterSupport();
|
||||||
|
NotificationBroadcasterSupport old =
|
||||||
|
localeToEmitter.putIfAbsent(locale, broadcaster);
|
||||||
|
if (old != null)
|
||||||
|
broadcaster = old;
|
||||||
|
}
|
||||||
|
broadcaster.addNotificationListener(listener, filter, handback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeNotificationListener(NotificationListener listener)
|
||||||
|
throws ListenerNotFoundException {
|
||||||
|
Locale locale = ClientContext.getLocale();
|
||||||
|
NotificationBroadcasterSupport broadcaster =
|
||||||
|
localeToEmitter.get(locale);
|
||||||
|
if (broadcaster == null)
|
||||||
|
throw new ListenerNotFoundException();
|
||||||
|
broadcaster.removeNotificationListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeNotificationListener(
|
||||||
|
NotificationListener listener,
|
||||||
|
NotificationFilter filter,
|
||||||
|
Object handback)
|
||||||
|
throws ListenerNotFoundException {
|
||||||
|
Locale locale = ClientContext.getLocale();
|
||||||
|
NotificationBroadcasterSupport broadcaster =
|
||||||
|
localeToEmitter.get(locale);
|
||||||
|
if (broadcaster == null)
|
||||||
|
throw new ListenerNotFoundException();
|
||||||
|
broadcaster.removeNotificationListener(listener, filter, handback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MBeanNotificationInfo[] getNotificationInfo() {
|
||||||
|
return new MBeanNotificationInfo[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Localize notif using the convention that the message looks like
|
||||||
|
// [resourcebundlename:resourcekey]defaultmessage
|
||||||
|
// for example [foo.bar.Resources:unknown.problem]
|
||||||
|
static Notification localizeNotification(Notification n, Locale locale) {
|
||||||
|
String msg = n.getMessage();
|
||||||
|
if (!msg.startsWith("["))
|
||||||
|
return n;
|
||||||
|
int close = msg.indexOf(']');
|
||||||
|
if (close < 0)
|
||||||
|
throw new IllegalArgumentException("Bad notification message: " + msg);
|
||||||
|
int colon = msg.indexOf(':');
|
||||||
|
if (colon < 0 || colon > close)
|
||||||
|
throw new IllegalArgumentException("Bad notification message: " + msg);
|
||||||
|
String bundleName = msg.substring(1, colon);
|
||||||
|
String key = msg.substring(colon + 1, close);
|
||||||
|
ClassLoader loader = LocaleAwareBroadcasterTest.class.getClassLoader();
|
||||||
|
ResourceBundle bundle =
|
||||||
|
ResourceBundle.getBundle(bundleName, locale, loader);
|
||||||
|
try {
|
||||||
|
msg = bundle.getString(key);
|
||||||
|
} catch (MissingResourceException e) {
|
||||||
|
msg = msg.substring(close + 1);
|
||||||
|
}
|
||||||
|
n = (Notification) n.clone();
|
||||||
|
n.setMessage(msg);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
Locale.setDefault(new Locale("en"));
|
||||||
|
testLocal();
|
||||||
|
testRemote();
|
||||||
|
if (failure == null)
|
||||||
|
System.out.println("TEST PASSED");
|
||||||
|
else
|
||||||
|
throw new Exception("TEST FAILED: " + failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
static interface AddListenerInLocale {
|
||||||
|
public void addListenerInLocale(
|
||||||
|
MBeanServerConnection mbsc,
|
||||||
|
NotificationListener listener,
|
||||||
|
Locale locale) throws Exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testLocal() throws Exception {
|
||||||
|
System.out.println("Test local MBeanServer using doWithContext");
|
||||||
|
MBeanServer mbs = makeMBS();
|
||||||
|
AddListenerInLocale addListener = new AddListenerInLocale() {
|
||||||
|
public void addListenerInLocale(
|
||||||
|
final MBeanServerConnection mbsc,
|
||||||
|
final NotificationListener listener,
|
||||||
|
Locale locale) throws Exception {
|
||||||
|
Map<String, String> localeContext = Collections.singletonMap(
|
||||||
|
ClientContext.LOCALE_KEY, locale.toString());
|
||||||
|
ClientContext.doWithContext(
|
||||||
|
localeContext, new Callable<Void>() {
|
||||||
|
public Void call() throws Exception {
|
||||||
|
mbsc.addNotificationListener(
|
||||||
|
mbeanName, listener, null, null);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
test(mbs, addListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testRemote() throws Exception {
|
||||||
|
System.out.println("Test remote MBeanServer using withLocale");
|
||||||
|
MBeanServer mbs = makeMBS();
|
||||||
|
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://");
|
||||||
|
JMXConnectorServer cs =
|
||||||
|
JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
|
||||||
|
cs.start();
|
||||||
|
JMXServiceURL addr = cs.getAddress();
|
||||||
|
JMXConnector cc = JMXConnectorFactory.connect(addr);
|
||||||
|
MBeanServerConnection mbsc = cc.getMBeanServerConnection();
|
||||||
|
AddListenerInLocale addListenerInLocale = new AddListenerInLocale() {
|
||||||
|
public void addListenerInLocale(
|
||||||
|
MBeanServerConnection mbsc,
|
||||||
|
NotificationListener listener,
|
||||||
|
Locale locale) throws Exception {
|
||||||
|
mbsc = ClientContext.withLocale(mbsc, locale);
|
||||||
|
mbsc.addNotificationListener(mbeanName, listener, null, null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
test(mbsc, addListenerInLocale);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
cc.close();
|
||||||
|
} catch (Exception e) {}
|
||||||
|
cs.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class QueueListener implements NotificationListener {
|
||||||
|
final BlockingQueue<Notification> queue =
|
||||||
|
new ArrayBlockingQueue<Notification>(10);
|
||||||
|
|
||||||
|
public void handleNotification(Notification notification,
|
||||||
|
Object handback) {
|
||||||
|
queue.add(notification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void test(
|
||||||
|
MBeanServerConnection mbsc, AddListenerInLocale addListener)
|
||||||
|
throws Exception {
|
||||||
|
QueueListener defaultListener = new QueueListener();
|
||||||
|
QueueListener frenchListener = new QueueListener();
|
||||||
|
QueueListener irishListener = new QueueListener();
|
||||||
|
mbsc.addNotificationListener(mbeanName, defaultListener, null, null);
|
||||||
|
addListener.addListenerInLocale(mbsc, frenchListener, new Locale("fr"));
|
||||||
|
addListener.addListenerInLocale(mbsc, irishListener, new Locale("ga"));
|
||||||
|
|
||||||
|
LocaleAwareMBean proxy =
|
||||||
|
JMX.newMBeanProxy(mbsc, mbeanName, LocaleAwareMBean.class);
|
||||||
|
String notifMsg = "[" + Bundle.class.getName() + ":" + messageKey + "]" +
|
||||||
|
"broken window (default message that should never be seen)";
|
||||||
|
Notification notif = new Notification(
|
||||||
|
"notif.type", mbeanName, 0L, notifMsg);
|
||||||
|
proxy.sendNotification(notif);
|
||||||
|
|
||||||
|
final Object[][] expected = {
|
||||||
|
{defaultListener, defaultMessage},
|
||||||
|
{frenchListener, frenchMessage},
|
||||||
|
{irishListener, irishMessage},
|
||||||
|
};
|
||||||
|
for (Object[] exp : expected) {
|
||||||
|
QueueListener ql = (QueueListener) exp[0];
|
||||||
|
String msg = (String) exp[1];
|
||||||
|
System.out.println("Checking: " + msg);
|
||||||
|
Notification n = ql.queue.poll(1, TimeUnit.SECONDS);
|
||||||
|
if (n == null)
|
||||||
|
fail("Did not receive expected notif: " + msg);
|
||||||
|
if (!n.getMessage().equals(msg)) {
|
||||||
|
fail("Received notif with wrong message: got " +
|
||||||
|
n.getMessage() + ", expected " + msg);
|
||||||
|
}
|
||||||
|
n = ql.queue.poll(2, TimeUnit.MILLISECONDS);
|
||||||
|
if (n != null)
|
||||||
|
fail("Received unexpected extra notif: " + n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MBeanServer makeMBS() throws Exception {
|
||||||
|
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
|
||||||
|
LocaleAware aware = new LocaleAware();
|
||||||
|
mbs.registerMBean(aware, mbeanName);
|
||||||
|
return mbs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static <K, V> ConcurrentMap<K, V> newConcurrentMap() {
|
||||||
|
return new ConcurrentHashMap<K, V>();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fail(String why) {
|
||||||
|
System.out.println("FAIL: " + why);
|
||||||
|
failure = why;
|
||||||
|
}
|
||||||
|
}
|
140
jdk/test/javax/management/context/LocaleTest.java
Normal file
140
jdk/test/javax/management/context/LocaleTest.java
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc. 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.
|
||||||
|
*
|
||||||
|
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||||
|
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||||
|
* have any questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test LocaleTest.java
|
||||||
|
* @bug 5072267
|
||||||
|
* @summary Test client locales.
|
||||||
|
* @author Eamonn McManus
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.ListResourceBundle;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import javax.management.ClientContext;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import javax.management.MBeanServer;
|
||||||
|
import javax.management.ObjectName;
|
||||||
|
|
||||||
|
public class LocaleTest {
|
||||||
|
private static String failure;
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
|
||||||
|
// Test the translation String -> Locale
|
||||||
|
|
||||||
|
Locale[] locales = Locale.getAvailableLocales();
|
||||||
|
System.out.println("Testing String->Locale for " + locales.length +
|
||||||
|
" locales");
|
||||||
|
for (Locale loc : locales) {
|
||||||
|
Map<String, String> ctx = Collections.singletonMap(
|
||||||
|
ClientContext.LOCALE_KEY, loc.toString());
|
||||||
|
Locale loc2 = ClientContext.doWithContext(
|
||||||
|
ctx, new Callable<Locale>() {
|
||||||
|
public Locale call() {
|
||||||
|
return ClientContext.getLocale();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assertEquals(loc, loc2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that a locale-sensitive attribute works
|
||||||
|
|
||||||
|
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
|
||||||
|
mbs = ClientContext.newContextForwarder(mbs, null);
|
||||||
|
ObjectName name = new ObjectName("a:type=LocaleSensitive");
|
||||||
|
mbs.registerMBean(new LocaleSensitive(), name);
|
||||||
|
Locale.setDefault(Locale.US);
|
||||||
|
|
||||||
|
assertEquals("spectacular failure",
|
||||||
|
mbs.getAttribute(name, "LastProblemDescription"));
|
||||||
|
|
||||||
|
MBeanServer frmbs = ClientContext.withContext(
|
||||||
|
mbs, ClientContext.LOCALE_KEY, Locale.FRANCE.toString());
|
||||||
|
assertEquals("\u00e9chec r\u00e9tentissant",
|
||||||
|
frmbs.getAttribute(name, "LastProblemDescription"));
|
||||||
|
|
||||||
|
if (failure == null)
|
||||||
|
System.out.println("TEST PASSED");
|
||||||
|
else
|
||||||
|
throw new Exception("TEST FAILED: " + failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface LocaleSensitiveMBean {
|
||||||
|
public String getLastProblemDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LocaleSensitive implements LocaleSensitiveMBean {
|
||||||
|
public String getLastProblemDescription() {
|
||||||
|
Locale loc = ClientContext.getLocale();
|
||||||
|
ResourceBundle rb = ResourceBundle.getBundle(
|
||||||
|
MyResources.class.getName(), loc);
|
||||||
|
return rb.getString("spectacular");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MyResources extends ListResourceBundle {
|
||||||
|
protected Object[][] getContents() {
|
||||||
|
return new Object[][] {
|
||||||
|
{"spectacular", "spectacular failure"},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MyResources_fr extends ListResourceBundle {
|
||||||
|
protected Object[][] getContents() {
|
||||||
|
return new Object[][] {
|
||||||
|
{"spectacular", "\u00e9chec r\u00e9tentissant"},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertEquals(Object x, Object y) {
|
||||||
|
if (!equal(x, y))
|
||||||
|
failed("expected " + string(x) + "; got " + string(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean equal(Object x, Object y) {
|
||||||
|
if (x == y)
|
||||||
|
return true;
|
||||||
|
if (x == null || y == null)
|
||||||
|
return false;
|
||||||
|
if (x.getClass().isArray())
|
||||||
|
return Arrays.deepEquals(new Object[] {x}, new Object[] {y});
|
||||||
|
return x.equals(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String string(Object x) {
|
||||||
|
String s = Arrays.deepToString(new Object[] {x});
|
||||||
|
return s.substring(1, s.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void failed(String why) {
|
||||||
|
failure = why;
|
||||||
|
new Throwable("FAILED: " + why).printStackTrace(System.out);
|
||||||
|
}
|
||||||
|
}
|
192
jdk/test/javax/management/context/LocalizableTest.java
Normal file
192
jdk/test/javax/management/context/LocalizableTest.java
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc. 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.
|
||||||
|
*
|
||||||
|
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||||
|
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||||
|
* have any questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test LocalizableTest
|
||||||
|
* @bug 5072267 6635499
|
||||||
|
* @summary Test localizable MBeanInfo using LocalizableMBeanFactory.
|
||||||
|
* @author Eamonn McManus
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
import javax.management.ClientContext;
|
||||||
|
import javax.management.Description;
|
||||||
|
import javax.management.JMX;
|
||||||
|
import javax.management.MBeanAttributeInfo;
|
||||||
|
import javax.management.MBeanConstructorInfo;
|
||||||
|
import javax.management.MBeanInfo;
|
||||||
|
import javax.management.MBeanOperationInfo;
|
||||||
|
import javax.management.MBeanParameterInfo;
|
||||||
|
import javax.management.MBeanServer;
|
||||||
|
import javax.management.ObjectName;
|
||||||
|
|
||||||
|
import localizable.MBeanDescriptions_fr;
|
||||||
|
import localizable.Whatsit;
|
||||||
|
|
||||||
|
import static localizable.WhatsitMBean.*;
|
||||||
|
|
||||||
|
public class LocalizableTest {
|
||||||
|
// If you change the order of the array elements or their number then
|
||||||
|
// you must also change these constants.
|
||||||
|
private static final int
|
||||||
|
MBEAN = 0, ATTR = 1, OPER = 2, PARAM = 3, CONSTR = 4,
|
||||||
|
CONSTR_PARAM = 5;
|
||||||
|
private static final String[] englishDescriptions = {
|
||||||
|
englishMBeanDescription, englishAttrDescription, englishOperDescription,
|
||||||
|
englishParamDescription, englishConstrDescription,
|
||||||
|
englishConstrParamDescription,
|
||||||
|
};
|
||||||
|
private static final String[] defaultDescriptions = englishDescriptions.clone();
|
||||||
|
static {
|
||||||
|
defaultDescriptions[MBEAN] = defaultMBeanDescription;
|
||||||
|
}
|
||||||
|
private static final String[] frenchDescriptions = {
|
||||||
|
frenchMBeanDescription, frenchAttrDescription, frenchOperDescription,
|
||||||
|
frenchParamDescription, frenchConstrDescription,
|
||||||
|
frenchConstrParamDescription,
|
||||||
|
};
|
||||||
|
|
||||||
|
private static String failure;
|
||||||
|
|
||||||
|
@Description(unlocalizedMBeanDescription)
|
||||||
|
public static interface UnlocalizedMBean {}
|
||||||
|
public static class Unlocalized implements UnlocalizedMBean {}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
ResourceBundle frenchBundle = new MBeanDescriptions_fr();
|
||||||
|
// The purpose of the previous line is to force that class to be compiled
|
||||||
|
// when the test is run so it will be available for reflection.
|
||||||
|
// Yes, we could do this with a @build tag.
|
||||||
|
|
||||||
|
MBeanServer plainMBS = ManagementFactory.getPlatformMBeanServer();
|
||||||
|
MBeanServer unlocalizedMBS =
|
||||||
|
ClientContext.newContextForwarder(plainMBS, null);
|
||||||
|
MBeanServer localizedMBS =
|
||||||
|
ClientContext.newLocalizeMBeanInfoForwarder(plainMBS);
|
||||||
|
localizedMBS = ClientContext.newContextForwarder(localizedMBS, null);
|
||||||
|
ObjectName name = new ObjectName("a:b=c");
|
||||||
|
|
||||||
|
Whatsit whatsit = new Whatsit();
|
||||||
|
Object[][] locales = {
|
||||||
|
{null, englishDescriptions},
|
||||||
|
{"en", englishDescriptions},
|
||||||
|
{"fr", frenchDescriptions},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (Object[] localePair : locales) {
|
||||||
|
String locale = (String) localePair[0];
|
||||||
|
String[] localizedDescriptions = (String[]) localePair[1];
|
||||||
|
System.out.println("===Testing locale " + locale + "===");
|
||||||
|
for (boolean localized : new boolean[] {false, true}) {
|
||||||
|
String[] descriptions = localized ?
|
||||||
|
localizedDescriptions : defaultDescriptions;
|
||||||
|
MBeanServer mbs = localized ? localizedMBS : unlocalizedMBS;
|
||||||
|
System.out.println("Testing MBean " + whatsit + " with " +
|
||||||
|
"localized=" + localized);
|
||||||
|
mbs.registerMBean(whatsit, name);
|
||||||
|
System.out.println(mbs.getMBeanInfo(name));
|
||||||
|
try {
|
||||||
|
test(mbs, name, locale, descriptions);
|
||||||
|
} catch (Exception e) {
|
||||||
|
fail("Caught exception: " + e);
|
||||||
|
} finally {
|
||||||
|
mbs.unregisterMBean(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("===Testing unlocalizable MBean===");
|
||||||
|
Object mbean = new Unlocalized();
|
||||||
|
localizedMBS.registerMBean(mbean, name);
|
||||||
|
try {
|
||||||
|
MBeanInfo mbi = localizedMBS.getMBeanInfo(name);
|
||||||
|
assertEquals("MBean description", unlocalizedMBeanDescription,
|
||||||
|
mbi.getDescription());
|
||||||
|
} finally {
|
||||||
|
localizedMBS.unregisterMBean(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("===Testing MBeanInfo.localizeDescriptions===");
|
||||||
|
plainMBS.registerMBean(whatsit, name);
|
||||||
|
MBeanInfo mbi = plainMBS.getMBeanInfo(name);
|
||||||
|
Locale french = new Locale("fr");
|
||||||
|
mbi = mbi.localizeDescriptions(french, whatsit.getClass().getClassLoader());
|
||||||
|
checkDescriptions(mbi, frenchDescriptions);
|
||||||
|
|
||||||
|
if (failure == null)
|
||||||
|
System.out.println("TEST PASSED");
|
||||||
|
else
|
||||||
|
throw new Exception("TEST FAILED: Last failure: " + failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void test(MBeanServer mbs, ObjectName name, String locale,
|
||||||
|
String[] expectedDescriptions)
|
||||||
|
throws Exception {
|
||||||
|
if (locale != null)
|
||||||
|
mbs = ClientContext.withLocale(mbs, new Locale(locale));
|
||||||
|
MBeanInfo mbi = mbs.getMBeanInfo(name);
|
||||||
|
checkDescriptions(mbi, expectedDescriptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkDescriptions(MBeanInfo mbi,
|
||||||
|
String[] expectedDescriptions) {
|
||||||
|
assertEquals("MBean description",
|
||||||
|
expectedDescriptions[MBEAN], mbi.getDescription());
|
||||||
|
MBeanAttributeInfo mbai = mbi.getAttributes()[0];
|
||||||
|
assertEquals("Attribute description",
|
||||||
|
expectedDescriptions[ATTR], mbai.getDescription());
|
||||||
|
MBeanOperationInfo mboi = mbi.getOperations()[0];
|
||||||
|
assertEquals("Operation description",
|
||||||
|
expectedDescriptions[OPER], mboi.getDescription());
|
||||||
|
MBeanParameterInfo mbpi = mboi.getSignature()[0];
|
||||||
|
assertEquals("Parameter description",
|
||||||
|
expectedDescriptions[PARAM], mbpi.getDescription());
|
||||||
|
MBeanConstructorInfo[] mbcis = mbi.getConstructors();
|
||||||
|
assertEquals("Number of constructors", 2, mbcis.length);
|
||||||
|
for (MBeanConstructorInfo mbci : mbcis) {
|
||||||
|
MBeanParameterInfo[] mbcpis = mbci.getSignature();
|
||||||
|
String constrName = mbcpis.length + "-arg constructor";
|
||||||
|
assertEquals(constrName + " description",
|
||||||
|
expectedDescriptions[CONSTR], mbci.getDescription());
|
||||||
|
if (mbcpis.length > 0) {
|
||||||
|
assertEquals(constrName + " parameter description",
|
||||||
|
expectedDescriptions[CONSTR_PARAM],
|
||||||
|
mbcpis[0].getDescription());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertEquals(String what, Object expect, Object actual) {
|
||||||
|
if (expect.equals(actual))
|
||||||
|
System.out.println("...OK: " + what + " = " + expect);
|
||||||
|
else
|
||||||
|
fail(what + " should be " + expect + ", was " + actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void fail(String why) {
|
||||||
|
System.out.println("FAIL: " + why);
|
||||||
|
failure = why;
|
||||||
|
}
|
||||||
|
}
|
496
jdk/test/javax/management/context/RemoteContextTest.java
Normal file
496
jdk/test/javax/management/context/RemoteContextTest.java
Normal file
@ -0,0 +1,496 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007-2008 Sun Microsystems, Inc. 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.
|
||||||
|
*
|
||||||
|
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||||
|
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||||
|
* have any questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test RemoteContextTest.java
|
||||||
|
* @bug 5072267
|
||||||
|
* @summary Test client contexts with namespaces.
|
||||||
|
* @author Eamonn McManus, Daniel Fuchs
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import javax.management.Attribute;
|
||||||
|
import javax.management.AttributeList;
|
||||||
|
import javax.management.ClientContext;
|
||||||
|
import javax.management.DynamicMBean;
|
||||||
|
import javax.management.JMX;
|
||||||
|
import javax.management.ListenerNotFoundException;
|
||||||
|
import javax.management.MBeanNotificationInfo;
|
||||||
|
import javax.management.MBeanRegistration;
|
||||||
|
import javax.management.MBeanServer;
|
||||||
|
import javax.management.MBeanServerConnection;
|
||||||
|
import javax.management.MBeanServerDelegate;
|
||||||
|
import javax.management.Notification;
|
||||||
|
import javax.management.NotificationBroadcasterSupport;
|
||||||
|
import javax.management.NotificationFilter;
|
||||||
|
import javax.management.NotificationListener;
|
||||||
|
import javax.management.ObjectInstance;
|
||||||
|
import javax.management.ObjectName;
|
||||||
|
import javax.management.StandardMBean;
|
||||||
|
import javax.management.loading.MLet;
|
||||||
|
import javax.management.namespace.JMXNamespaces;
|
||||||
|
import javax.management.namespace.JMXRemoteNamespace;
|
||||||
|
import javax.management.namespace.JMXNamespace;
|
||||||
|
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
|
import javax.management.MBeanServerFactory;
|
||||||
|
import javax.management.remote.JMXConnectorServer;
|
||||||
|
import javax.management.remote.JMXConnectorServerFactory;
|
||||||
|
import javax.management.remote.JMXServiceURL;
|
||||||
|
|
||||||
|
public class RemoteContextTest {
|
||||||
|
private static String failure;
|
||||||
|
|
||||||
|
public static interface ShowContextMBean {
|
||||||
|
public Map<String, String> getContext();
|
||||||
|
public Map<String, String> getCreationContext();
|
||||||
|
public Set<String> getCalledOps();
|
||||||
|
public String getThing();
|
||||||
|
public void setThing(String x);
|
||||||
|
public int add(int x, int y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ShowContext
|
||||||
|
extends NotificationBroadcasterSupport
|
||||||
|
implements ShowContextMBean, MBeanRegistration {
|
||||||
|
private final Map<String, String> creationContext;
|
||||||
|
private final Set<String> calledOps = new HashSet<String>();
|
||||||
|
|
||||||
|
public ShowContext() {
|
||||||
|
creationContext = getContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getContext() {
|
||||||
|
return ClientContext.getContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getCreationContext() {
|
||||||
|
return creationContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getCalledOps() {
|
||||||
|
return calledOps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getThing() {
|
||||||
|
return "x";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setThing(String x) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public int add(int x, int y) {
|
||||||
|
return x + y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectName preRegister(MBeanServer server, ObjectName name) {
|
||||||
|
assertEquals(creationContext, getContext());
|
||||||
|
calledOps.add("preRegister");
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void postRegister(Boolean registrationDone) {
|
||||||
|
assertEquals(creationContext, getContext());
|
||||||
|
calledOps.add("postRegister");
|
||||||
|
}
|
||||||
|
|
||||||
|
// The condition checked here is not guaranteed universally true,
|
||||||
|
// but is true every time we unregister an instance of this MBean
|
||||||
|
// in this test.
|
||||||
|
public void preDeregister() throws Exception {
|
||||||
|
assertEquals(creationContext, getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void postDeregister() {
|
||||||
|
assertEquals(creationContext, getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same remark as for preDeregister
|
||||||
|
@Override
|
||||||
|
public MBeanNotificationInfo[] getNotificationInfo() {
|
||||||
|
calledOps.add("getNotificationInfo");
|
||||||
|
return super.getNotificationInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addNotificationListener(
|
||||||
|
NotificationListener listener, NotificationFilter filter, Object handback) {
|
||||||
|
calledOps.add("addNotificationListener");
|
||||||
|
super.addNotificationListener(listener, filter, handback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeNotificationListener(
|
||||||
|
NotificationListener listener)
|
||||||
|
throws ListenerNotFoundException {
|
||||||
|
calledOps.add("removeNL1");
|
||||||
|
super.removeNotificationListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeNotificationListener(
|
||||||
|
NotificationListener listener, NotificationFilter filter, Object handback)
|
||||||
|
throws ListenerNotFoundException {
|
||||||
|
calledOps.add("removeNL3");
|
||||||
|
super.removeNotificationListener(listener, filter, handback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LogRecord {
|
||||||
|
final String op;
|
||||||
|
final Object[] params;
|
||||||
|
final Map<String, String> context;
|
||||||
|
LogRecord(String op, Object[] params, Map<String, String> context) {
|
||||||
|
this.op = op;
|
||||||
|
this.params = params;
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return op + Arrays.deepToString(params) + " " + context;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LogIH implements InvocationHandler {
|
||||||
|
private final Object wrapped;
|
||||||
|
Queue<LogRecord> log = new LinkedList<LogRecord>();
|
||||||
|
|
||||||
|
LogIH(Object wrapped) {
|
||||||
|
this.wrapped = wrapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object invoke(Object proxy, Method method, Object[] args)
|
||||||
|
throws Throwable {
|
||||||
|
if (method.getDeclaringClass() != Object.class) {
|
||||||
|
LogRecord lr =
|
||||||
|
new LogRecord(
|
||||||
|
method.getName(), args, ClientContext.getContext());
|
||||||
|
log.add(lr);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return method.invoke(wrapped, args);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
throw e.getCause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> T newSnoop(Class<T> wrappedClass, LogIH logIH) {
|
||||||
|
return wrappedClass.cast(Proxy.newProxyInstance(
|
||||||
|
wrappedClass.getClassLoader(),
|
||||||
|
new Class<?>[] {wrappedClass},
|
||||||
|
logIH));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
final String subnamespace = "sub";
|
||||||
|
final ObjectName locname = new ObjectName("a:b=c");
|
||||||
|
final ObjectName name = JMXNamespaces.insertPath(subnamespace,locname);
|
||||||
|
final MBeanServer mbs = ClientContext.newContextForwarder(
|
||||||
|
ManagementFactory.getPlatformMBeanServer(), null);
|
||||||
|
final MBeanServer sub = ClientContext.newContextForwarder(
|
||||||
|
MBeanServerFactory.newMBeanServer(), null);
|
||||||
|
final JMXServiceURL anonym = new JMXServiceURL("rmi",null,0);
|
||||||
|
final Map<String, Object> env = Collections.emptyMap();
|
||||||
|
final Map<String, String> emptyContext = Collections.emptyMap();
|
||||||
|
final JMXConnectorServer srv =
|
||||||
|
JMXConnectorServerFactory.newJMXConnectorServer(anonym,env,sub);
|
||||||
|
sub.registerMBean(new ShowContext(), locname);
|
||||||
|
|
||||||
|
srv.start();
|
||||||
|
|
||||||
|
try {
|
||||||
|
JMXRemoteNamespace subns = JMXRemoteNamespace.
|
||||||
|
newJMXRemoteNamespace(srv.getAddress(),null);
|
||||||
|
mbs.registerMBean(subns, JMXNamespaces.getNamespaceObjectName("sub"));
|
||||||
|
mbs.invoke(JMXNamespaces.getNamespaceObjectName("sub"),
|
||||||
|
"connect", null,null);
|
||||||
|
final ShowContextMBean show =
|
||||||
|
JMX.newMBeanProxy(mbs, name, ShowContextMBean.class);
|
||||||
|
|
||||||
|
assertEquals(emptyContext, show.getContext());
|
||||||
|
ClientContext.doWithContext(singletonMap("foo", "bar"), new Callable<Void>() {
|
||||||
|
public Void call() {
|
||||||
|
assertEquals(singletonMap("foo", "bar"), show.getContext());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assertEquals(emptyContext, show.getContext());
|
||||||
|
String got = ClientContext.doWithContext(
|
||||||
|
singletonMap("foo", "baz"), new Callable<String>() {
|
||||||
|
public String call() {
|
||||||
|
return ClientContext.getContext().get("foo");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assertEquals("baz", got);
|
||||||
|
|
||||||
|
Map<String, String> combined = ClientContext.doWithContext(
|
||||||
|
singletonMap("foo", "baz"), new Callable<Map<String, String>>() {
|
||||||
|
public Map<String, String> call() throws Exception {
|
||||||
|
return ClientContext.doWithContext(
|
||||||
|
singletonMap("fred", "jim"),
|
||||||
|
new Callable<Map<String, String>>() {
|
||||||
|
public Map<String, String> call() {
|
||||||
|
return ClientContext.getContext();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assertEquals(singletonMap("fred", "jim"), combined);
|
||||||
|
|
||||||
|
final String ugh = "a!?//*=:\"% ";
|
||||||
|
ClientContext.doWithContext(singletonMap(ugh, ugh), new Callable<Void>() {
|
||||||
|
public Void call() {
|
||||||
|
assertEquals(Collections.singletonMap(ugh, ugh),
|
||||||
|
ClientContext.getContext());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Basic withContext tests
|
||||||
|
|
||||||
|
LogIH mbsIH = new LogIH(mbs);
|
||||||
|
MBeanServer snoopMBS = newSnoop(MBeanServer.class, mbsIH);
|
||||||
|
MBeanServer ughMBS = ClientContext.withContext(snoopMBS, ugh, ugh);
|
||||||
|
// ughMBS is never referenced but we check that the withContext call
|
||||||
|
// included a call to snoopMBS.isRegistered.
|
||||||
|
String encodedUgh = URLEncoder.encode(ugh, "UTF-8").replace("*", "%2A");
|
||||||
|
ObjectName expectedName = new ObjectName(
|
||||||
|
ClientContext.NAMESPACE + ObjectName.NAMESPACE_SEPARATOR +
|
||||||
|
encodedUgh + "=" + encodedUgh +
|
||||||
|
ObjectName.NAMESPACE_SEPARATOR + ":" +
|
||||||
|
JMXNamespace.TYPE_ASSIGNMENT);
|
||||||
|
assertCalled(mbsIH, "isRegistered", new Object[] {expectedName},
|
||||||
|
emptyContext);
|
||||||
|
|
||||||
|
// Test withDynamicContext
|
||||||
|
|
||||||
|
MBeanServerConnection dynamicSnoop =
|
||||||
|
ClientContext.withDynamicContext(snoopMBS);
|
||||||
|
assertCalled(mbsIH, "isRegistered",
|
||||||
|
new Object[] {
|
||||||
|
JMXNamespaces.getNamespaceObjectName(ClientContext.NAMESPACE)
|
||||||
|
},
|
||||||
|
emptyContext);
|
||||||
|
final ShowContextMBean dynamicShow =
|
||||||
|
JMX.newMBeanProxy(dynamicSnoop, name, ShowContextMBean.class);
|
||||||
|
assertEquals(Collections.emptyMap(), dynamicShow.getContext());
|
||||||
|
assertCalled(mbsIH, "getAttribute", new Object[] {name, "Context"},
|
||||||
|
emptyContext);
|
||||||
|
|
||||||
|
Map<String, String> expectedDynamic =
|
||||||
|
Collections.singletonMap("gladstone", "gander");
|
||||||
|
Map<String, String> dynamic = ClientContext.doWithContext(
|
||||||
|
expectedDynamic,
|
||||||
|
new Callable<Map<String, String>>() {
|
||||||
|
public Map<String, String> call() throws Exception {
|
||||||
|
return dynamicShow.getContext();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assertEquals(expectedDynamic, dynamic);
|
||||||
|
ObjectName expectedDynamicName = new ObjectName(
|
||||||
|
ClientContext.encode(expectedDynamic) +
|
||||||
|
ObjectName.NAMESPACE_SEPARATOR + name);
|
||||||
|
assertCalled(mbsIH, "getAttribute",
|
||||||
|
new Object[] {expectedDynamicName, "Context"}, dynamic);
|
||||||
|
|
||||||
|
MBeanServer cmbs = ClientContext.withContext(
|
||||||
|
mbs, "mickey", "mouse");
|
||||||
|
ShowContextMBean cshow =
|
||||||
|
JMX.newMBeanProxy(cmbs, name, ShowContextMBean.class);
|
||||||
|
assertEquals(Collections.singletonMap("mickey", "mouse"), cshow.getContext());
|
||||||
|
|
||||||
|
MBeanServer ccmbs = ClientContext.withContext(
|
||||||
|
cmbs, "donald", "duck");
|
||||||
|
ShowContextMBean ccshow =
|
||||||
|
JMX.newMBeanProxy(ccmbs, name, ShowContextMBean.class);
|
||||||
|
Map<String, String> disney = new HashMap<String, String>();
|
||||||
|
disney.put("mickey", "mouse");
|
||||||
|
disney.put("donald", "duck");
|
||||||
|
assertEquals(disney, ccshow.getContext());
|
||||||
|
|
||||||
|
// Test that all MBS ops produce reasonable results
|
||||||
|
|
||||||
|
ObjectName logger = new ObjectName("a:type=Logger");
|
||||||
|
DynamicMBean showMBean =
|
||||||
|
new StandardMBean(new ShowContext(), ShowContextMBean.class);
|
||||||
|
LogIH mbeanLogIH = new LogIH(showMBean);
|
||||||
|
DynamicMBean logMBean = newSnoop(DynamicMBean.class, mbeanLogIH);
|
||||||
|
ObjectInstance loggerOI = ccmbs.registerMBean(logMBean, logger);
|
||||||
|
assertEquals(logger, loggerOI.getObjectName());
|
||||||
|
|
||||||
|
// We get a getMBeanInfo call to determine the className in the
|
||||||
|
// ObjectInstance to return from registerMBean.
|
||||||
|
assertCalled(mbeanLogIH, "getMBeanInfo", disney);
|
||||||
|
|
||||||
|
ccmbs.getAttribute(logger, "Thing");
|
||||||
|
assertCalled(mbeanLogIH, "getAttribute", disney);
|
||||||
|
|
||||||
|
ccmbs.getAttributes(logger, new String[] {"Thing", "Context"});
|
||||||
|
assertCalled(mbeanLogIH, "getAttributes", disney);
|
||||||
|
|
||||||
|
ccmbs.setAttribute(logger, new Attribute("Thing", "bar"));
|
||||||
|
assertCalled(mbeanLogIH, "setAttribute", disney);
|
||||||
|
|
||||||
|
ccmbs.setAttributes(logger, new AttributeList(
|
||||||
|
Arrays.asList(new Attribute("Thing", "baz"))));
|
||||||
|
assertCalled(mbeanLogIH, "setAttributes", disney);
|
||||||
|
|
||||||
|
ccmbs.getMBeanInfo(logger);
|
||||||
|
assertCalled(mbeanLogIH, "getMBeanInfo", disney);
|
||||||
|
|
||||||
|
Set<ObjectName> names = ccmbs.queryNames(null, null);
|
||||||
|
Set<ObjectName> expectedNames = new HashSet<ObjectName>(
|
||||||
|
Collections.singleton(MBeanServerDelegate.DELEGATE_NAME));
|
||||||
|
expectedNames.removeAll(names);
|
||||||
|
assertEquals(0, expectedNames.size());
|
||||||
|
|
||||||
|
Set<ObjectName> nsNames =
|
||||||
|
ccmbs.queryNames(new ObjectName("**?*?//:*"), null);
|
||||||
|
Set<ObjectName> expectedNsNames = new HashSet<ObjectName>(
|
||||||
|
Arrays.asList(
|
||||||
|
new ObjectName(ClientContext.NAMESPACE +
|
||||||
|
ObjectName.NAMESPACE_SEPARATOR + ":" +
|
||||||
|
JMXNamespace.TYPE_ASSIGNMENT)));
|
||||||
|
expectedNsNames.removeAll(nsNames);
|
||||||
|
assertEquals(0, expectedNsNames.size());
|
||||||
|
|
||||||
|
Set<ObjectInstance> insts = ccmbs.queryMBeans(
|
||||||
|
MBeanServerDelegate.DELEGATE_NAME, null);
|
||||||
|
assertEquals(1, insts.size());
|
||||||
|
assertEquals(MBeanServerDelegate.DELEGATE_NAME,
|
||||||
|
insts.iterator().next().getObjectName());
|
||||||
|
|
||||||
|
ObjectName createdName = new ObjectName("a:type=Created");
|
||||||
|
ObjectInstance createdOI =
|
||||||
|
ccmbs.createMBean(ShowContext.class.getName(), createdName);
|
||||||
|
assertEquals(ShowContext.class.getName(), createdOI.getClassName());
|
||||||
|
assertEquals(createdName, createdOI.getObjectName());
|
||||||
|
assertEquals(disney, ccmbs.getAttribute(createdName, "CreationContext"));
|
||||||
|
|
||||||
|
NotificationListener nothingListener = new NotificationListener() {
|
||||||
|
public void handleNotification(Notification n, Object h) {}
|
||||||
|
};
|
||||||
|
ccmbs.addNotificationListener(createdName, nothingListener, null, null);
|
||||||
|
ccmbs.removeNotificationListener(createdName, nothingListener, null, null);
|
||||||
|
ccmbs.addNotificationListener(createdName, nothingListener, null, null);
|
||||||
|
ccmbs.removeNotificationListener(createdName, nothingListener);
|
||||||
|
Set<String> expectedOps = new HashSet<String>(Arrays.asList(
|
||||||
|
"preRegister", "postRegister", "addNotificationListener",
|
||||||
|
"removeNL1", "removeNL3", "getNotificationInfo"));
|
||||||
|
assertEquals(expectedOps, ccmbs.getAttribute(createdName, "CalledOps"));
|
||||||
|
|
||||||
|
assertEquals(ShowContext.class.getClassLoader(),
|
||||||
|
ccmbs.getClassLoaderFor(createdName));
|
||||||
|
|
||||||
|
assertEquals(true, ccmbs.isRegistered(createdName));
|
||||||
|
assertEquals(true, ccmbs.isInstanceOf(createdName,
|
||||||
|
ShowContext.class.getName()));
|
||||||
|
assertEquals(false, ccmbs.isInstanceOf(createdName,
|
||||||
|
DynamicMBean.class.getName()));
|
||||||
|
ccmbs.unregisterMBean(createdName);
|
||||||
|
assertEquals(false, ccmbs.isRegistered(createdName));
|
||||||
|
|
||||||
|
MLet mlet = new MLet();
|
||||||
|
ObjectName defaultMLetName = new ObjectName("DefaultDomain:type=MLet");
|
||||||
|
|
||||||
|
ccmbs.registerMBean(mlet, defaultMLetName);
|
||||||
|
|
||||||
|
assertEquals(mlet, ccmbs.getClassLoader(defaultMLetName));
|
||||||
|
|
||||||
|
assertEquals(0, mbeanLogIH.log.size());
|
||||||
|
|
||||||
|
// Test that contexts still work when we can't combine two encoded contexts.
|
||||||
|
// Here, we wrap cmbs (mickey=mouse) so that ccmbs2 (donald=duck) cannot
|
||||||
|
// see that it already contains a context and therefore cannot combine
|
||||||
|
// into mickey=mouse;donald=duck. We don't actually use the snoop
|
||||||
|
// capabilities of the returned object -- we just want an opaque
|
||||||
|
// MBeanServer wrapper
|
||||||
|
MBeanServer cmbs2 = newSnoop(MBeanServer.class, new LogIH(cmbs));
|
||||||
|
MBeanServer ccmbs2 = ClientContext.withContext(cmbs2, "donald", "duck");
|
||||||
|
assertEquals(disney, ccmbs2.getAttribute(name, "Context"));
|
||||||
|
|
||||||
|
// ADD NEW TESTS HERE ^^^
|
||||||
|
|
||||||
|
if (failure != null)
|
||||||
|
throw new Exception(failure);
|
||||||
|
} finally {
|
||||||
|
srv.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertEquals(Object x, Object y) {
|
||||||
|
if (!equal(x, y))
|
||||||
|
failed("expected " + string(x) + "; got " + string(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean equal(Object x, Object y) {
|
||||||
|
if (x == y)
|
||||||
|
return true;
|
||||||
|
if (x == null || y == null)
|
||||||
|
return false;
|
||||||
|
if (x.getClass().isArray())
|
||||||
|
return Arrays.deepEquals(new Object[] {x}, new Object[] {y});
|
||||||
|
return x.equals(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String string(Object x) {
|
||||||
|
String s = Arrays.deepToString(new Object[] {x});
|
||||||
|
return s.substring(1, s.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertCalled(
|
||||||
|
LogIH logIH, String op, Map<String, String> expectedContext) {
|
||||||
|
assertCalled(logIH, op, null, expectedContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertCalled(
|
||||||
|
LogIH logIH, String op, Object[] params,
|
||||||
|
Map<String, String> expectedContext) {
|
||||||
|
LogRecord lr = logIH.log.remove();
|
||||||
|
assertEquals(op, lr.op);
|
||||||
|
if (params != null)
|
||||||
|
assertEquals(params, lr.params);
|
||||||
|
assertEquals(expectedContext, lr.context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void failed(String why) {
|
||||||
|
failure = why;
|
||||||
|
new Throwable("FAILED: " + why).printStackTrace(System.out);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
# This is the default description ResourceBundle for MBeans in this package.
|
||||||
|
# Resources here override the descriptions specified with @Description
|
||||||
|
# but only when localization is happening and when there is not a more
|
||||||
|
# specific resource for the description (for example from MBeanDescriptions_fr).
|
||||||
|
|
||||||
|
WhatsitMBean.mbean = A whatsit
|
||||||
|
# This must be the same as WhatsitMBean.englishMBeanDescription for the
|
||||||
|
# purposes of this test.
|
||||||
|
|
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc. 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.
|
||||||
|
*
|
||||||
|
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||||
|
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||||
|
* have any questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package localizable;
|
||||||
|
|
||||||
|
import java.util.ListResourceBundle;
|
||||||
|
import static localizable.WhatsitMBean.*;
|
||||||
|
|
||||||
|
public class MBeanDescriptions_fr extends ListResourceBundle {
|
||||||
|
@Override
|
||||||
|
protected Object[][] getContents() {
|
||||||
|
String constrProp = "WhatsitMBean.constructor." + Whatsit.class.getName();
|
||||||
|
return new Object[][] {
|
||||||
|
{"WhatsitMBean.mbean", frenchMBeanDescription},
|
||||||
|
{"WhatsitMBean.attribute.Whatsit", frenchAttrDescription},
|
||||||
|
{"WhatsitMBean.operation.frob", frenchOperDescription},
|
||||||
|
{"WhatsitMBean.operation.frob.p1", frenchParamDescription},
|
||||||
|
{constrProp, frenchConstrDescription},
|
||||||
|
{constrProp + ".p1", frenchConstrParamDescription},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
59
jdk/test/javax/management/context/localizable/Whatsit.java
Normal file
59
jdk/test/javax/management/context/localizable/Whatsit.java
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc. 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.
|
||||||
|
*
|
||||||
|
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||||
|
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||||
|
* have any questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package localizable;
|
||||||
|
|
||||||
|
import javax.management.Description;
|
||||||
|
|
||||||
|
public class Whatsit implements WhatsitMBean {
|
||||||
|
/**
|
||||||
|
* Attribute : NewAttribute0
|
||||||
|
*/
|
||||||
|
private String newAttribute0;
|
||||||
|
@Description(englishConstrDescription)
|
||||||
|
public Whatsit() {}
|
||||||
|
|
||||||
|
@Description(englishConstrDescription)
|
||||||
|
public Whatsit(@Description(englishConstrParamDescription) int type) {}
|
||||||
|
|
||||||
|
public String getWhatsit() {
|
||||||
|
return "whatsit";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void frob(String whatsit) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Tiddly
|
||||||
|
*/
|
||||||
|
public String getNewAttribute0() {
|
||||||
|
return newAttribute0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Tiddly
|
||||||
|
*/
|
||||||
|
public void setNewAttribute0(String value) {
|
||||||
|
newAttribute0 = value;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc. 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.
|
||||||
|
*
|
||||||
|
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||||
|
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||||
|
* have any questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package localizable;
|
||||||
|
|
||||||
|
import javax.management.Description;
|
||||||
|
|
||||||
|
@Description(WhatsitMBean.defaultMBeanDescription)
|
||||||
|
public interface WhatsitMBean {
|
||||||
|
public static final String
|
||||||
|
defaultMBeanDescription = "Default whatsit MBean description",
|
||||||
|
englishMBeanDescription = "A whatsit",
|
||||||
|
// Previous description appears in MBeanDescriptions.properties
|
||||||
|
// so it overrides the @Description when that file is used.
|
||||||
|
frenchMBeanDescription = "Un bidule",
|
||||||
|
englishAttrDescription = "The whatsit",
|
||||||
|
frenchAttrDescription = "Le bidule",
|
||||||
|
englishOperDescription = "Frob the whatsit",
|
||||||
|
frenchOperDescription = "Frober le bidule",
|
||||||
|
englishParamDescription = "The whatsit to frob",
|
||||||
|
frenchParamDescription = "Le bidule \u00e0 frober",
|
||||||
|
englishConstrDescription = "Make a whatsit",
|
||||||
|
frenchConstrDescription = "Fabriquer un bidule",
|
||||||
|
englishConstrParamDescription = "Type of whatsit to make",
|
||||||
|
frenchConstrParamDescription = "Type de bidule \u00e0 fabriquer",
|
||||||
|
unlocalizedMBeanDescription = "Unlocalized MBean";
|
||||||
|
|
||||||
|
@Description(englishAttrDescription)
|
||||||
|
public String getWhatsit();
|
||||||
|
|
||||||
|
@Description(englishOperDescription)
|
||||||
|
public void frob(@Description(englishParamDescription) String whatsit);
|
||||||
|
}
|
@ -200,8 +200,7 @@ public class CustomForwarderTest {
|
|||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
|
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
|
||||||
MBeanServerForwarder mbsf = EventClientDelegate.newForwarder();
|
MBeanServerForwarder mbsf = EventClientDelegate.newForwarder(mbs, null);
|
||||||
mbsf.setMBeanServer(mbs);
|
|
||||||
mbs = mbsf;
|
mbs = mbsf;
|
||||||
|
|
||||||
// for 1.5
|
// for 1.5
|
||||||
|
@ -65,8 +65,7 @@ public class EventClientExecutorTest {
|
|||||||
new NamedThreadFactory("LEASE"));
|
new NamedThreadFactory("LEASE"));
|
||||||
|
|
||||||
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
|
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
|
||||||
MBeanServerForwarder mbsf = EventClientDelegate.newForwarder();
|
MBeanServerForwarder mbsf = EventClientDelegate.newForwarder(mbs, null);
|
||||||
mbsf.setMBeanServer(mbs);
|
|
||||||
mbs = mbsf;
|
mbs = mbsf;
|
||||||
|
|
||||||
EventClientDelegateMBean ecd = EventClientDelegate.getProxy(mbs);
|
EventClientDelegateMBean ecd = EventClientDelegate.getProxy(mbs);
|
||||||
|
@ -98,7 +98,7 @@ public class EventManagerTest {
|
|||||||
succeed &= test(new EventClient(ecd,
|
succeed &= test(new EventClient(ecd,
|
||||||
new RMIPushEventRelay(ecd),
|
new RMIPushEventRelay(ecd),
|
||||||
null, null,
|
null, null,
|
||||||
EventClient.DEFAULT_LEASE_TIMEOUT));
|
EventClient.DEFAULT_REQUESTED_LEASE_TIME));
|
||||||
|
|
||||||
conn.close();
|
conn.close();
|
||||||
server.stop();
|
server.stop();
|
||||||
|
@ -99,7 +99,7 @@ public class ListenerTest {
|
|||||||
succeed &= test(new EventClient(ecd,
|
succeed &= test(new EventClient(ecd,
|
||||||
new RMIPushEventRelay(ecd),
|
new RMIPushEventRelay(ecd),
|
||||||
null, null,
|
null, null,
|
||||||
EventClient.DEFAULT_LEASE_TIMEOUT));
|
EventClient.DEFAULT_REQUESTED_LEASE_TIME));
|
||||||
|
|
||||||
conn.close();
|
conn.close();
|
||||||
server.stop();
|
server.stop();
|
||||||
|
@ -95,7 +95,7 @@ public class NotSerializableNotifTest {
|
|||||||
FetchingEventRelay.DEFAULT_MAX_NOTIFICATIONS,
|
FetchingEventRelay.DEFAULT_MAX_NOTIFICATIONS,
|
||||||
null);
|
null);
|
||||||
EventClient ec = new EventClient(ecd, eventRelay, null, null,
|
EventClient ec = new EventClient(ecd, eventRelay, null, null,
|
||||||
EventClient.DEFAULT_LEASE_TIMEOUT);
|
EventClient.DEFAULT_REQUESTED_LEASE_TIME);
|
||||||
|
|
||||||
// add listener from the client side
|
// add listener from the client side
|
||||||
Listener listener = new Listener();
|
Listener listener = new Listener();
|
||||||
|
@ -1,3 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 Sun Microsystems, Inc. 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.
|
||||||
|
*
|
||||||
|
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||||
|
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||||
|
* have any questions.
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @test UsingEventService.java 1.10 08/01/22
|
* @test UsingEventService.java 1.10 08/01/22
|
||||||
* @bug 5108776
|
* @bug 5108776
|
||||||
|
@ -85,6 +85,7 @@ public class EventWithNamespaceControlTest extends EventWithNamespaceTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Map<String, ?> getServerMap() {
|
public Map<String, ?> getServerMap() {
|
||||||
Map<String, ?> retValue = Collections.emptyMap();
|
Map<String, ?> retValue = Collections.emptyMap();
|
||||||
return retValue;
|
return retValue;
|
||||||
|
@ -51,6 +51,7 @@ import javax.management.namespace.JMXDomain;
|
|||||||
import javax.management.namespace.JMXNamespace;
|
import javax.management.namespace.JMXNamespace;
|
||||||
import javax.management.namespace.JMXNamespaces;
|
import javax.management.namespace.JMXNamespaces;
|
||||||
import javax.management.remote.JMXConnectorServer;
|
import javax.management.remote.JMXConnectorServer;
|
||||||
|
import javax.management.ClientContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -42,6 +42,7 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import javax.management.ClientContext;
|
||||||
import javax.management.JMException;
|
import javax.management.JMException;
|
||||||
import javax.management.MBeanRegistration;
|
import javax.management.MBeanRegistration;
|
||||||
import javax.management.MBeanServer;
|
import javax.management.MBeanServer;
|
||||||
@ -62,11 +63,6 @@ import javax.management.remote.JMXServiceURL;
|
|||||||
*/
|
*/
|
||||||
public class JMXNamespaceViewTest {
|
public class JMXNamespaceViewTest {
|
||||||
|
|
||||||
// TODO: Remove this when contexts are added.
|
|
||||||
public static class ClientContext {
|
|
||||||
public final static String NAMESPACE = "jmx.context";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describe the configuration of a namespace
|
* Describe the configuration of a namespace
|
||||||
*/
|
*/
|
||||||
|
@ -68,13 +68,7 @@ public class JMXRemoteTargetNamespace extends JMXRemoteNamespace {
|
|||||||
|
|
||||||
public JMXRemoteTargetNamespace(JMXServiceURL sourceURL,
|
public JMXRemoteTargetNamespace(JMXServiceURL sourceURL,
|
||||||
Map<String,?> optionsMap, String sourceNamespace) {
|
Map<String,?> optionsMap, String sourceNamespace) {
|
||||||
this(sourceURL,optionsMap,sourceNamespace,false);
|
super(sourceURL, optionsMap);
|
||||||
}
|
|
||||||
|
|
||||||
public JMXRemoteTargetNamespace(JMXServiceURL sourceURL,
|
|
||||||
Map<String,?> optionsMap, String sourceNamespace,
|
|
||||||
boolean createEventClient) {
|
|
||||||
super(sourceURL,optionsMap);
|
|
||||||
this.sourceNamespace = sourceNamespace;
|
this.sourceNamespace = sourceNamespace;
|
||||||
this.createEventClient = createEventClient(optionsMap);
|
this.createEventClient = createEventClient(optionsMap);
|
||||||
}
|
}
|
||||||
@ -92,14 +86,14 @@ public class JMXRemoteTargetNamespace extends JMXRemoteNamespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected JMXConnector newJMXConnector(JMXServiceURL url,
|
protected MBeanServerConnection getMBeanServerConnection(JMXConnector jmxc)
|
||||||
Map<String, ?> env) throws IOException {
|
throws IOException {
|
||||||
JMXConnector sup = super.newJMXConnector(url, env);
|
MBeanServerConnection mbsc = super.getMBeanServerConnection(jmxc);
|
||||||
if (sourceNamespace == null || "".equals(sourceNamespace))
|
if (sourceNamespace != null && sourceNamespace.length() > 0)
|
||||||
return sup;
|
mbsc = JMXNamespaces.narrowToNamespace(mbsc, sourceNamespace);
|
||||||
if (createEventClient)
|
if (createEventClient)
|
||||||
sup = EventClient.withEventClient(sup);
|
mbsc = EventClient.getEventClientConnection(mbsc);
|
||||||
return JMXNamespaces.narrowToNamespace(sup, sourceNamespace);
|
return mbsc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -206,18 +206,10 @@ public class NamespaceNotificationsTest {
|
|||||||
aconn.addNotificationListener(deep,listener,null,deep);
|
aconn.addNotificationListener(deep,listener,null,deep);
|
||||||
|
|
||||||
|
|
||||||
final JMXServiceURL urlx = new JMXServiceURL(url1.toString());
|
MBeanServerConnection iconn =
|
||||||
System.out.println("conn: "+urlx);
|
JMXNamespaces.narrowToNamespace(aconn, "server1//server1");
|
||||||
final JMXConnector jc2 = JMXNamespaces.narrowToNamespace(
|
MBeanServerConnection bconn =
|
||||||
JMXConnectorFactory.connect(urlx),"server1//server1");
|
JMXNamespaces.narrowToNamespace(aconn, "server3");
|
||||||
final JMXConnector jc3 = JMXNamespaces.narrowToNamespace(jc2,"server3");
|
|
||||||
jc3.connect();
|
|
||||||
System.out.println("JC#3: " +
|
|
||||||
((jc3 instanceof JMXAddressable)?
|
|
||||||
((JMXAddressable)jc3).getAddress():
|
|
||||||
jc3.toString()));
|
|
||||||
final MBeanServerConnection bconn =
|
|
||||||
jc3.getMBeanServerConnection();
|
|
||||||
final ObjectName shallow =
|
final ObjectName shallow =
|
||||||
new ObjectName("bush:"+
|
new ObjectName("bush:"+
|
||||||
deep.getKeyPropertyListString());
|
deep.getKeyPropertyListString());
|
||||||
|
@ -155,7 +155,7 @@ public class NullDomainObjectNameTest {
|
|||||||
// namespace.
|
// namespace.
|
||||||
//
|
//
|
||||||
RoutingServerProxy proxy =
|
RoutingServerProxy proxy =
|
||||||
new RoutingServerProxy(sub, "", "faked", false);
|
new RoutingServerProxy(sub, "", "faked", true);
|
||||||
|
|
||||||
// These should fail because the ObjectName doesn't start
|
// These should fail because the ObjectName doesn't start
|
||||||
// with "faked//"
|
// with "faked//"
|
||||||
|
@ -162,7 +162,7 @@ public class NullObjectNameTest {
|
|||||||
// this case.
|
// this case.
|
||||||
//
|
//
|
||||||
RoutingServerProxy proxy =
|
RoutingServerProxy proxy =
|
||||||
new RoutingServerProxy(sub,"","faked",false);
|
new RoutingServerProxy(sub, "", "faked", true);
|
||||||
final ObjectInstance moi3 =
|
final ObjectInstance moi3 =
|
||||||
proxy.registerMBean(new MyWombat(),null);
|
proxy.registerMBean(new MyWombat(),null);
|
||||||
System.out.println(moi3.getObjectName().toString()+
|
System.out.println(moi3.getObjectName().toString()+
|
||||||
|
@ -21,19 +21,19 @@
|
|||||||
* have any questions.
|
* have any questions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import javax.management.openmbean.CompositeType;
|
|
||||||
import javax.management.openmbean.OpenType;
|
|
||||||
import javax.management.openmbean.SimpleType;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @bug 6610174
|
* @bug 6610174
|
||||||
* @summary Test that CompositeDataSupport.toString() represents arrays correctly
|
* @summary Test that CompositeDataSupport.toString() represents arrays correctly
|
||||||
* @author Eamonn McManus
|
* @author Eamonn McManus
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import javax.management.openmbean.ArrayType;
|
import javax.management.openmbean.ArrayType;
|
||||||
import javax.management.openmbean.CompositeData;
|
import javax.management.openmbean.CompositeData;
|
||||||
import javax.management.openmbean.CompositeDataSupport;
|
import javax.management.openmbean.CompositeDataSupport;
|
||||||
|
import javax.management.openmbean.CompositeType;
|
||||||
|
import javax.management.openmbean.OpenType;
|
||||||
|
import javax.management.openmbean.SimpleType;
|
||||||
|
|
||||||
public class CompositeDataStringTest {
|
public class CompositeDataStringTest {
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
@ -86,16 +86,20 @@ public class ForwarderChainTest {
|
|||||||
test(cs, mbs);
|
test(cs, mbs);
|
||||||
|
|
||||||
System.out.println("===Remove any leftover forwarders===");
|
System.out.println("===Remove any leftover forwarders===");
|
||||||
while (cs.getSystemMBeanServer() instanceof MBeanServerForwarder) {
|
MBeanServerForwarder systemMBSF = cs.getSystemMBeanServerForwarder();
|
||||||
MBeanServerForwarder mbsf =
|
// Real code would just do systemMBSF.setMBeanServer(mbs).
|
||||||
(MBeanServerForwarder) cs.getSystemMBeanServer();
|
while (true) {
|
||||||
cs.removeMBeanServerForwarder(mbsf);
|
MBeanServer xmbs = systemMBSF.getMBeanServer();
|
||||||
|
if (!(xmbs instanceof MBeanServerForwarder))
|
||||||
|
break;
|
||||||
|
cs.removeMBeanServerForwarder((MBeanServerForwarder) xmbs);
|
||||||
}
|
}
|
||||||
expectChain(cs, "U", mbs);
|
expectChain(cs, "U", mbs);
|
||||||
|
|
||||||
System.out.println("===Ensure forwarders are called===");
|
System.out.println("===Ensure forwarders are called===");
|
||||||
cs.setMBeanServerForwarder(forwarders[0]);
|
cs.setMBeanServerForwarder(forwarders[0]);
|
||||||
cs.setSystemMBeanServerForwarder(forwarders[1]);
|
systemMBSF.setMBeanServer(forwarders[1]);
|
||||||
|
forwarders[1].setMBeanServer(forwarders[0]);
|
||||||
expectChain(cs, "1U0", mbs);
|
expectChain(cs, "1U0", mbs);
|
||||||
cs.start();
|
cs.start();
|
||||||
if (forwarders[0].defaultDomainCount != 0 ||
|
if (forwarders[0].defaultDomainCount != 0 ||
|
||||||
@ -125,8 +129,8 @@ public class ForwarderChainTest {
|
|||||||
private static void test(JMXConnectorServer cs, MBeanServer end) {
|
private static void test(JMXConnectorServer cs, MBeanServer end) {
|
||||||
// A newly-created connector server might have system forwarders,
|
// A newly-created connector server might have system forwarders,
|
||||||
// so get rid of those.
|
// so get rid of those.
|
||||||
while (cs.getSystemMBeanServer() != cs.getMBeanServer())
|
MBeanServerForwarder systemMBSF = cs.getSystemMBeanServerForwarder();
|
||||||
cs.removeMBeanServerForwarder((MBeanServerForwarder) cs.getSystemMBeanServer());
|
systemMBSF.setMBeanServer(cs.getMBeanServer());
|
||||||
|
|
||||||
expectChain(cs, "U", end);
|
expectChain(cs, "U", end);
|
||||||
|
|
||||||
@ -139,7 +143,8 @@ public class ForwarderChainTest {
|
|||||||
expectChain(cs, "U10", end);
|
expectChain(cs, "U10", end);
|
||||||
|
|
||||||
System.out.println("Add a system forwarder");
|
System.out.println("Add a system forwarder");
|
||||||
cs.setSystemMBeanServerForwarder(forwarders[2]);
|
forwarders[2].setMBeanServer(systemMBSF.getMBeanServer());
|
||||||
|
systemMBSF.setMBeanServer(forwarders[2]);
|
||||||
expectChain(cs, "2U10", end);
|
expectChain(cs, "2U10", end);
|
||||||
|
|
||||||
System.out.println("Add another user forwarder");
|
System.out.println("Add another user forwarder");
|
||||||
@ -147,7 +152,8 @@ public class ForwarderChainTest {
|
|||||||
expectChain(cs, "2U310", end);
|
expectChain(cs, "2U310", end);
|
||||||
|
|
||||||
System.out.println("Add another system forwarder");
|
System.out.println("Add another system forwarder");
|
||||||
cs.setSystemMBeanServerForwarder(forwarders[4]);
|
forwarders[4].setMBeanServer(systemMBSF.getMBeanServer());
|
||||||
|
systemMBSF.setMBeanServer(forwarders[4]);
|
||||||
expectChain(cs, "42U310", end);
|
expectChain(cs, "42U310", end);
|
||||||
|
|
||||||
System.out.println("Remove the first user forwarder");
|
System.out.println("Remove the first user forwarder");
|
||||||
@ -215,9 +221,8 @@ public class ForwarderChainTest {
|
|||||||
}
|
}
|
||||||
case 2: { // add it to the system chain
|
case 2: { // add it to the system chain
|
||||||
System.out.println("Add " + c + " to system chain");
|
System.out.println("Add " + c + " to system chain");
|
||||||
if (cs.getSystemMBeanServer() == null)
|
mbsf.setMBeanServer(systemMBSF.getMBeanServer());
|
||||||
mbsf.setMBeanServer(null);
|
systemMBSF.setMBeanServer(mbsf);
|
||||||
cs.setSystemMBeanServerForwarder(mbsf);
|
|
||||||
chain = c + chain;
|
chain = c + chain;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -240,7 +245,7 @@ public class ForwarderChainTest {
|
|||||||
private static void expectChain(
|
private static void expectChain(
|
||||||
JMXConnectorServer cs, String chain, MBeanServer end) {
|
JMXConnectorServer cs, String chain, MBeanServer end) {
|
||||||
System.out.println("...expected chain: " + chain);
|
System.out.println("...expected chain: " + chain);
|
||||||
MBeanServer curr = cs.getSystemMBeanServer();
|
MBeanServer curr = cs.getSystemMBeanServerForwarder().getMBeanServer();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (i < chain.length()) {
|
while (i < chain.length()) {
|
||||||
char c = chain.charAt(i);
|
char c = chain.charAt(i);
|
||||||
|
@ -26,6 +26,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import javax.management.ClientContext;
|
||||||
import javax.management.MBeanServer;
|
import javax.management.MBeanServer;
|
||||||
import javax.management.event.EventClientDelegate;
|
import javax.management.event.EventClientDelegate;
|
||||||
import javax.management.remote.JMXConnectorServer;
|
import javax.management.remote.JMXConnectorServer;
|
||||||
@ -62,13 +63,23 @@ public class StandardForwardersTest {
|
|||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
|
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
|
||||||
|
|
||||||
|
MBeanServerForwarder ctxFwd = ClientContext.newContextForwarder(mbs, null);
|
||||||
|
Forwarder ctx = new Forwarder(
|
||||||
|
JMXConnectorServer.CONTEXT_FORWARDER, true, ctxFwd.getClass());
|
||||||
|
|
||||||
|
MBeanServerForwarder locFwd =
|
||||||
|
ClientContext.newLocalizeMBeanInfoForwarder(mbs);
|
||||||
|
Forwarder loc = new Forwarder(
|
||||||
|
JMXConnectorServer.LOCALIZE_MBEAN_INFO_FORWARDER, false,
|
||||||
|
locFwd.getClass());
|
||||||
|
|
||||||
MBeanServerForwarder ecdFwd =
|
MBeanServerForwarder ecdFwd =
|
||||||
EventClientDelegate.newForwarder();
|
EventClientDelegate.newForwarder(mbs, null);
|
||||||
Forwarder ecd = new Forwarder(
|
Forwarder ecd = new Forwarder(
|
||||||
JMXConnectorServer.EVENT_CLIENT_DELEGATE_FORWARDER, true,
|
JMXConnectorServer.EVENT_CLIENT_DELEGATE_FORWARDER, true,
|
||||||
ecdFwd.getClass());
|
ecdFwd.getClass());
|
||||||
|
|
||||||
Forwarder[] forwarders = {ecd};
|
Forwarder[] forwarders = {ctx, loc, ecd};
|
||||||
|
|
||||||
// Now go through every combination of forwarders. Each forwarder
|
// Now go through every combination of forwarders. Each forwarder
|
||||||
// may be explicitly enabled, explicitly disabled, or left to its
|
// may be explicitly enabled, explicitly disabled, or left to its
|
||||||
@ -154,9 +165,11 @@ public class StandardForwardersTest {
|
|||||||
}
|
}
|
||||||
MBeanServer stop = cs.getMBeanServer();
|
MBeanServer stop = cs.getMBeanServer();
|
||||||
List<Class<?>> foundClasses = new ArrayList<Class<?>>();
|
List<Class<?>> foundClasses = new ArrayList<Class<?>>();
|
||||||
for (MBeanServer mbs = cs.getSystemMBeanServer(); mbs != stop;
|
for (MBeanServer mbs = cs.getSystemMBeanServerForwarder().getMBeanServer();
|
||||||
mbs = ((MBeanServerForwarder) mbs).getMBeanServer())
|
mbs != stop;
|
||||||
|
mbs = ((MBeanServerForwarder) mbs).getMBeanServer()) {
|
||||||
foundClasses.add(mbs.getClass());
|
foundClasses.add(mbs.getClass());
|
||||||
|
}
|
||||||
if (!expectedClasses.equals(foundClasses)) {
|
if (!expectedClasses.equals(foundClasses)) {
|
||||||
fail("Incorrect forwarder chain: expected " + expectedClasses +
|
fail("Incorrect forwarder chain: expected " + expectedClasses +
|
||||||
"; found " + foundClasses);
|
"; found " + foundClasses);
|
||||||
@ -165,9 +178,12 @@ public class StandardForwardersTest {
|
|||||||
|
|
||||||
// env is consistent if either (a) localizer is not enabled or (b)
|
// env is consistent if either (a) localizer is not enabled or (b)
|
||||||
// localizer is enabled and context is enabled.
|
// localizer is enabled and context is enabled.
|
||||||
// Neither of those is present in this codebase so env is always consistent.
|
|
||||||
private static boolean isConsistent(Map<String, String> env) {
|
private static boolean isConsistent(Map<String, String> env) {
|
||||||
return true;
|
String ctxS = env.get(JMXConnectorServer.CONTEXT_FORWARDER);
|
||||||
|
boolean ctx = (ctxS == null) ? true : Boolean.parseBoolean(ctxS);
|
||||||
|
String locS = env.get(JMXConnectorServer.LOCALIZE_MBEAN_INFO_FORWARDER);
|
||||||
|
boolean loc = (locS == null) ? false : Boolean.parseBoolean(locS);
|
||||||
|
return !loc || ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void fail(String why) {
|
private static void fail(String why) {
|
||||||
|
@ -46,6 +46,8 @@ import javax.management.MBeanServer;
|
|||||||
/*
|
/*
|
||||||
* Tests jar services provider are called
|
* Tests jar services provider are called
|
||||||
*/
|
*/
|
||||||
|
import provider.JMXConnectorProviderImpl;
|
||||||
|
import provider.JMXConnectorServerProviderImpl;
|
||||||
public class ProviderTest {
|
public class ProviderTest {
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
System.out.println("Starting ProviderTest");
|
System.out.println("Starting ProviderTest");
|
||||||
@ -56,8 +58,14 @@ public class ProviderTest {
|
|||||||
|
|
||||||
dotest(url, mbs);
|
dotest(url, mbs);
|
||||||
|
|
||||||
if(!provider.JMXConnectorProviderImpl.called() ||
|
boolean clientCalled = provider.JMXConnectorProviderImpl.called();
|
||||||
!provider.JMXConnectorServerProviderImpl.called()) {
|
boolean serverCalled = provider.JMXConnectorServerProviderImpl.called();
|
||||||
|
boolean ok = clientCalled && serverCalled;
|
||||||
|
if (!ok) {
|
||||||
|
if (!clientCalled)
|
||||||
|
System.out.println("Client provider not called");
|
||||||
|
if (!serverCalled)
|
||||||
|
System.out.println("Server provider not called");
|
||||||
System.out.println("Test Failed");
|
System.out.println("Test Failed");
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ public class SimpleStandard
|
|||||||
* @return the current value of the "State" attribute.
|
* @return the current value of the "State" attribute.
|
||||||
*/
|
*/
|
||||||
public String getState() {
|
public String getState() {
|
||||||
checkSubject();
|
checkSubject("getState");
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ public class SimpleStandard
|
|||||||
* @param <VAR>s</VAR> the new value of the "State" attribute.
|
* @param <VAR>s</VAR> the new value of the "State" attribute.
|
||||||
*/
|
*/
|
||||||
public void setState(String s) {
|
public void setState(String s) {
|
||||||
checkSubject();
|
checkSubject("setState");
|
||||||
state = s;
|
state = s;
|
||||||
nbChanges++;
|
nbChanges++;
|
||||||
}
|
}
|
||||||
@ -97,7 +97,7 @@ public class SimpleStandard
|
|||||||
* @return the current value of the "NbChanges" attribute.
|
* @return the current value of the "NbChanges" attribute.
|
||||||
*/
|
*/
|
||||||
public int getNbChanges() {
|
public int getNbChanges() {
|
||||||
checkSubject();
|
checkSubject("getNbChanges");
|
||||||
return nbChanges;
|
return nbChanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +106,7 @@ public class SimpleStandard
|
|||||||
* attributes of the "SimpleStandard" standard MBean.
|
* attributes of the "SimpleStandard" standard MBean.
|
||||||
*/
|
*/
|
||||||
public void reset() {
|
public void reset() {
|
||||||
checkSubject();
|
checkSubject("reset");
|
||||||
AttributeChangeNotification acn =
|
AttributeChangeNotification acn =
|
||||||
new AttributeChangeNotification(this,
|
new AttributeChangeNotification(this,
|
||||||
0,
|
0,
|
||||||
@ -149,18 +149,18 @@ public class SimpleStandard
|
|||||||
* Check that the principal contained in the Subject is of
|
* Check that the principal contained in the Subject is of
|
||||||
* type JMXPrincipal and refers to the principalName identity.
|
* type JMXPrincipal and refers to the principalName identity.
|
||||||
*/
|
*/
|
||||||
private void checkSubject() {
|
private void checkSubject(String op) {
|
||||||
AccessControlContext acc = AccessController.getContext();
|
AccessControlContext acc = AccessController.getContext();
|
||||||
Subject subject = Subject.getSubject(acc);
|
Subject subject = Subject.getSubject(acc);
|
||||||
Set principals = subject.getPrincipals();
|
Set principals = subject.getPrincipals();
|
||||||
Principal principal = (Principal) principals.iterator().next();
|
Principal principal = (Principal) principals.iterator().next();
|
||||||
if (!(principal instanceof JMXPrincipal))
|
if (!(principal instanceof JMXPrincipal))
|
||||||
throw new SecurityException("Authenticated subject contains " +
|
throw new SecurityException(op+": Authenticated subject contains " +
|
||||||
"invalid principal type = " +
|
"invalid principal type = " +
|
||||||
principal.getClass().getName());
|
principal.getClass().getName());
|
||||||
String identity = principal.getName();
|
String identity = principal.getName();
|
||||||
if (!identity.equals(principalName))
|
if (!identity.equals(principalName))
|
||||||
throw new SecurityException("Authenticated subject contains " +
|
throw new SecurityException(op+": Authenticated subject contains " +
|
||||||
"invalid principal name = " + identity);
|
"invalid principal name = " + identity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user