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.
|
||||
* <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.
|
||||
|
@ -41,7 +41,7 @@ public class EventParams {
|
||||
|
||||
@SuppressWarnings("cast") // cast for jdk 1.5
|
||||
public static long getLeaseTimeout() {
|
||||
long timeout = EventClient.DEFAULT_LEASE_TIMEOUT;
|
||||
long timeout = EventClient.DEFAULT_REQUESTED_LEASE_TIME;
|
||||
try {
|
||||
final GetPropertyAction act =
|
||||
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.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@ -143,9 +144,10 @@ public class LeaseManager {
|
||||
private final Runnable callback;
|
||||
private ScheduledFuture<?> scheduled; // If null, the lease has expired.
|
||||
|
||||
private static final ThreadFactory threadFactory =
|
||||
new DaemonThreadFactory("JMX LeaseManager %d");
|
||||
private final ScheduledExecutorService executor
|
||||
= Executors.newScheduledThreadPool(1,
|
||||
new DaemonThreadFactory("JMX LeaseManager %d"));
|
||||
= Executors.newScheduledThreadPool(1, threadFactory);
|
||||
|
||||
private static final ClassLogger logger =
|
||||
new ClassLogger("javax.management.event", "LeaseManager");
|
||||
|
@ -55,9 +55,19 @@ import javax.management.namespace.JMXNamespaces;
|
||||
import javax.management.namespace.MBeanServerSupport;
|
||||
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 {
|
||||
|
||||
private final ObjectName mbeanName;
|
||||
private final boolean visible;
|
||||
private DynamicMBean mbean;
|
||||
|
||||
private MBeanServer mbeanMBS = new MBeanServerSupport() {
|
||||
@ -85,10 +95,20 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
|
||||
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.visible = visible;
|
||||
setSingleMBean(mbean);
|
||||
}
|
||||
|
||||
@ -213,8 +233,10 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
|
||||
|
||||
@Override
|
||||
public String[] getDomains() {
|
||||
TreeSet<String> domainSet =
|
||||
new TreeSet<String>(Arrays.asList(super.getDomains()));
|
||||
String[] domains = super.getDomains();
|
||||
if (!visible)
|
||||
return domains;
|
||||
TreeSet<String> domainSet = new TreeSet<String>(Arrays.asList(domains));
|
||||
domainSet.add(mbeanName.getDomain());
|
||||
return domainSet.toArray(new String[domainSet.size()]);
|
||||
}
|
||||
@ -222,7 +244,7 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
|
||||
@Override
|
||||
public Integer getMBeanCount() {
|
||||
Integer count = super.getMBeanCount();
|
||||
if (!super.isRegistered(mbeanName))
|
||||
if (visible && !super.isRegistered(mbeanName))
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
@ -284,7 +306,7 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
|
||||
*/
|
||||
private boolean applies(ObjectName pattern) {
|
||||
// we know pattern is not null.
|
||||
if (!pattern.apply(mbeanName))
|
||||
if (!visible || !pattern.apply(mbeanName))
|
||||
return false;
|
||||
|
||||
final String dompat = pattern.getDomain();
|
||||
@ -306,10 +328,12 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
|
||||
@Override
|
||||
public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
|
||||
Set<ObjectInstance> names = super.queryMBeans(name, query);
|
||||
if (name == null || applies(name) ) {
|
||||
// Don't assume mbs.queryNames returns a writable set.
|
||||
names = Util.cloneSet(names);
|
||||
names.addAll(mbeanMBS.queryMBeans(name, query));
|
||||
if (visible) {
|
||||
if (name == null || applies(name) ) {
|
||||
// Don't assume mbs.queryNames returns a writable set.
|
||||
names = Util.cloneSet(names);
|
||||
names.addAll(mbeanMBS.queryMBeans(name, query));
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
@ -317,10 +341,12 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
|
||||
@Override
|
||||
public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
|
||||
Set<ObjectName> names = super.queryNames(name, query);
|
||||
if (name == null || applies(name)) {
|
||||
// Don't assume mbs.queryNames returns a writable set.
|
||||
names = Util.cloneSet(names);
|
||||
names.addAll(mbeanMBS.queryNames(name, query));
|
||||
if (visible) {
|
||||
if (name == null || applies(name)) {
|
||||
// Don't assume mbs.queryNames returns a writable set.
|
||||
names = Util.cloneSet(names);
|
||||
names.addAll(mbeanMBS.queryNames(name, query));
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ public final class JmxMBeanServer
|
||||
* {@link javax.management.MBeanServerFactory#newMBeanServer(java.lang.String)}
|
||||
* instead.
|
||||
* <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.
|
||||
* </ul>
|
||||
* @param domain The default domain name used by this MBeanServer.
|
||||
@ -239,7 +239,7 @@ public final class JmxMBeanServer
|
||||
this.mBeanServerDelegateObject = delegate;
|
||||
this.outerShell = outer;
|
||||
|
||||
final Repository repository = new Repository(domain,fairLock);
|
||||
final Repository repository = new Repository(domain);
|
||||
this.mbsInterceptor =
|
||||
new NamespaceDispatchInterceptor(outer, delegate, instantiator,
|
||||
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 boolean identity;
|
||||
|
||||
|
||||
public ObjectNameRouter(String targetDirName) {
|
||||
this(targetDirName,null);
|
||||
}
|
||||
|
||||
/** Creates a new instance of ObjectNameRouter */
|
||||
public ObjectNameRouter(final String remove, final String add) {
|
||||
this.targetPrefix = (remove==null?"":remove);
|
||||
@ -186,6 +181,4 @@ public class ObjectNameRouter {
|
||||
b.append(NAMESPACE_SEPARATOR);
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -31,7 +31,6 @@ import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.namespace.JMXNamespaces;
|
||||
|
||||
|
||||
/**
|
||||
@ -57,22 +56,14 @@ public class RoutingConnectionProxy
|
||||
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
|
||||
*/
|
||||
public RoutingConnectionProxy(MBeanServerConnection source,
|
||||
String sourceDir,
|
||||
String targetDir,
|
||||
boolean forwardsContext) {
|
||||
super(source,sourceDir,targetDir,forwardsContext);
|
||||
boolean probe) {
|
||||
super(source, sourceDir, targetDir, probe);
|
||||
|
||||
if (LOG.isLoggable(Level.FINER))
|
||||
LOG.finer("RoutingConnectionProxy for " + getSourceNamespace() +
|
||||
@ -85,15 +76,13 @@ public class RoutingConnectionProxy
|
||||
final String sourceNs = getSourceNamespace();
|
||||
String wrapped = String.valueOf(source());
|
||||
if ("".equals(targetNs)) {
|
||||
if (forwardsContext)
|
||||
wrapped = "ClientContext.withDynamicContext("+wrapped+")";
|
||||
return "JMXNamespaces.narrowToNamespace("+
|
||||
wrapped+", \""+
|
||||
sourceNs+"\")";
|
||||
}
|
||||
return this.getClass().getSimpleName()+"("+wrapped+", \""+
|
||||
sourceNs+"\", \""+
|
||||
targetNs+"\", "+forwardsContext+")";
|
||||
targetNs+"\")";
|
||||
}
|
||||
|
||||
static final RoutingProxyFactory
|
||||
@ -102,22 +91,16 @@ public class RoutingConnectionProxy
|
||||
<MBeanServerConnection,RoutingConnectionProxy>() {
|
||||
|
||||
public RoutingConnectionProxy newInstance(MBeanServerConnection source,
|
||||
String sourcePath, String targetPath,
|
||||
boolean forwardsContext) {
|
||||
String sourcePath, String targetPath, boolean probe) {
|
||||
return new RoutingConnectionProxy(source,sourcePath,
|
||||
targetPath,forwardsContext);
|
||||
}
|
||||
|
||||
public RoutingConnectionProxy newInstance(
|
||||
MBeanServerConnection source, String sourcePath) {
|
||||
return new RoutingConnectionProxy(source,sourcePath);
|
||||
targetPath, probe);
|
||||
}
|
||||
};
|
||||
|
||||
public static MBeanServerConnection cd(MBeanServerConnection source,
|
||||
String sourcePath) {
|
||||
public static MBeanServerConnection cd(
|
||||
MBeanServerConnection source, String sourcePath, boolean probe) {
|
||||
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.Logger;
|
||||
|
||||
import javax.management.InstanceNotFoundException;
|
||||
import javax.management.MBeanException;
|
||||
import javax.management.MBeanRegistrationException;
|
||||
|
||||
@ -90,17 +91,9 @@ import javax.management.namespace.JMXNamespaces;
|
||||
// targetNs=<encoded-context> // context must be removed from object name
|
||||
// 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,
|
||||
// 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
|
||||
// another RoutingProxy. See RoutingConnectionProxy.cd and
|
||||
// RoutingServerProxy.cd
|
||||
@ -146,25 +139,27 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
|
||||
private final T source;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// The name of the JMXNamespace that handles the source name space
|
||||
private final ObjectName handlerName;
|
||||
private final ObjectNameRouter router;
|
||||
final boolean forwardsContext;
|
||||
private volatile String defaultDomain = null;
|
||||
|
||||
/**
|
||||
* Creates a new instance of RoutingProxy
|
||||
*/
|
||||
protected RoutingProxy(T source,
|
||||
String sourceNs,
|
||||
String targetNs,
|
||||
boolean forwardsContext) {
|
||||
String sourceNs,
|
||||
String targetNs,
|
||||
boolean probe) {
|
||||
if (source == null) throw new IllegalArgumentException("null");
|
||||
this.sourceNs = JMXNamespaces.normalizeNamespaceName(sourceNs);
|
||||
|
||||
@ -177,13 +172,17 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
|
||||
// System.err.println("sourceNs: "+sourceNs);
|
||||
this.handlerName =
|
||||
JMXNamespaces.getNamespaceObjectName(this.sourceNs);
|
||||
try {
|
||||
// System.err.println("handlerName: "+handlerName);
|
||||
if (!source.isRegistered(handlerName))
|
||||
throw new IllegalArgumentException(sourceNs +
|
||||
": no such name space");
|
||||
} catch (IOException x) {
|
||||
throw new IllegalArgumentException("source stale: "+x,x);
|
||||
if (probe) {
|
||||
try {
|
||||
if (!source.isRegistered(handlerName)) {
|
||||
InstanceNotFoundException infe =
|
||||
new InstanceNotFoundException(handlerName);
|
||||
throw new IllegalArgumentException(sourceNs +
|
||||
": no such name space", infe);
|
||||
}
|
||||
} catch (IOException x) {
|
||||
throw new IllegalArgumentException("source stale: "+x,x);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.source = source;
|
||||
@ -191,7 +190,6 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
|
||||
JMXNamespaces.normalizeNamespaceName(targetNs));
|
||||
this.router =
|
||||
new ObjectNameRouter(this.targetNs,this.sourceNs);
|
||||
this.forwardsContext = forwardsContext;
|
||||
|
||||
if (LOG.isLoggable(Level.FINER))
|
||||
LOG.finer("RoutingProxy for " + this.sourceNs + " created");
|
||||
@ -200,14 +198,6 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
|
||||
@Override
|
||||
public T source() { return source; }
|
||||
|
||||
ObjectNameRouter getObjectNameRouter() {
|
||||
// TODO: uncomment this when contexts are added
|
||||
// if (forwardsContext)
|
||||
// return ObjectNameRouter.wrapWithContext(router);
|
||||
// else
|
||||
return router;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectName toSource(ObjectName targetName)
|
||||
throws MalformedObjectNameException {
|
||||
@ -222,8 +212,7 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
|
||||
if (defaultDomain != null)
|
||||
targetName = targetName.withDomain(defaultDomain);
|
||||
}
|
||||
final ObjectNameRouter r = getObjectNameRouter();
|
||||
return r.toSourceContext(targetName,true);
|
||||
return router.toSourceContext(targetName,true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -243,8 +232,7 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
|
||||
public ObjectName toTarget(ObjectName sourceName)
|
||||
throws MalformedObjectNameException {
|
||||
if (sourceName == null) return null;
|
||||
final ObjectNameRouter r = getObjectNameRouter();
|
||||
return r.toTargetContext(sourceName,false);
|
||||
return router.toTargetContext(sourceName,false);
|
||||
}
|
||||
|
||||
private Object getAttributeFromHandler(String attributeName)
|
||||
@ -357,11 +345,8 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
|
||||
// instance.
|
||||
static interface RoutingProxyFactory<T extends MBeanServerConnection,
|
||||
R extends RoutingProxy<T>> {
|
||||
R newInstance(T source,
|
||||
String sourcePath, String targetPath,
|
||||
boolean forwardsContext);
|
||||
R newInstance(T source,
|
||||
String sourcePath);
|
||||
public R newInstance(
|
||||
T source, String sourcePath, String targetPath, boolean probe);
|
||||
}
|
||||
|
||||
// Performs a narrowDownToNamespace operation.
|
||||
@ -377,7 +362,7 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
|
||||
static <T extends MBeanServerConnection, R extends RoutingProxy<T>>
|
||||
R cd(Class<R> routingProxyClass,
|
||||
RoutingProxyFactory<T,R> factory,
|
||||
T source, String sourcePath) {
|
||||
T source, String sourcePath, boolean probe) {
|
||||
if (source == null) throw new IllegalArgumentException("null");
|
||||
if (source.getClass().equals(routingProxyClass)) {
|
||||
// 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 =
|
||||
JMXNamespaces.concat(other.getSourceNamespace(),
|
||||
sourcePath);
|
||||
return factory.newInstance(other.source(),path,"",
|
||||
other.forwardsContext);
|
||||
return factory.newInstance(other.source(), path, "", probe);
|
||||
}
|
||||
// Note: we could do possibly something here - but it would involve
|
||||
// removing part of targetDir, and possibly adding
|
||||
// something to sourcePath.
|
||||
// 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.ReflectionException;
|
||||
import javax.management.loading.ClassLoaderRepository;
|
||||
import javax.management.namespace.JMXNamespaces;
|
||||
|
||||
/**
|
||||
* A RoutingServerProxy is an MBeanServer proxy that proxies a
|
||||
@ -76,19 +75,11 @@ public class RoutingServerProxy
|
||||
extends RoutingProxy<MBeanServer>
|
||||
implements MBeanServer {
|
||||
|
||||
/**
|
||||
* Creates a new instance of RoutingServerProxy
|
||||
*/
|
||||
public RoutingServerProxy(MBeanServer source,
|
||||
String sourceNs) {
|
||||
this(source,sourceNs,"",false);
|
||||
}
|
||||
|
||||
public RoutingServerProxy(MBeanServer source,
|
||||
String sourceNs,
|
||||
String targetNs,
|
||||
boolean forwardsContext) {
|
||||
super(source,sourceNs,targetNs,forwardsContext);
|
||||
boolean probe) {
|
||||
super(source, sourceNs, targetNs, probe);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -571,20 +562,15 @@ public class RoutingServerProxy
|
||||
FACTORY = new RoutingProxyFactory<MBeanServer,RoutingServerProxy>() {
|
||||
|
||||
public RoutingServerProxy newInstance(MBeanServer source,
|
||||
String sourcePath, String targetPath,
|
||||
boolean forwardsContext) {
|
||||
return new RoutingServerProxy(source,sourcePath,
|
||||
targetPath,forwardsContext);
|
||||
}
|
||||
|
||||
public RoutingServerProxy newInstance(
|
||||
MBeanServer source, String sourcePath) {
|
||||
return new RoutingServerProxy(source,sourcePath);
|
||||
String sourcePath, String targetPath, boolean probe) {
|
||||
return new RoutingServerProxy(
|
||||
source, sourcePath, targetPath, probe);
|
||||
}
|
||||
};
|
||||
|
||||
public static MBeanServer cd(MBeanServer source, String sourcePath) {
|
||||
public static MBeanServer cd(
|
||||
MBeanServer source, String sourcePath, boolean probe) {
|
||||
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
|
||||
* for the first time. If null, a default factory will be used
|
||||
* (see {@link #createEventClient}).
|
||||
* @return the
|
||||
* @return the MBeanServerConnection.
|
||||
**/
|
||||
public static MBeanServerConnection getEventConnectionFor(
|
||||
MBeanServerConnection connection,
|
||||
Callable<EventClient> eventClientFactory) {
|
||||
// if c already uses an EventClient no need to create a new one.
|
||||
//
|
||||
if (connection instanceof EventClientFactory
|
||||
&& eventClientFactory != null)
|
||||
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:
|
||||
import java.lang.management.MemoryUsage;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import javax.management.openmbean.CompositeData;
|
||||
import javax.management.openmbean.MXBeanMappingFactory;
|
||||
import javax.management.openmbean.OpenMBeanAttributeInfoSupport;
|
||||
@ -118,19 +118,22 @@ import javax.management.openmbean.OpenType;
|
||||
* deprecation, for example {@code "1.3 Replaced by the Capacity
|
||||
* attribute"}.</td>
|
||||
*
|
||||
* <tr id="descriptionResourceBundleBaseName">
|
||||
* <td>descriptionResource<br>BundleBaseName</td><td>String</td><td>Any</td>
|
||||
* <tr><td id="descriptionResourceBundleBaseName"><i>descriptionResource<br>
|
||||
* BundleBaseName</i></td><td>String</td><td>Any</td>
|
||||
*
|
||||
* <td>The base name for the {@link ResourceBundle} in which the key given in
|
||||
* 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">
|
||||
* <td>descriptionResourceKey</td><td>String</td><td>Any</td>
|
||||
* <tr><td id="descriptionResourceKey"><i>descriptionResourceKey</i></td>
|
||||
* <td>String</td><td>Any</td>
|
||||
*
|
||||
* <td>A resource key for the description of this element. In
|
||||
* 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>
|
||||
* <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>
|
||||
* 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
|
||||
* reasonably be expected to be unchanged. The value can be a {@code Long}
|
||||
* or a decimal string. This provides a hint from a DynamicMBean or any
|
||||
* <td>The time in milli-seconds that the MBeanInfo can reasonably be
|
||||
* expected to be unchanged. The value can be a {@code Long} or a
|
||||
* decimal string. This provides a hint from a DynamicMBean or any
|
||||
* MBean that does not define {@code immutableInfo} as {@code true}
|
||||
* that the MBeanInfo is not likely to change within this period and
|
||||
* 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
|
||||
* {@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>
|
||||
* <td>MBeanAttributeInfo<br>MBeanParameterInfo</td>
|
||||
*
|
||||
|
@ -30,6 +30,7 @@ import com.sun.jmx.mbeanserver.MBeanInjector;
|
||||
import com.sun.jmx.remote.util.ClassLogger;
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@ -37,6 +38,7 @@ import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import javax.management.namespace.JMXNamespaces;
|
||||
import javax.management.openmbean.MXBeanMappingFactory;
|
||||
|
||||
/**
|
||||
@ -60,6 +62,21 @@ public class JMX {
|
||||
*/
|
||||
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
|
||||
* immutableInfo}</a> field.
|
||||
@ -78,6 +95,12 @@ public class JMX {
|
||||
*/
|
||||
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
|
||||
* maxValue}</a> field.
|
||||
@ -120,13 +143,12 @@ public class JMX {
|
||||
* <p>Options to apply to an MBean proxy or to an instance of {@link
|
||||
* StandardMBean}.</p>
|
||||
*
|
||||
* <p>For example, to specify a custom {@link MXBeanMappingFactory}
|
||||
* for a {@code StandardMBean}, you might write this:</p>
|
||||
* <p>For example, to specify the "wrapped object visible" option for a
|
||||
* {@code StandardMBean}, you might write this:</p>
|
||||
*
|
||||
* <pre>
|
||||
* MXBeanMappingFactory factory = new MyMXBeanMappingFactory();
|
||||
* JMX.MBeanOptions opts = new JMX.MBeanOptions();
|
||||
* opts.setMXBeanMappingFactory(factory);
|
||||
* StandardMBean.Options opts = new StandardMBean.Options();
|
||||
* opts.setWrappedObjectVisible(true);
|
||||
* StandardMBean mbean = new StandardMBean(impl, intf, opts);
|
||||
* </pre>
|
||||
*
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
package javax.management;
|
||||
|
||||
import com.sun.jmx.mbeanserver.Util;
|
||||
import java.io.IOException;
|
||||
import java.io.StreamCorruptedException;
|
||||
import java.io.Serializable;
|
||||
@ -37,6 +38,12 @@ import java.util.WeakHashMap;
|
||||
import java.security.AccessController;
|
||||
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;
|
||||
|
||||
/**
|
||||
@ -290,6 +297,7 @@ public class MBeanInfo implements Cloneable, Serializable, DescriptorRead {
|
||||
* <p>Since this class is immutable, the clone method is chiefly of
|
||||
* interest to subclasses.</p>
|
||||
*/
|
||||
@Override
|
||||
public Object clone () {
|
||||
try {
|
||||
return super.clone() ;
|
||||
@ -474,6 +482,7 @@ public class MBeanInfo implements Cloneable, Serializable, DescriptorRead {
|
||||
return (Descriptor) nonNullDescriptor(descriptor).clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return
|
||||
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
|
||||
* to this one according to the rules above.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this)
|
||||
return true;
|
||||
@ -524,6 +534,7 @@ public class MBeanInfo implements Cloneable, Serializable, DescriptorRead {
|
||||
Arrays.equals(p.fastGetNotifications(), fastGetNotifications()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
/* Since computing the hashCode is quite expensive, we cache it.
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <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
|
||||
* de-registration.
|
||||
* unregistration.
|
||||
* <P>
|
||||
* To receive to MBeanServerNotifications, you need to be declared as listener to
|
||||
* the {@link javax.management.MBeanServerDelegate javax.management.MBeanServerDelegate} MBean
|
||||
* that represents the MBeanServer. The ObjectName of the MBeanServerDelegate is:
|
||||
* To receive MBeanServerNotifications, you need to register a listener with
|
||||
* the {@link MBeanServerDelegate MBeanServerDelegate} MBean
|
||||
* that represents the MBeanServer. The ObjectName of the MBeanServerDelegate is
|
||||
* {@link MBeanServerDelegate#DELEGATE_NAME}, which is
|
||||
* <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
|
||||
*/
|
||||
public class MBeanServerNotification extends Notification {
|
||||
|
@ -54,7 +54,7 @@ import com.sun.jmx.mbeanserver.GetPropertyAction;
|
||||
* @since 1.5
|
||||
*/
|
||||
@SuppressWarnings("serial") // serialVersionUID is not constant
|
||||
public class Notification extends EventObject {
|
||||
public class Notification extends EventObject implements Cloneable {
|
||||
|
||||
// Serialization compatibility stuff:
|
||||
// Two serial forms are supported in this class. The selected form depends
|
||||
@ -243,6 +243,26 @@ public class Notification extends EventObject {
|
||||
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.
|
||||
*
|
||||
@ -285,8 +305,10 @@ public class Notification extends EventObject {
|
||||
/**
|
||||
* Get the notification type.
|
||||
*
|
||||
* @return The notification type. It's a string expressed in a dot notation similar
|
||||
* to Java properties. An example of a notification type is network.alarm.router .
|
||||
* @return The notification type. It's a string expressed in a dot notation
|
||||
* 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() {
|
||||
return type ;
|
||||
@ -317,14 +339,25 @@ public class Notification extends EventObject {
|
||||
/**
|
||||
* Get the notification message.
|
||||
*
|
||||
* @return The message string of this notification object. It contains in a string,
|
||||
* which could be the explanation of the notification for displaying to a user
|
||||
* @return The message string of this notification object.
|
||||
*
|
||||
* @see #setMessage
|
||||
*/
|
||||
public String getMessage() {
|
||||
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.
|
||||
*
|
||||
@ -355,6 +388,7 @@ public class Notification extends EventObject {
|
||||
*
|
||||
* @return A String representation of this notification.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
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.ReceiverBuffer;
|
||||
import com.sun.jmx.event.RepeatedSingletonJob;
|
||||
import com.sun.jmx.namespace.JMXNamespaceUtils;
|
||||
import com.sun.jmx.mbeanserver.PerThreadGroupPool;
|
||||
import com.sun.jmx.remote.util.ClassLogger;
|
||||
|
||||
@ -58,7 +57,6 @@ import javax.management.NotificationBroadcasterSupport;
|
||||
import javax.management.NotificationFilter;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.NotificationResult;
|
||||
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";
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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>
|
||||
@ -173,7 +172,7 @@ public class EventClient implements EventConsumer, NotificationManager {
|
||||
*/
|
||||
public EventClient(EventClientDelegateMBean delegate)
|
||||
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.
|
||||
* @param requestedLeaseTime The lease time used to keep this client alive
|
||||
* 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 IOException If an I/O error occurs when communicating with the
|
||||
@ -213,7 +212,7 @@ public class EventClient implements EventConsumer, NotificationManager {
|
||||
}
|
||||
|
||||
if (requestedLeaseTime == 0)
|
||||
requestedLeaseTime = DEFAULT_LEASE_TIMEOUT;
|
||||
requestedLeaseTime = DEFAULT_REQUESTED_LEASE_TIME;
|
||||
else if (requestedLeaseTime < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Negative lease time: " + requestedLeaseTime);
|
||||
@ -269,7 +268,13 @@ public class EventClient implements EventConsumer, NotificationManager {
|
||||
new ScheduledThreadPoolExecutor(20, daemonThreadFactory);
|
||||
executor.setKeepAliveTime(1, TimeUnit.SECONDS);
|
||||
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
|
||||
// in its queue even after they have been cancelled. They
|
||||
// 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
|
||||
// of objects remaining referenced until the renewal time
|
||||
// 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 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 {
|
||||
EventClientDelegateMBean ecd = EventClientDelegate.getProxy(conn);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
leaseRenewerThreadPool = PerThreadGroupPool.make();
|
||||
}
|
||||
|
@ -149,6 +149,7 @@ public class EventClientDelegate implements EventClientDelegateMBean {
|
||||
// of a setMBeanServer on some other forwarder later in the chain.
|
||||
|
||||
private static class Forwarder extends SingleMBeanForwarder {
|
||||
private MBeanServer loopMBS;
|
||||
|
||||
private static class UnsupportedInvocationHandler
|
||||
implements InvocationHandler {
|
||||
@ -173,7 +174,11 @@ public class EventClientDelegate implements EventClientDelegateMBean {
|
||||
private volatile boolean madeECD;
|
||||
|
||||
Forwarder() {
|
||||
super(OBJECT_NAME, makeUnsupportedECD());
|
||||
super(OBJECT_NAME, makeUnsupportedECD(), true);
|
||||
}
|
||||
|
||||
synchronized void setLoopMBS(MBeanServer loopMBS) {
|
||||
this.loopMBS = loopMBS;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -186,7 +191,7 @@ public class EventClientDelegate implements EventClientDelegateMBean {
|
||||
AccessController.doPrivileged(
|
||||
new PrivilegedAction<EventClientDelegate>() {
|
||||
public EventClientDelegate run() {
|
||||
return getEventClientDelegate(Forwarder.this);
|
||||
return getEventClientDelegate(loopMBS);
|
||||
}
|
||||
});
|
||||
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
|
||||
* 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
|
||||
* of an {@code EventClientDelegateMBean}.
|
||||
*
|
||||
* @see javax.management.remote.JMXConnectorServer#installStandardForwarders
|
||||
*/
|
||||
public static MBeanServerForwarder newForwarder() {
|
||||
return new Forwarder();
|
||||
public static MBeanServerForwarder newForwarder(
|
||||
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 class ClientInfo {
|
||||
String clientId;
|
||||
EventBuffer buffer;
|
||||
NotificationListener clientListener;
|
||||
Map<Integer, AddedListener> listenerInfoMap =
|
||||
final String clientId;
|
||||
final NotificationListener clientListener;
|
||||
final Map<Integer, AddedListener> listenerInfoMap =
|
||||
new HashMap<Integer, AddedListener>();
|
||||
|
||||
ClientInfo(String clientId, EventForwarder forwarder) {
|
||||
@ -703,7 +742,8 @@ public class EventClientDelegate implements EventClientDelegateMBean {
|
||||
clientInfo = clientInfoMap.get(clientId);
|
||||
|
||||
if (clientInfo == null) {
|
||||
throw new EventClientNotFoundException("The client is not found.");
|
||||
throw new EventClientNotFoundException(
|
||||
"Client not found (id " + clientId + ")");
|
||||
}
|
||||
|
||||
return clientInfo;
|
||||
|
@ -51,7 +51,8 @@ import javax.management.remote.NotificationResult;
|
||||
* 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
|
||||
* 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
|
||||
* used locally with a forwarder as described above. Since
|
||||
@ -61,9 +62,7 @@ import javax.management.remote.NotificationResult;
|
||||
*
|
||||
* <pre>
|
||||
* MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); // or whatever
|
||||
* MBeanServerForwarder mbsf = EventClientDelegate.newForwarder();
|
||||
* mbsf.setMBeanServer(mbs);
|
||||
* mbs = mbsf;
|
||||
* mbs = EventClientDelegate.newForwarder(mbs, null);
|
||||
* // now use mbs just as you did before, but it will have an EventClientDelegate
|
||||
* </pre>
|
||||
*
|
||||
|
@ -27,7 +27,6 @@ package javax.management.event;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Executors; // for javadoc
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
|
||||
/**
|
||||
* This interface is used to specify a way to receive
|
||||
|
@ -83,8 +83,8 @@
|
||||
* javax.management.event.EventClientDelegateMBean EventClientDelegateMBean}
|
||||
* must be registered in the MBean Server, or the connector server must
|
||||
* be configured to simulate the existence of this MBean, for example
|
||||
* using {@link javax.management.event.EventClientDelegate#newForwarder()
|
||||
* EventClientDelegate.newForwarder()}. The standard RMI connector is so
|
||||
* using {@link javax.management.event.EventClientDelegate#newForwarder
|
||||
* EventClientDelegate.newForwarder}. The standard RMI connector is so
|
||||
* configured by default. The {@code EventClientDelegateMBean} documentation
|
||||
* has further details.</p>
|
||||
*
|
||||
|
@ -26,21 +26,19 @@
|
||||
package javax.management.namespace;
|
||||
|
||||
import com.sun.jmx.defaults.JmxProperties;
|
||||
import com.sun.jmx.namespace.JMXNamespaceUtils;
|
||||
import com.sun.jmx.namespace.ObjectNameRouter;
|
||||
import com.sun.jmx.namespace.serial.RewritingProcessor;
|
||||
import com.sun.jmx.namespace.RoutingConnectionProxy;
|
||||
import com.sun.jmx.namespace.RoutingServerProxy;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.management.InstanceNotFoundException;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.MalformedObjectNameException;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.remote.JMXConnector;
|
||||
|
||||
/**
|
||||
* Static constants and utility methods to help work with
|
||||
@ -68,23 +66,6 @@ public class JMXNamespaces {
|
||||
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
|
||||
* {@linkplain javax.management.namespace sub name space}
|
||||
@ -96,15 +77,18 @@ public class JMXNamespaces {
|
||||
* name space} in which to narrow.
|
||||
* @return A new {@code MBeanServerConnection} proxy that shows the content
|
||||
* of that name space.
|
||||
* @throws IllegalArgumentException if the name space does not exist, or
|
||||
* if a proxy for that name space cannot be created.
|
||||
* @throws IllegalArgumentException if either argument is null,
|
||||
* 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(
|
||||
MBeanServerConnection parent,
|
||||
String namespace) {
|
||||
if (LOG.isLoggable(Level.FINER))
|
||||
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.
|
||||
* @throws IllegalArgumentException if either argument is null,
|
||||
* 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,
|
||||
String namespace) {
|
||||
if (LOG.isLoggable(Level.FINER))
|
||||
LOG.finer("Making NamespaceServerProxy for: " +namespace);
|
||||
return RoutingServerProxy.cd(parent,namespace);
|
||||
LOG.finer("Making MBeanServer for: " +namespace);
|
||||
return RoutingServerProxy.cd(parent, namespace, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -266,7 +252,7 @@ public class JMXNamespaces {
|
||||
ObjectNameRouter.normalizeNamespacePath(namespace,false,
|
||||
true,false);
|
||||
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
|
||||
// namespace instead of the whole ObjectName seems preferable.
|
||||
return ObjectName.getInstance(sourcePath+
|
||||
|
@ -27,10 +27,10 @@ package javax.management.namespace;
|
||||
|
||||
import com.sun.jmx.defaults.JmxProperties;
|
||||
import com.sun.jmx.mbeanserver.Util;
|
||||
import com.sun.jmx.namespace.JMXNamespaceUtils;
|
||||
import com.sun.jmx.remote.util.EnvHelp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
@ -39,6 +39,7 @@ import java.util.logging.Logger;
|
||||
|
||||
import javax.management.AttributeChangeNotification;
|
||||
|
||||
import javax.management.ClientContext;
|
||||
import javax.management.InstanceNotFoundException;
|
||||
import javax.management.ListenerNotFoundException;
|
||||
import javax.management.MBeanNotificationInfo;
|
||||
@ -220,17 +221,26 @@ public class JMXRemoteNamespace
|
||||
initParentOnce(this);
|
||||
|
||||
// URL must not be null.
|
||||
this.jmxURL = JMXNamespaceUtils.checkNonNull(sourceURL,"url");
|
||||
if (sourceURL == null)
|
||||
throw new IllegalArgumentException("Null URL");
|
||||
this.jmxURL = sourceURL;
|
||||
this.broadcaster =
|
||||
new NotificationBroadcasterSupport(connectNotification);
|
||||
|
||||
// handles options
|
||||
this.optionsMap = JMXNamespaceUtils.unmodifiableMap(optionsMap);
|
||||
this.optionsMap = unmodifiableMap(optionsMap);
|
||||
|
||||
// handles (dis)connection events
|
||||
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
|
||||
* 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 {
|
||||
final JMXConnector c = newJMXConnector(jmxURL, env);
|
||||
final JMXConnector c = newJMXConnector(url, env);
|
||||
c.connect(env);
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new JMXConnector with the specified {@code url} and
|
||||
* {@code env} options map.
|
||||
* <p>
|
||||
* This method first calls {@link JMXConnectorFactory#newJMXConnector
|
||||
* JMXConnectorFactory.newJMXConnector(jmxURL, env)} to obtain a new
|
||||
* 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>
|
||||
* <p>Creates a new JMXConnector with the specified {@code url} and
|
||||
* {@code env} options map. The default implementation of this method
|
||||
* returns {@link JMXConnectorFactory#newJMXConnector
|
||||
* JMXConnectorFactory.newJMXConnector(jmxURL, env)}. Subclasses can
|
||||
* override this method to customize behavior.</p>
|
||||
*
|
||||
* @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
|
||||
* JMXConnectorFactory#newJMXConnector creating} the
|
||||
* {@link JMXConnector} that can connect to the remote source
|
||||
* MBean Server.
|
||||
* @return An unconnected JMXConnector to use to connect to the remote
|
||||
* server
|
||||
* @throws java.io.IOException if the connector could not be created.
|
||||
* @return A JMXConnector to use to connect to the remote server
|
||||
* @throws IOException if the connector could not be created.
|
||||
* @see JMXConnectorFactory#newJMXConnector(javax.management.remote.JMXServiceURL, java.util.Map)
|
||||
* @see #JMXRemoteNamespace
|
||||
*/
|
||||
protected JMXConnector newJMXConnector(JMXServiceURL url,
|
||||
Map<String,?> optionsMap) throws IOException {
|
||||
final JMXConnector c =
|
||||
JMXConnectorFactory.newJMXConnector(jmxURL, optionsMap);
|
||||
// TODO: uncomment this when contexts are added
|
||||
// return ClientContext.withDynamicContext(c);
|
||||
return c;
|
||||
return JMXConnectorFactory.newJMXConnector(jmxURL, optionsMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* <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 {
|
||||
LOG.fine("connecting...");
|
||||
final Map<String,Object> env =
|
||||
@ -590,7 +665,7 @@ public class JMXRemoteNamespace
|
||||
try {
|
||||
// XXX: We should probably document this...
|
||||
// This allows to specify a loader name - which will be
|
||||
// retrieved from the paret MBeanServer.
|
||||
// retrieved from the parent MBeanServer.
|
||||
defaultClassLoader =
|
||||
EnvHelp.resolveServerClassLoader(env,getMBeanServer());
|
||||
} catch (InstanceNotFoundException x) {
|
||||
@ -604,7 +679,7 @@ public class JMXRemoteNamespace
|
||||
final JMXConnector aconn = connect(url,env);
|
||||
final MBeanServerConnection msc;
|
||||
try {
|
||||
msc = aconn.getMBeanServerConnection();
|
||||
msc = getMBeanServerConnection(aconn);
|
||||
aconn.addConnectionNotificationListener(listener,null,aconn);
|
||||
} catch (IOException io) {
|
||||
close(aconn);
|
||||
|
@ -322,10 +322,12 @@ public class JMXConnectorFactory {
|
||||
JMXConnectorProvider.class;
|
||||
final String protocol = serviceURL.getProtocol();
|
||||
final String providerClassName = "ClientProvider";
|
||||
final JMXServiceURL providerURL = serviceURL;
|
||||
|
||||
JMXConnectorProvider provider =
|
||||
getProvider(serviceURL, envcopy, providerClassName,
|
||||
targetInterface, loader);
|
||||
JMXConnectorProvider provider = getProvider(providerURL, envcopy,
|
||||
providerClassName,
|
||||
targetInterface,
|
||||
loader);
|
||||
|
||||
IOException exception = null;
|
||||
if (provider == null) {
|
||||
@ -336,7 +338,7 @@ public class JMXConnectorFactory {
|
||||
if (loader != null) {
|
||||
try {
|
||||
JMXConnector connection =
|
||||
getConnectorAsService(loader, serviceURL, envcopy);
|
||||
getConnectorAsService(loader, providerURL, envcopy);
|
||||
if (connection != null)
|
||||
return connection;
|
||||
} catch (JMXProviderException e) {
|
||||
@ -345,8 +347,7 @@ public class JMXConnectorFactory {
|
||||
exception = e;
|
||||
}
|
||||
}
|
||||
provider =
|
||||
getProvider(protocol, PROTOCOL_PROVIDER_DEFAULT_PACKAGE,
|
||||
provider = getProvider(protocol, PROTOCOL_PROVIDER_DEFAULT_PACKAGE,
|
||||
JMXConnectorFactory.class.getClassLoader(),
|
||||
providerClassName, targetInterface);
|
||||
}
|
||||
@ -448,9 +449,10 @@ public class JMXConnectorFactory {
|
||||
getProviderIterator(JMXConnectorProvider.class, loader);
|
||||
JMXConnector connection;
|
||||
IOException exception = null;
|
||||
while(providers.hasNext()) {
|
||||
while (providers.hasNext()) {
|
||||
JMXConnectorProvider provider = providers.next();
|
||||
try {
|
||||
connection = providers.next().newJMXConnector(url, map);
|
||||
connection = provider.newJMXConnector(url, map);
|
||||
return connection;
|
||||
} catch (JMXProviderException e) {
|
||||
throw e;
|
||||
@ -553,4 +555,5 @@ public class JMXConnectorFactory {
|
||||
private static String protocol2package(String protocol) {
|
||||
return protocol.replace('+', '.').replace('-', '_');
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
import javax.management.ClientContext;
|
||||
import javax.management.MBeanInfo; // for javadoc
|
||||
import javax.management.MBeanNotificationInfo;
|
||||
import javax.management.MBeanRegistration;
|
||||
@ -101,6 +102,56 @@ public abstract class JMXConnectorServer
|
||||
public static final String DELEGATE_TO_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
|
||||
* 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.
|
||||
*
|
||||
* @see #setMBeanServerForwarder
|
||||
* @see #getSystemMBeanServer
|
||||
* @see #getSystemMBeanServerForwarder
|
||||
*/
|
||||
public synchronized MBeanServer getMBeanServer() {
|
||||
return userMBeanServer;
|
||||
@ -176,30 +227,36 @@ public abstract class JMXConnectorServer
|
||||
* this method, the first occurrence in the chain of an object that is
|
||||
* {@linkplain Object#equals equal} to {@code mbsf} will have been
|
||||
* removed.</p>
|
||||
*
|
||||
* @param mbsf the forwarder to remove
|
||||
*
|
||||
* @throws NoSuchElementException if there is no occurrence of {@code mbsf}
|
||||
* 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) {
|
||||
if (mbsf == null)
|
||||
throw new IllegalArgumentException("Invalid null argument: mbsf");
|
||||
if (systemMBeanServerForwarder.equals(mbsf))
|
||||
throw new IllegalArgumentException("Cannot remove system forwarder");
|
||||
|
||||
MBeanServerForwarder prev = null;
|
||||
MBeanServer curr = systemMBeanServer;
|
||||
while (curr instanceof MBeanServerForwarder && !mbsf.equals(curr)) {
|
||||
prev = (MBeanServerForwarder) curr;
|
||||
MBeanServerForwarder prev = systemMBeanServerForwarder;
|
||||
MBeanServer curr;
|
||||
while (true) {
|
||||
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))
|
||||
throw new NoSuchElementException("MBeanServerForwarder not in chain");
|
||||
MBeanServerForwarder deleted = (MBeanServerForwarder) curr;
|
||||
MBeanServer next = deleted.getMBeanServer();
|
||||
if (prev != null)
|
||||
prev.setMBeanServer(next);
|
||||
if (systemMBeanServer == deleted)
|
||||
systemMBeanServer = next;
|
||||
if (userMBeanServer == deleted)
|
||||
MBeanServer next = mbsf.getMBeanServer();
|
||||
prev.setMBeanServer(next);
|
||||
if (userMBeanServer == mbsf)
|
||||
userMBeanServer = next;
|
||||
}
|
||||
|
||||
@ -209,66 +266,63 @@ public abstract class JMXConnectorServer
|
||||
* the systemMBeanServer and userMBeanServer field declarations.
|
||||
*/
|
||||
private void insertUserMBeanServer(MBeanServer mbs) {
|
||||
MBeanServerForwarder lastSystemMBSF = null;
|
||||
for (MBeanServer mbsi = systemMBeanServer;
|
||||
mbsi != userMBeanServer;
|
||||
mbsi = lastSystemMBSF.getMBeanServer()) {
|
||||
MBeanServerForwarder lastSystemMBSF = systemMBeanServerForwarder;
|
||||
while (true) {
|
||||
MBeanServer mbsi = lastSystemMBSF.getMBeanServer();
|
||||
if (mbsi == userMBeanServer)
|
||||
break;
|
||||
lastSystemMBSF = (MBeanServerForwarder) mbsi;
|
||||
}
|
||||
userMBeanServer = mbs;
|
||||
if (lastSystemMBSF == null)
|
||||
systemMBeanServer = mbs;
|
||||
else
|
||||
lastSystemMBSF.setMBeanServer(mbs);
|
||||
lastSystemMBSF.setMBeanServer(mbs);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns the first item in the chain of system and then user
|
||||
* forwarders. In the simplest case, a {@code JMXConnectorServer}
|
||||
* is connected directly to an {@code MBeanServer}. But there can
|
||||
* also be a chain of {@link MBeanServerForwarder}s between the two.
|
||||
* This chain consists of two sub-chains: first the <em>system chain</em>
|
||||
* and then the <em>user chain</em>. Incoming requests are given to the
|
||||
* first forwarder in the system chain. Each forwarder can handle
|
||||
* a request itself, or more usually forward it to the next forwarder,
|
||||
* perhaps with some extra behavior such as logging or security
|
||||
* checking before or after the forwarding. The last forwarder in
|
||||
* the system chain is followed by the first forwarder in the user
|
||||
* chain.</p>
|
||||
* forwarders. There is a chain of {@link MBeanServerForwarder}s between
|
||||
* a {@code JMXConnectorServer} and its {@code MBeanServer}. This chain
|
||||
* consists of two sub-chains: first the <em>system chain</em> and then
|
||||
* the <em>user chain</em>. Incoming requests are given to the first
|
||||
* forwarder in the system chain. Each forwarder can handle a request
|
||||
* itself, or more usually forward it to the next forwarder, perhaps with
|
||||
* some extra behavior such as logging or security checking before or after
|
||||
* the forwarding. The last forwarder in the system chain is followed by
|
||||
* the first forwarder in the user chain.</p>
|
||||
*
|
||||
* <p>The <em>system chain</em> is usually
|
||||
* defined by a connector server based on the environment Map;
|
||||
* see {@link 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 {@link #setSystemMBeanServerForwarder} or
|
||||
* {@link #removeMBeanServerForwarder}, but in that case the system
|
||||
* chain is no longer guaranteed to be correct.</p>
|
||||
* <p>The object returned by this method is the first forwarder in the
|
||||
* system chain. For a given {@code JMXConnectorServer}, this method
|
||||
* always returns the same object, which simply forwards every request
|
||||
* to the next object in the chain.</p>
|
||||
*
|
||||
* <p>Not all connector servers support a system chain of forwarders,
|
||||
* although the standard {@linkplain
|
||||
* javax.management.remote.rmi.RMIConnectorServer RMI connector
|
||||
* 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
|
||||
* #setMBeanServerForwarder} to insert forwarders 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>
|
||||
* #setMBeanServerForwarder setMBeanServerForwarder} to insert forwarders
|
||||
* at the head of the user chain.</p>
|
||||
*
|
||||
* <p>This code illustrates how the chains can be traversed:</p>
|
||||
*
|
||||
* <pre>
|
||||
* JMXConnectorServer cs;
|
||||
* System.out.println("system chain:");
|
||||
* MBeanServer mbs = cs.getSystemMBeanServer();
|
||||
* MBeanServer mbs = cs.getSystemMBeanServerForwarder();
|
||||
* while (true) {
|
||||
* if (mbs == cs.getMBeanServer())
|
||||
* System.out.println("user chain:");
|
||||
@ -281,65 +335,40 @@ public abstract class JMXConnectorServer
|
||||
* System.out.println("--MBean Server");
|
||||
* </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.
|
||||
*
|
||||
* @see #setSystemMBeanServerForwarder
|
||||
* @throws UnsupportedOperationException if {@link
|
||||
* #supportsSystemMBeanServerForwarder} returns false.
|
||||
*
|
||||
* @see #supportsSystemMBeanServerForwarder
|
||||
* @see #setMBeanServerForwarder
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
public synchronized MBeanServer getSystemMBeanServer() {
|
||||
return systemMBeanServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Inserts an object that intercepts requests for the MBean server
|
||||
* that arrive through this connector server. This object will be
|
||||
* 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;
|
||||
public MBeanServerForwarder getSystemMBeanServerForwarder() {
|
||||
if (!supportsSystemMBeanServerForwarder()) {
|
||||
throw new UnsupportedOperationException(
|
||||
"System MBeanServerForwarder not supported by this " +
|
||||
"connector server");
|
||||
}
|
||||
return systemMBeanServerForwarder;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -350,19 +379,13 @@ public abstract class JMXConnectorServer
|
||||
*
|
||||
* @return true if this connector server supports the system chain of
|
||||
* forwarders.
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
public boolean supportsSystemMBeanServerForwarder() {
|
||||
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
|
||||
* based on the attributes in the given {@code Map}. A connector
|
||||
@ -374,34 +397,90 @@ public abstract class JMXConnectorServer
|
||||
* <ul>
|
||||
*
|
||||
* <li>If {@link #EVENT_CLIENT_DELEGATE_FORWARDER} is absent, or is
|
||||
* present with the value {@code "true"}, then a forwarder with the
|
||||
* functionality of {@link EventClientDelegate#newForwarder} is inserted
|
||||
* at the start of the system chain.</li>
|
||||
* present with the value {@code "true"}, then a forwarder
|
||||
* equivalent to {@link EventClientDelegate#newForwarder
|
||||
* 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>
|
||||
*
|
||||
* <p>For {@code EVENT_CLIENT_DELEGATE_FORWARDER}, if the
|
||||
* attribute is absent from the {@code Map} and a system property
|
||||
* of the same name is defined, then the value of the system
|
||||
* property is used as if it were in the {@code Map}.
|
||||
* <p>For {@code EVENT_CLIENT_DELEGATE_FORWARDER} and {@code
|
||||
* CONTEXT_FORWARDER}, if the attribute is absent from the {@code
|
||||
* Map} and a system property of the same name is defined, then
|
||||
* 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
|
||||
* by this method.</p>
|
||||
*
|
||||
* @throws UnsupportedOperationException if {@link
|
||||
* #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) {
|
||||
mustSupportSystemMBSF();
|
||||
MBeanServerForwarder sysMBSF = getSystemMBeanServerForwarder();
|
||||
|
||||
// Remember that forwarders must be added in reverse order!
|
||||
|
||||
boolean ecd = EnvHelp.computeBooleanFromString(
|
||||
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) {
|
||||
MBeanServerForwarder mbsf = EventClientDelegate.newForwarder();
|
||||
setSystemMBeanServerForwarder(mbsf);
|
||||
MBeanServerForwarder mbsf = EventClientDelegate.newForwarder(
|
||||
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.
|
||||
*/
|
||||
@Override
|
||||
public MBeanNotificationInfo[] getNotificationInfo() {
|
||||
final String[] types = {
|
||||
JMXConnectionNotification.OPENED,
|
||||
@ -684,30 +764,29 @@ public abstract class JMXConnectorServer
|
||||
* Fields describing the chains of forwarders (MBeanServerForwarders).
|
||||
* In the general case, the forwarders look something like this:
|
||||
*
|
||||
* systemMBeanServer userMBeanServer
|
||||
* | |
|
||||
* v v
|
||||
* mbsf1 -> mbsf2 -> mbsf3 -> mbsf4 -> mbsf5 -> mbs
|
||||
* userMBeanServer
|
||||
* |
|
||||
* v
|
||||
* systemMBeanServerForwarder -> mbsf2 -> mbsf3 -> mbsf4 -> mbsf5 -> mbs
|
||||
*
|
||||
* Here, each mbsfi is an MBeanServerForwarder, and the arrows
|
||||
* illustrate its getMBeanServer() method. The last MBeanServerForwarder
|
||||
* can point to an MBeanServer that is not instanceof MBeanServerForwarder,
|
||||
* here mbs.
|
||||
*
|
||||
* Initially, the chain can be empty if this JMXConnectorServer was
|
||||
* constructed without an MBeanServer. In this case, both systemMBS
|
||||
* and userMBS will be null. If there is initially an MBeanServer,
|
||||
* then both systemMBS and userMBS will point to it.
|
||||
* The system chain is never empty because it always has at least
|
||||
* systemMBeanServerForwarder. Initially, the user chain can be empty if
|
||||
* this JMXConnectorServer was constructed without an MBeanServer. In
|
||||
* 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
|
||||
* are forwarders in the system chain (between systemMBS and userMBS in the
|
||||
* picture above), then the last one must point to the old value of userMBS
|
||||
* (possibly null). It must be updated to point to the new value. If there
|
||||
* are no forwarders in the system chain, then systemMBS must be updated to
|
||||
* the new value of userMBS. The invariant is that starting from systemMBS
|
||||
* and repeatedly calling MBSF.getMBeanServer() you will end up at
|
||||
* userMBS. The implication is that you will not see any MBeanServer
|
||||
* object on the way that is not also an MBeanServerForwarder.
|
||||
* Whenever userMBS is changed, the system chain must be updated. Before
|
||||
* the update, the last forwarder in the system chain points to the old
|
||||
* value of userMBS (possibly null). It must be updated to point to
|
||||
* the new value. The invariant is that starting from systemMBSF and
|
||||
* repeatedly calling MBSF.getMBeanServer() you will end up at 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
|
||||
* and adjust the system chain appropriately.
|
||||
@ -716,7 +795,7 @@ public abstract class JMXConnectorServer
|
||||
* MBeanServer, then userMBS becomes that MBeanServer, and the system
|
||||
* 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
|
||||
* (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
|
||||
@ -726,7 +805,8 @@ public abstract class JMXConnectorServer
|
||||
|
||||
private MBeanServer userMBeanServer;
|
||||
|
||||
private MBeanServer systemMBeanServer;
|
||||
private final MBeanServerForwarder systemMBeanServerForwarder =
|
||||
new IdentityMBeanServerForwarder();
|
||||
|
||||
/**
|
||||
* 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,
|
||||
* 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>.
|
||||
*
|
||||
@ -141,7 +141,7 @@ public interface JMXConnectorServerMBean {
|
||||
* with <code>IllegalArgumentException</code>. This includes the
|
||||
* case where <code>mbsf</code> is null.
|
||||
*
|
||||
* @see JMXConnectorServer#setSystemMBeanServerForwarder
|
||||
* @see JMXConnectorServer#getSystemMBeanServerForwarder
|
||||
*/
|
||||
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 {
|
||||
if (tracing) logger.trace("start", "setting default class loader");
|
||||
defaultClassLoader = EnvHelp.resolveServerClassLoader(
|
||||
attributes, getSystemMBeanServer());
|
||||
attributes, getSystemMBeanServerForwarder());
|
||||
} catch (InstanceNotFoundException infc) {
|
||||
IllegalArgumentException x = new
|
||||
IllegalArgumentException("ClassLoader not found: "+infc);
|
||||
@ -398,7 +398,7 @@ public class RMIConnectorServer extends JMXConnectorServer {
|
||||
else
|
||||
rmiServer = newServer();
|
||||
|
||||
rmiServer.setMBeanServer(getSystemMBeanServer());
|
||||
rmiServer.setMBeanServer(getSystemMBeanServerForwarder());
|
||||
rmiServer.setDefaultClassLoader(defaultClassLoader);
|
||||
rmiServer.setRMIConnectorServer(this);
|
||||
rmiServer.export();
|
||||
@ -592,31 +592,6 @@ public class RMIConnectorServer extends JMXConnectorServer {
|
||||
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}
|
||||
* @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
|
||||
even though they have protected access. */
|
||||
|
||||
@Override
|
||||
protected void connectionOpened(String connectionId, String message,
|
||||
Object userData) {
|
||||
super.connectionOpened(connectionId, message, userData);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void connectionClosed(String connectionId, String message,
|
||||
Object userData) {
|
||||
super.connectionClosed(connectionId, message, userData);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void connectionFailed(String connectionId, String message,
|
||||
Object userData) {
|
||||
super.connectionFailed(connectionId, message, userData);
|
||||
|
@ -39,7 +39,8 @@ import javax.management.*;
|
||||
|
||||
/*
|
||||
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
|
||||
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
|
||||
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
|
||||
is placed on a getter. The test checks that the generated
|
||||
MBeanAttributeInfo contains the corresponding Descriptor. The tested
|
||||
@ -78,12 +83,6 @@ import javax.management.*;
|
||||
public class AnnotationTest {
|
||||
private static String failed = null;
|
||||
|
||||
// @Retention(RetentionPolicy.RUNTIME) @Inherited
|
||||
// @Target(ElementType.METHOD)
|
||||
// public static @interface DescriptorKey {
|
||||
// String value();
|
||||
// }
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public static @interface Pair {
|
||||
@DescriptorKey("x")
|
||||
@ -112,11 +111,12 @@ public class AnnotationTest {
|
||||
boolean[] booleanArrayValue();
|
||||
}
|
||||
|
||||
/* We use the annotation @Pair(x = 3, y = "foo") everywhere, and this is
|
||||
the Descriptor that it should produce: */
|
||||
/* We use the annotations @Pair(x = 3, y = "foo")
|
||||
and @DescriptorFields({"foo=bar", "baz="}) everywhere, and this is
|
||||
the Descriptor that they should produce: */
|
||||
private static Descriptor expectedDescriptor =
|
||||
new ImmutableDescriptor(new String[] {"x", "y"},
|
||||
new Object[] {3, "foo"});
|
||||
new ImmutableDescriptor(new String[] {"x", "y", "foo", "baz"},
|
||||
new Object[] {3, "foo", "bar", ""});
|
||||
|
||||
private static Descriptor expectedFullDescriptor =
|
||||
new ImmutableDescriptor(new String[] {
|
||||
@ -136,8 +136,10 @@ public class AnnotationTest {
|
||||
});
|
||||
|
||||
@Pair(x = 3, y = "foo")
|
||||
@DescriptorFields({"foo=bar", "baz="})
|
||||
public static interface ThingMBean {
|
||||
@Pair(x = 3, y = "foo")
|
||||
@DescriptorFields({"foo=bar", "baz="})
|
||||
@Full(classValue=Full.class,
|
||||
enumValue=RetentionPolicy.RUNTIME,
|
||||
booleanValue=false,
|
||||
@ -149,32 +151,47 @@ public class AnnotationTest {
|
||||
int getReadOnly();
|
||||
|
||||
@Pair(x = 3, y = "foo")
|
||||
@DescriptorFields({"foo=bar", "baz="})
|
||||
void setWriteOnly(int x);
|
||||
|
||||
@Pair(x = 3, y = "foo")
|
||||
@DescriptorFields({"foo=bar", "baz="})
|
||||
int getReadWrite1();
|
||||
void setReadWrite1(int x);
|
||||
|
||||
@Pair(x = 3, y = "foo")
|
||||
@DescriptorFields({"foo=bar", "baz="})
|
||||
int getReadWrite2();
|
||||
@Pair(x = 3, y = "foo")
|
||||
@DescriptorFields({"foo=bar", "baz="})
|
||||
void setReadWrite2(int x);
|
||||
|
||||
int getReadWrite3();
|
||||
@Pair(x = 3, y = "foo")
|
||||
@DescriptorFields({"foo=bar", "baz="})
|
||||
void setReadWrite3(int x);
|
||||
|
||||
@Pair(x = 3, y = "foo")
|
||||
int operation(@Pair(x = 3, y = "foo") int p1,
|
||||
@Pair(x = 3, y = "foo") int p2);
|
||||
@DescriptorFields({"foo=bar", "baz="})
|
||||
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 {
|
||||
@Pair(x = 3, y = "foo")
|
||||
@DescriptorFields({"foo=bar", "baz="})
|
||||
public Thing() {}
|
||||
|
||||
@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;}
|
||||
|
||||
@ -193,14 +210,20 @@ public class AnnotationTest {
|
||||
}
|
||||
|
||||
@Pair(x = 3, y = "foo")
|
||||
@DescriptorFields({"foo=bar", "baz="})
|
||||
public static interface ThingMXBean extends ThingMBean {}
|
||||
|
||||
public static class ThingImpl implements ThingMXBean {
|
||||
@Pair(x = 3, y = "foo")
|
||||
@DescriptorFields({"foo=bar", "baz="})
|
||||
public ThingImpl() {}
|
||||
|
||||
@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;}
|
||||
|
||||
@ -218,6 +241,79 @@ public class AnnotationTest {
|
||||
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 {
|
||||
System.out.println("Testing that annotations are correctly " +
|
||||
"reflected in Descriptor entries");
|
||||
@ -225,20 +321,62 @@ public class AnnotationTest {
|
||||
MBeanServer mbs =
|
||||
java.lang.management.ManagementFactory.getPlatformMBeanServer();
|
||||
ObjectName on = new ObjectName("a:b=c");
|
||||
|
||||
Thing thing = new Thing();
|
||||
mbs.registerMBean(thing, on);
|
||||
check(mbs, on);
|
||||
mbs.unregisterMBean(on);
|
||||
|
||||
ThingImpl thingImpl = new ThingImpl();
|
||||
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);
|
||||
|
||||
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)
|
||||
System.out.println("Test passed");
|
||||
else if (true)
|
||||
throw new Exception("TEST FAILED: " + failed);
|
||||
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 {
|
||||
@ -295,151 +433,4 @@ public class AnnotationTest {
|
||||
for (DescriptorRead x : xx)
|
||||
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 {
|
||||
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
|
||||
MBeanServerForwarder mbsf = EventClientDelegate.newForwarder();
|
||||
mbsf.setMBeanServer(mbs);
|
||||
MBeanServerForwarder mbsf = EventClientDelegate.newForwarder(mbs, null);
|
||||
mbs = mbsf;
|
||||
|
||||
// for 1.5
|
||||
|
@ -65,8 +65,7 @@ public class EventClientExecutorTest {
|
||||
new NamedThreadFactory("LEASE"));
|
||||
|
||||
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
|
||||
MBeanServerForwarder mbsf = EventClientDelegate.newForwarder();
|
||||
mbsf.setMBeanServer(mbs);
|
||||
MBeanServerForwarder mbsf = EventClientDelegate.newForwarder(mbs, null);
|
||||
mbs = mbsf;
|
||||
|
||||
EventClientDelegateMBean ecd = EventClientDelegate.getProxy(mbs);
|
||||
|
@ -98,7 +98,7 @@ public class EventManagerTest {
|
||||
succeed &= test(new EventClient(ecd,
|
||||
new RMIPushEventRelay(ecd),
|
||||
null, null,
|
||||
EventClient.DEFAULT_LEASE_TIMEOUT));
|
||||
EventClient.DEFAULT_REQUESTED_LEASE_TIME));
|
||||
|
||||
conn.close();
|
||||
server.stop();
|
||||
|
@ -99,7 +99,7 @@ public class ListenerTest {
|
||||
succeed &= test(new EventClient(ecd,
|
||||
new RMIPushEventRelay(ecd),
|
||||
null, null,
|
||||
EventClient.DEFAULT_LEASE_TIMEOUT));
|
||||
EventClient.DEFAULT_REQUESTED_LEASE_TIME));
|
||||
|
||||
conn.close();
|
||||
server.stop();
|
||||
|
@ -95,7 +95,7 @@ public class NotSerializableNotifTest {
|
||||
FetchingEventRelay.DEFAULT_MAX_NOTIFICATIONS,
|
||||
null);
|
||||
EventClient ec = new EventClient(ecd, eventRelay, null, null,
|
||||
EventClient.DEFAULT_LEASE_TIMEOUT);
|
||||
EventClient.DEFAULT_REQUESTED_LEASE_TIME);
|
||||
|
||||
// add listener from the client side
|
||||
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
|
||||
* @bug 5108776
|
||||
|
@ -85,6 +85,7 @@ public class EventWithNamespaceControlTest extends EventWithNamespaceTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ?> getServerMap() {
|
||||
Map<String, ?> retValue = Collections.emptyMap();
|
||||
return retValue;
|
||||
|
@ -51,6 +51,7 @@ import javax.management.namespace.JMXDomain;
|
||||
import javax.management.namespace.JMXNamespace;
|
||||
import javax.management.namespace.JMXNamespaces;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.ClientContext;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -42,6 +42,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.management.ClientContext;
|
||||
import javax.management.JMException;
|
||||
import javax.management.MBeanRegistration;
|
||||
import javax.management.MBeanServer;
|
||||
@ -62,11 +63,6 @@ import javax.management.remote.JMXServiceURL;
|
||||
*/
|
||||
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
|
||||
*/
|
||||
|
@ -68,13 +68,7 @@ public class JMXRemoteTargetNamespace extends JMXRemoteNamespace {
|
||||
|
||||
public JMXRemoteTargetNamespace(JMXServiceURL sourceURL,
|
||||
Map<String,?> optionsMap, String sourceNamespace) {
|
||||
this(sourceURL,optionsMap,sourceNamespace,false);
|
||||
}
|
||||
|
||||
public JMXRemoteTargetNamespace(JMXServiceURL sourceURL,
|
||||
Map<String,?> optionsMap, String sourceNamespace,
|
||||
boolean createEventClient) {
|
||||
super(sourceURL,optionsMap);
|
||||
super(sourceURL, optionsMap);
|
||||
this.sourceNamespace = sourceNamespace;
|
||||
this.createEventClient = createEventClient(optionsMap);
|
||||
}
|
||||
@ -92,14 +86,14 @@ public class JMXRemoteTargetNamespace extends JMXRemoteNamespace {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JMXConnector newJMXConnector(JMXServiceURL url,
|
||||
Map<String, ?> env) throws IOException {
|
||||
JMXConnector sup = super.newJMXConnector(url, env);
|
||||
if (sourceNamespace == null || "".equals(sourceNamespace))
|
||||
return sup;
|
||||
protected MBeanServerConnection getMBeanServerConnection(JMXConnector jmxc)
|
||||
throws IOException {
|
||||
MBeanServerConnection mbsc = super.getMBeanServerConnection(jmxc);
|
||||
if (sourceNamespace != null && sourceNamespace.length() > 0)
|
||||
mbsc = JMXNamespaces.narrowToNamespace(mbsc, sourceNamespace);
|
||||
if (createEventClient)
|
||||
sup = EventClient.withEventClient(sup);
|
||||
return JMXNamespaces.narrowToNamespace(sup, sourceNamespace);
|
||||
mbsc = EventClient.getEventClientConnection(mbsc);
|
||||
return mbsc;
|
||||
}
|
||||
|
||||
|
||||
|
@ -206,18 +206,10 @@ public class NamespaceNotificationsTest {
|
||||
aconn.addNotificationListener(deep,listener,null,deep);
|
||||
|
||||
|
||||
final JMXServiceURL urlx = new JMXServiceURL(url1.toString());
|
||||
System.out.println("conn: "+urlx);
|
||||
final JMXConnector jc2 = JMXNamespaces.narrowToNamespace(
|
||||
JMXConnectorFactory.connect(urlx),"server1//server1");
|
||||
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();
|
||||
MBeanServerConnection iconn =
|
||||
JMXNamespaces.narrowToNamespace(aconn, "server1//server1");
|
||||
MBeanServerConnection bconn =
|
||||
JMXNamespaces.narrowToNamespace(aconn, "server3");
|
||||
final ObjectName shallow =
|
||||
new ObjectName("bush:"+
|
||||
deep.getKeyPropertyListString());
|
||||
|
@ -155,7 +155,7 @@ public class NullDomainObjectNameTest {
|
||||
// namespace.
|
||||
//
|
||||
RoutingServerProxy proxy =
|
||||
new RoutingServerProxy(sub, "", "faked", false);
|
||||
new RoutingServerProxy(sub, "", "faked", true);
|
||||
|
||||
// These should fail because the ObjectName doesn't start
|
||||
// with "faked//"
|
||||
|
@ -162,7 +162,7 @@ public class NullObjectNameTest {
|
||||
// this case.
|
||||
//
|
||||
RoutingServerProxy proxy =
|
||||
new RoutingServerProxy(sub,"","faked",false);
|
||||
new RoutingServerProxy(sub, "", "faked", true);
|
||||
final ObjectInstance moi3 =
|
||||
proxy.registerMBean(new MyWombat(),null);
|
||||
System.out.println(moi3.getObjectName().toString()+
|
||||
|
@ -21,19 +21,19 @@
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
import javax.management.openmbean.CompositeType;
|
||||
import javax.management.openmbean.OpenType;
|
||||
import javax.management.openmbean.SimpleType;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6610174
|
||||
* @summary Test that CompositeDataSupport.toString() represents arrays correctly
|
||||
* @author Eamonn McManus
|
||||
*/
|
||||
|
||||
import javax.management.openmbean.ArrayType;
|
||||
import javax.management.openmbean.CompositeData;
|
||||
import javax.management.openmbean.CompositeDataSupport;
|
||||
import javax.management.openmbean.CompositeType;
|
||||
import javax.management.openmbean.OpenType;
|
||||
import javax.management.openmbean.SimpleType;
|
||||
|
||||
public class CompositeDataStringTest {
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
@ -86,16 +86,20 @@ public class ForwarderChainTest {
|
||||
test(cs, mbs);
|
||||
|
||||
System.out.println("===Remove any leftover forwarders===");
|
||||
while (cs.getSystemMBeanServer() instanceof MBeanServerForwarder) {
|
||||
MBeanServerForwarder mbsf =
|
||||
(MBeanServerForwarder) cs.getSystemMBeanServer();
|
||||
cs.removeMBeanServerForwarder(mbsf);
|
||||
MBeanServerForwarder systemMBSF = cs.getSystemMBeanServerForwarder();
|
||||
// Real code would just do systemMBSF.setMBeanServer(mbs).
|
||||
while (true) {
|
||||
MBeanServer xmbs = systemMBSF.getMBeanServer();
|
||||
if (!(xmbs instanceof MBeanServerForwarder))
|
||||
break;
|
||||
cs.removeMBeanServerForwarder((MBeanServerForwarder) xmbs);
|
||||
}
|
||||
expectChain(cs, "U", mbs);
|
||||
|
||||
System.out.println("===Ensure forwarders are called===");
|
||||
cs.setMBeanServerForwarder(forwarders[0]);
|
||||
cs.setSystemMBeanServerForwarder(forwarders[1]);
|
||||
systemMBSF.setMBeanServer(forwarders[1]);
|
||||
forwarders[1].setMBeanServer(forwarders[0]);
|
||||
expectChain(cs, "1U0", mbs);
|
||||
cs.start();
|
||||
if (forwarders[0].defaultDomainCount != 0 ||
|
||||
@ -125,8 +129,8 @@ public class ForwarderChainTest {
|
||||
private static void test(JMXConnectorServer cs, MBeanServer end) {
|
||||
// A newly-created connector server might have system forwarders,
|
||||
// so get rid of those.
|
||||
while (cs.getSystemMBeanServer() != cs.getMBeanServer())
|
||||
cs.removeMBeanServerForwarder((MBeanServerForwarder) cs.getSystemMBeanServer());
|
||||
MBeanServerForwarder systemMBSF = cs.getSystemMBeanServerForwarder();
|
||||
systemMBSF.setMBeanServer(cs.getMBeanServer());
|
||||
|
||||
expectChain(cs, "U", end);
|
||||
|
||||
@ -139,7 +143,8 @@ public class ForwarderChainTest {
|
||||
expectChain(cs, "U10", end);
|
||||
|
||||
System.out.println("Add a system forwarder");
|
||||
cs.setSystemMBeanServerForwarder(forwarders[2]);
|
||||
forwarders[2].setMBeanServer(systemMBSF.getMBeanServer());
|
||||
systemMBSF.setMBeanServer(forwarders[2]);
|
||||
expectChain(cs, "2U10", end);
|
||||
|
||||
System.out.println("Add another user forwarder");
|
||||
@ -147,7 +152,8 @@ public class ForwarderChainTest {
|
||||
expectChain(cs, "2U310", end);
|
||||
|
||||
System.out.println("Add another system forwarder");
|
||||
cs.setSystemMBeanServerForwarder(forwarders[4]);
|
||||
forwarders[4].setMBeanServer(systemMBSF.getMBeanServer());
|
||||
systemMBSF.setMBeanServer(forwarders[4]);
|
||||
expectChain(cs, "42U310", end);
|
||||
|
||||
System.out.println("Remove the first user forwarder");
|
||||
@ -215,9 +221,8 @@ public class ForwarderChainTest {
|
||||
}
|
||||
case 2: { // add it to the system chain
|
||||
System.out.println("Add " + c + " to system chain");
|
||||
if (cs.getSystemMBeanServer() == null)
|
||||
mbsf.setMBeanServer(null);
|
||||
cs.setSystemMBeanServerForwarder(mbsf);
|
||||
mbsf.setMBeanServer(systemMBSF.getMBeanServer());
|
||||
systemMBSF.setMBeanServer(mbsf);
|
||||
chain = c + chain;
|
||||
break;
|
||||
}
|
||||
@ -240,7 +245,7 @@ public class ForwarderChainTest {
|
||||
private static void expectChain(
|
||||
JMXConnectorServer cs, String chain, MBeanServer end) {
|
||||
System.out.println("...expected chain: " + chain);
|
||||
MBeanServer curr = cs.getSystemMBeanServer();
|
||||
MBeanServer curr = cs.getSystemMBeanServerForwarder().getMBeanServer();
|
||||
int i = 0;
|
||||
while (i < chain.length()) {
|
||||
char c = chain.charAt(i);
|
||||
|
@ -26,6 +26,7 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.management.ClientContext;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.event.EventClientDelegate;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
@ -62,13 +63,23 @@ public class StandardForwardersTest {
|
||||
public static void main(String[] args) throws Exception {
|
||||
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 =
|
||||
EventClientDelegate.newForwarder();
|
||||
EventClientDelegate.newForwarder(mbs, null);
|
||||
Forwarder ecd = new Forwarder(
|
||||
JMXConnectorServer.EVENT_CLIENT_DELEGATE_FORWARDER, true,
|
||||
ecdFwd.getClass());
|
||||
|
||||
Forwarder[] forwarders = {ecd};
|
||||
Forwarder[] forwarders = {ctx, loc, ecd};
|
||||
|
||||
// Now go through every combination of forwarders. Each forwarder
|
||||
// may be explicitly enabled, explicitly disabled, or left to its
|
||||
@ -154,9 +165,11 @@ public class StandardForwardersTest {
|
||||
}
|
||||
MBeanServer stop = cs.getMBeanServer();
|
||||
List<Class<?>> foundClasses = new ArrayList<Class<?>>();
|
||||
for (MBeanServer mbs = cs.getSystemMBeanServer(); mbs != stop;
|
||||
mbs = ((MBeanServerForwarder) mbs).getMBeanServer())
|
||||
for (MBeanServer mbs = cs.getSystemMBeanServerForwarder().getMBeanServer();
|
||||
mbs != stop;
|
||||
mbs = ((MBeanServerForwarder) mbs).getMBeanServer()) {
|
||||
foundClasses.add(mbs.getClass());
|
||||
}
|
||||
if (!expectedClasses.equals(foundClasses)) {
|
||||
fail("Incorrect forwarder chain: expected " + expectedClasses +
|
||||
"; found " + foundClasses);
|
||||
@ -165,9 +178,12 @@ public class StandardForwardersTest {
|
||||
|
||||
// env is consistent if either (a) localizer is not enabled or (b)
|
||||
// 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) {
|
||||
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) {
|
||||
|
@ -46,6 +46,8 @@ import javax.management.MBeanServer;
|
||||
/*
|
||||
* Tests jar services provider are called
|
||||
*/
|
||||
import provider.JMXConnectorProviderImpl;
|
||||
import provider.JMXConnectorServerProviderImpl;
|
||||
public class ProviderTest {
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.out.println("Starting ProviderTest");
|
||||
@ -56,8 +58,14 @@ public class ProviderTest {
|
||||
|
||||
dotest(url, mbs);
|
||||
|
||||
if(!provider.JMXConnectorProviderImpl.called() ||
|
||||
!provider.JMXConnectorServerProviderImpl.called()) {
|
||||
boolean clientCalled = provider.JMXConnectorProviderImpl.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.exit(1);
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ public class SimpleStandard
|
||||
* @return the current value of the "State" attribute.
|
||||
*/
|
||||
public String getState() {
|
||||
checkSubject();
|
||||
checkSubject("getState");
|
||||
return state;
|
||||
}
|
||||
|
||||
@ -85,7 +85,7 @@ public class SimpleStandard
|
||||
* @param <VAR>s</VAR> the new value of the "State" attribute.
|
||||
*/
|
||||
public void setState(String s) {
|
||||
checkSubject();
|
||||
checkSubject("setState");
|
||||
state = s;
|
||||
nbChanges++;
|
||||
}
|
||||
@ -97,7 +97,7 @@ public class SimpleStandard
|
||||
* @return the current value of the "NbChanges" attribute.
|
||||
*/
|
||||
public int getNbChanges() {
|
||||
checkSubject();
|
||||
checkSubject("getNbChanges");
|
||||
return nbChanges;
|
||||
}
|
||||
|
||||
@ -106,7 +106,7 @@ public class SimpleStandard
|
||||
* attributes of the "SimpleStandard" standard MBean.
|
||||
*/
|
||||
public void reset() {
|
||||
checkSubject();
|
||||
checkSubject("reset");
|
||||
AttributeChangeNotification acn =
|
||||
new AttributeChangeNotification(this,
|
||||
0,
|
||||
@ -149,18 +149,18 @@ public class SimpleStandard
|
||||
* Check that the principal contained in the Subject is of
|
||||
* type JMXPrincipal and refers to the principalName identity.
|
||||
*/
|
||||
private void checkSubject() {
|
||||
private void checkSubject(String op) {
|
||||
AccessControlContext acc = AccessController.getContext();
|
||||
Subject subject = Subject.getSubject(acc);
|
||||
Set principals = subject.getPrincipals();
|
||||
Principal principal = (Principal) principals.iterator().next();
|
||||
if (!(principal instanceof JMXPrincipal))
|
||||
throw new SecurityException("Authenticated subject contains " +
|
||||
throw new SecurityException(op+": Authenticated subject contains " +
|
||||
"invalid principal type = " +
|
||||
principal.getClass().getName());
|
||||
String identity = principal.getName();
|
||||
if (!identity.equals(principalName))
|
||||
throw new SecurityException("Authenticated subject contains " +
|
||||
throw new SecurityException(op+": Authenticated subject contains " +
|
||||
"invalid principal name = " + identity);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user