--- a/jdk/src/share/classes/com/sun/jmx/defaults/ServiceName.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/com/sun/jmx/defaults/ServiceName.java Thu Nov 13 11:15:01 2008 -0800
@@ -69,9 +69,9 @@
/**
* 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.
--- a/jdk/src/share/classes/com/sun/jmx/event/EventParams.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/com/sun/jmx/event/EventParams.java Thu Nov 13 11:15:01 2008 -0800
@@ -41,7 +41,7 @@
@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);
--- a/jdk/src/share/classes/com/sun/jmx/event/LeaseManager.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/com/sun/jmx/event/LeaseManager.java Thu Nov 13 11:15:01 2008 -0800
@@ -29,6 +29,7 @@
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 @@
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");
--- a/jdk/src/share/classes/com/sun/jmx/interceptor/SingleMBeanForwarder.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/com/sun/jmx/interceptor/SingleMBeanForwarder.java Thu Nov 13 11:15:01 2008 -0800
@@ -55,9 +55,19 @@
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 @@
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 @@
@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 @@
@Override
public Integer getMBeanCount() {
Integer count = super.getMBeanCount();
- if (!super.isRegistered(mbeanName))
+ if (visible && !super.isRegistered(mbeanName))
count++;
return count;
}
@@ -284,7 +306,7 @@
*/
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 @@
@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 @@
@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;
}
--- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/JmxMBeanServer.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/JmxMBeanServer.java Thu Nov 13 11:15:01 2008 -0800
@@ -122,7 +122,7 @@
* {@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 @@
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);
--- a/jdk/src/share/classes/com/sun/jmx/namespace/JMXNamespaceUtils.java Wed Jul 05 16:43:43 2017 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -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;
- }
-
-
-}
--- a/jdk/src/share/classes/com/sun/jmx/namespace/ObjectNameRouter.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/com/sun/jmx/namespace/ObjectNameRouter.java Thu Nov 13 11:15:01 2008 -0800
@@ -49,11 +49,6 @@
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 @@
b.append(NAMESPACE_SEPARATOR);
return b.toString();
}
-
-
}
--- a/jdk/src/share/classes/com/sun/jmx/namespace/RoutingConnectionProxy.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/com/sun/jmx/namespace/RoutingConnectionProxy.java Thu Nov 13 11:15:01 2008 -0800
@@ -31,7 +31,6 @@
import java.util.logging.Logger;
import javax.management.MBeanServerConnection;
-import javax.management.namespace.JMXNamespaces;
/**
@@ -61,18 +60,10 @@
* 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 @@
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 @@
<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);
}
}
--- a/jdk/src/share/classes/com/sun/jmx/namespace/RoutingProxy.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/com/sun/jmx/namespace/RoutingProxy.java Thu Nov 13 11:15:01 2008 -0800
@@ -30,6 +30,7 @@
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 @@
// 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 @@
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 @@
// 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 @@
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 @@
@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 @@
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 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 @@
// 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 @@
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 @@
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);
}
}
--- a/jdk/src/share/classes/com/sun/jmx/namespace/RoutingServerProxy.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/com/sun/jmx/namespace/RoutingServerProxy.java Thu Nov 13 11:15:01 2008 -0800
@@ -54,7 +54,6 @@
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 @@
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 @@
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);
}
}
--- a/jdk/src/share/classes/com/sun/jmx/remote/util/EventClientConnection.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/com/sun/jmx/remote/util/EventClientConnection.java Thu Nov 13 11:15:01 2008 -0800
@@ -430,13 +430,11 @@
* 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");
--- a/jdk/src/share/classes/com/sun/net/ssl/internal/www/protocol/https/HttpsURLConnectionOldImpl.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/com/sun/net/ssl/internal/www/protocol/https/HttpsURLConnectionOldImpl.java Thu Nov 13 11:15:01 2008 -0800
@@ -497,6 +497,10 @@
delegate.setFixedLengthStreamingMode(contentLength);
}
+ public void setFixedLengthStreamingMode(long contentLength) {
+ delegate.setFixedLengthStreamingMode(contentLength);
+ }
+
public void setChunkedStreamingMode (int chunklen) {
delegate.setChunkedStreamingMode(chunklen);
}
--- a/jdk/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java Thu Nov 13 11:15:01 2008 -0800
@@ -86,6 +86,8 @@
* the principal name from the configuration is used. In the
* case where the principal property is not set and the principal
* entry also does not exist, the user is prompted for the name.
+ * When this property of entry is set, and <code>useTicketCache</code>
+ * is set to true, only TGT belonging to this principal is used.
*
* <p> The following is a list of configuration options supported
* for <code>Krb5LoginModule</code>:
@@ -101,18 +103,19 @@
* to false if you do not want this module to use the ticket cache.
* (Default is False).
* This module will
- * search for the tickect
+ * search for the ticket
* cache in the following locations:
- * For Windows 2000, it will use Local Security Authority (LSA) API
- * to get the TGT. On Solaris and Linux
+ * On Solaris and Linux
* it will look for the ticket cache in /tmp/krb5cc_<code>uid</code>
* where the uid is numeric user
* identifier. If the ticket cache is
- * not available in either of the above locations, or if we are on a
- * different Windows platform, it will look for the cache as
+ * not available in the above location, or if we are on a
+ * Windows platform, it will look for the cache as
* {user.home}{file.separator}krb5cc_{user.name}.
* You can override the ticket cache location by using
- * <code>ticketCache</code>
+ * <code>ticketCache</code>.
+ * For Windows, if a ticket cannot be retrieved from the file ticket cache,
+ * it will use Local Security Authority (LSA) API to get the TGT.
* <P>
* <dt><b><code>ticketCache</code></b>:</dt>
* <dd>Set this to the name of the ticket
@@ -129,20 +132,20 @@
* <dt><b><code>doNotPrompt</code></b>:</dt>
* <dd>Set this to true if you do not want to be
* prompted for the password
- * if credentials can
- * not be obtained from the cache or keytab.(Default is false)
- * If set to true authentication will fail if credentials can
- * not be obtained from the cache or keytab.</dd>
+ * if credentials can not be obtained from the cache, the keytab,
+ * or through shared state.(Default is false)
+ * If set to true, credential must be obtained through cache, keytab,
+ * or shared state. Otherwise, authentication will fail.</dd>
* <P>
* <dt><b><code>useKeyTab</code></b>:</dt>
* <dd>Set this to true if you
* want the module to get the principal's key from the
* the keytab.(default value is False)
- * If <code>keyatb</code>
+ * If <code>keytab</code>
* is not set then
* the module will locate the keytab from the
- * Kerberos configuration file.</dd>
- * If it is not specifed in the Kerberos configuration file
+ * Kerberos configuration file.
+ * If it is not specified in the Kerberos configuration file
* then it will look for the file
* <code>{user.home}{file.separator}</code>krb5.keytab.</dd>
* <P>
@@ -210,20 +213,34 @@
* exist for the username and password in the shared
* state, or if authentication fails.
*
- * clearPass if, true, this <code>LoginModule</code> clears the
- * username and password stored in the module's shared
- * state after both phases of authentication
- * (login and commit) have completed.
+ * clearPass if, true, this LoginModule clears the
+ * username and password stored in the module's shared
+ * state after both phases of authentication
+ * (login and commit) have completed.
* </pre>
+ * <p>If the principal system property or key is already provided, the value of
+ * "javax.security.auth.login.name" in the shared state is ignored.
+ * <p>When multiple mechanisms to retrieve a ticket or key is provided, the
+ * preference order looks like this:
+ * <ol>
+ * <li>ticket cache
+ * <li>keytab
+ * <li>shared state
+ * <li>user prompt
+ * </ol>
+ * <p>Note that if any step fails, it will fallback to the next step.
+ * There's only one exception, it the shared state step fails and
+ * <code>useFirstPass</code>=true, no user prompt is made.
* <p>Examples of some configuration values for Krb5LoginModule in
* JAAS config file and the results are:
* <ul>
* <p> <code>doNotPrompt</code>=true;
* </ul>
- * <p> This is an illegal combination since <code>useTicketCache</code>
- * is not set and the user can not be prompted for the password.
+ * <p> This is an illegal combination since none of <code>useTicketCache</code>,
+ * <code>useKeyTab</code>, <code>useFirstPass</code> and <code>tryFirstPass</code>
+ * is set and the user can not be prompted for the password.
*<ul>
- * <p> <code>ticketCache</code> = < filename >;
+ * <p> <code>ticketCache</code> = <filename>;
*</ul>
* <p> This is an illegal combination since <code>useTicketCache</code>
* is not set to true and the ticketCache is set. A configuration error
@@ -240,9 +257,9 @@
*</ul>
* <p> This is an illegal combination since <code>storeKey</code> is set to
* true but the key can not be obtained either by prompting the user or from
- * the keytab.A configuration error will occur.
+ * the keytab, or from the shared state. A configuration error will occur.
* <ul>
- * <p> <code>keyTab</code> = < filename > <code>doNotPrompt</code>=true ;
+ * <p> <code>keyTab</code> = <filename> <code>doNotPrompt</code>=true ;
* </ul>
* <p>This is an illegal combination since useKeyTab is not set to true and
* the keyTab is set. A configuration error will occur.
@@ -260,7 +277,7 @@
* with the principal and TGT. If the TGT is not available,
* do not prompt the user, instead fail the authentication.
* <ul>
- * <p><code>principal</code>=< name ><code>useTicketCache</code> = true
+ * <p><code>principal</code>=<name><code>useTicketCache</code> = true
* <code>doNotPrompt</code>=true;
*</ul>
* <p> Get the TGT from the default cache for the principal and populate the
@@ -269,9 +286,9 @@
* authentication will fail.
* <ul>
* <p> <code>useTicketCache</code> = true
- * <code>ticketCache</code>=< file name ><code>useKeyTab</code> = true
- * <code> keyTab</code>=< keytab filename >
- * <code>principal</code> = < principal name >
+ * <code>ticketCache</code>=<file name><code>useKeyTab</code> = true
+ * <code> keyTab</code>=<keytab filename>
+ * <code>principal</code> = <principal name>
* <code>doNotPrompt</code>=true;
*</ul>
* <p> Search the cache for the principal's TGT. If it is not available
@@ -281,7 +298,7 @@
* If the key is not available or valid then authentication will fail.
* <ul>
* <p><code>useTicketCache</code> = true
- * <code>ticketCache</code>=< file name >
+ * <code>ticketCache</code>=<file name>
*</ul>
* <p> The TGT will be obtained from the cache specified.
* The Kerberos principal name used will be the principal name in
@@ -292,8 +309,8 @@
* The Subject will be populated with the TGT.
*<ul>
* <p> <code>useKeyTab</code> = true
- * <code>keyTab</code>=< keytab filename >
- * <code>principal</code>= < principal name >
+ * <code>keyTab</code>=<keytab filename>
+ * <code>principal</code>= <principal name>
* <code>storeKey</code>=true;
*</ul>
* <p> The key for the principal will be retrieved from the keytab.
@@ -303,7 +320,7 @@
* password entered.
* <ul>
* <p> <code>useKeyTab</code> = true
- * <code>keyTab</code>=< keytabname >
+ * <code>keyTab</code>=<keytabname>
* <code>storeKey</code>=true
* <code>doNotPrompt</code>=true;
*</ul>
@@ -316,21 +333,23 @@
* Subject's private credentials set. Otherwise the authentication will
* fail.
*<ul>
- * <p><code>useKeyTab</code> = true
- * <code>keyTab</code>=< file name > <code>storeKey</code>=true
- * <code>principal</code>= < principal name >
+ * <p>
* <code>useTicketCache</code>=true
- * <code>ticketCache</code>=< file name >;
+ * <code>ticketCache</code>=<file name>;
+ * <code>useKeyTab</code> = true
+ * <code>keyTab</code>=<file name> <code>storeKey</code>=true
+ * <code>principal</code>= <principal name>
*</ul>
- * <p>The principal's key will be retrieved from the keytab and added
- * to the <code>Subject</code>'s private credentials. If the key
- * is not available, the
- * user will be prompted for the password; the key derived from the password
- * will be added to the Subject's private credentials set. The
- * client's TGT will be retrieved from the ticket cache and added to the
+ * <p>
+ * The client's TGT will be retrieved from the ticket cache and added to the
* <code>Subject</code>'s private credentials. If the TGT is not available
- * in the ticket cache, it will be obtained using the authentication
+ * in the ticket cache, or the TGT's client name does not match the principal
+ * name, Java will use a secret key to obtain the TGT using the authentication
* exchange and added to the Subject's private credentials.
+ * This secret key will be first retrieved from the keytab. If the key
+ * is not available, the user will be prompted for the password. In either
+ * case, the key derived from the password will be added to the
+ * Subject's private credentials set.
* <ul>
* <p><code>isInitiator</code> = false
*</ul>
@@ -856,11 +875,13 @@
}
private void validateConfiguration() throws LoginException {
- if (doNotPrompt && !useTicketCache && !useKeyTab)
+ if (doNotPrompt && !useTicketCache && !useKeyTab
+ && !tryFirstPass && !useFirstPass)
throw new LoginException
("Configuration Error"
+ " - either doNotPrompt should be "
- + " false or useTicketCache/useKeyTab "
+ + " false or at least one of useTicketCache, "
+ + " useKeyTab, tryFirstPass and useFirstPass"
+ " should be true");
if (ticketCacheName != null && !useTicketCache)
throw new LoginException
@@ -872,11 +893,12 @@
throw new LoginException
("Configuration Error - useKeyTab should be set to true "
+ "to use the keytab" + keyTabName);
- if (storeKey && doNotPrompt && !useKeyTab)
+ if (storeKey && doNotPrompt && !useKeyTab
+ && !tryFirstPass && !useFirstPass)
throw new LoginException
- ("Configuration Error - either doNotPrompt "
- + "should be set to false or "
- + "useKeyTab must be set to true for storeKey option");
+ ("Configuration Error - either doNotPrompt should be set to "
+ + " false or at least one of tryFirstPass, useFirstPass "
+ + "or useKeyTab must be set to true for storeKey option");
if (renewTGT && !useTicketCache)
throw new LoginException
("Configuration Error"
--- a/jdk/src/share/classes/java/net/HttpURLConnection.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/java/net/HttpURLConnection.java Thu Nov 13 11:15:01 2008 -0800
@@ -73,11 +73,24 @@
* The fixed content-length when using fixed-length streaming mode.
* A value of <code>-1</code> means fixed-length streaming mode is disabled
* for output.
+ *
+ * <P> <B>NOTE:</B> {@link #fixedContentLengthLong} is recommended instead
+ * of this field, as it allows larger content lengths to be set.
+ *
* @since 1.5
*/
protected int fixedContentLength = -1;
/**
+ * The fixed content-length when using fixed-length streaming mode.
+ * A value of {@code -1} means fixed-length streaming mode is disabled
+ * for output.
+ *
+ * @since 1.7
+ */
+ protected long fixedContentLengthLong = -1;
+
+ /**
* Returns the key for the <code>n</code><sup>th</sup> header field.
* Some implementations may treat the <code>0</code><sup>th</sup>
* header field as special, i.e. as the status line returned by the HTTP
@@ -109,6 +122,9 @@
* This exception can be queried for the details of the error.
* <p>
* This method must be called before the URLConnection is connected.
+ * <p>
+ * <B>NOTE:</B> {@link #setFixedLengthStreamingMode(long)} is recommended
+ * instead of this method as it allows larger content lengths to be set.
*
* @param contentLength The number of bytes which will be written
* to the OutputStream.
@@ -135,6 +151,52 @@
fixedContentLength = contentLength;
}
+ /**
+ * This method is used to enable streaming of a HTTP request body
+ * without internal buffering, when the content length is known in
+ * advance.
+ *
+ * <P> An exception will be thrown if the application attempts to write
+ * more data than the indicated content-length, or if the application
+ * closes the OutputStream before writing the indicated amount.
+ *
+ * <P> When output streaming is enabled, authentication and redirection
+ * cannot be handled automatically. A {@linkplain HttpRetryException} will
+ * be thrown when reading the response if authentication or redirection
+ * are required. This exception can be queried for the details of the
+ * error.
+ *
+ * <P> This method must be called before the URLConnection is connected.
+ *
+ * <P> The content length set by invoking this method takes precedence
+ * over any value set by {@link #setFixedLengthStreamingMode(int)}.
+ *
+ * @param contentLength
+ * The number of bytes which will be written to the OutputStream.
+ *
+ * @throws IllegalStateException
+ * if URLConnection is already connected or if a different
+ * streaming mode is already enabled.
+ *
+ * @throws IllegalArgumentException
+ * if a content length less than zero is specified.
+ *
+ * @since 1.7
+ */
+ public void setFixedLengthStreamingMode(long contentLength) {
+ if (connected) {
+ throw new IllegalStateException("Already connected");
+ }
+ if (chunkLength != -1) {
+ throw new IllegalStateException(
+ "Chunked encoding streaming mode set");
+ }
+ if (contentLength < 0) {
+ throw new IllegalArgumentException("invalid content length");
+ }
+ fixedContentLengthLong = contentLength;
+ }
+
/* Default chunk size (including chunk header) if not specified;
* we want to keep this in sync with the one defined in
* sun.net.www.http.ChunkedOutputStream
@@ -170,7 +232,7 @@
if (connected) {
throw new IllegalStateException ("Can't set streaming mode: already connected");
}
- if (fixedContentLength != -1) {
+ if (fixedContentLength != -1 || fixedContentLengthLong != -1) {
throw new IllegalStateException ("Fixed length streaming mode set");
}
chunkLength = chunklen <=0? DEFAULT_CHUNK_SIZE : chunklen;
--- a/jdk/src/share/classes/java/security/cert/CertPathValidatorException.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/java/security/cert/CertPathValidatorException.java Thu Nov 13 11:15:01 2008 -0800
@@ -113,7 +113,7 @@
* permitted, and indicates that the cause is nonexistent or unknown.)
*/
public CertPathValidatorException(Throwable cause) {
- this(null, cause);
+ this((cause == null ? null : cause.toString()), cause);
}
/**
--- a/jdk/src/share/classes/javax/management/AttributeList.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/javax/management/AttributeList.java Thu Nov 13 11:15:01 2008 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1999-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
@@ -27,17 +27,23 @@
import java.util.ArrayList;
import java.util.Collection;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
/**
- * Represents a list of values for attributes of an MBean. The methods
- * used for the insertion of {@link javax.management.Attribute
- * Attribute} objects in the <CODE>AttributeList</CODE> overrides the
- * corresponding methods in the superclass
- * <CODE>ArrayList</CODE>. This is needed in order to insure that the
- * objects contained in the <CODE>AttributeList</CODE> are only
- * <CODE>Attribute</CODE> objects. This avoids getting an exception
- * when retrieving elements from the <CODE>AttributeList</CODE>.
+ * <p>Represents a list of values for attributes of an MBean. See the
+ * {@link MBeanServerConnection#getAttributes getAttributes} and
+ * {@link MBeanServerConnection#setAttributes setAttributes} methods of
+ * {@link MBeanServer} and {@link MBeanServerConnection}.</p>
+ *
+ * <p id="type-safe">For compatibility reasons, it is possible, though
+ * highly discouraged, to add objects to an {@code AttributeList} that are
+ * not instances of {@code Attribute}. However, an {@code AttributeList}
+ * can be made <em>type-safe</em>, which means that an attempt to add
+ * an object that is not an {@code Attribute} will produce an {@code
+ * IllegalArgumentException}. An {@code AttributeList} becomes type-safe
+ * when the method {@link #asList()} is called on it.</p>
*
* @since 1.5
*/
@@ -58,8 +64,8 @@
*/
public class AttributeList extends ArrayList<Object> {
- private transient boolean typeSafe;
- private transient boolean tainted;
+ private transient volatile boolean typeSafe;
+ private transient volatile boolean tainted;
/* Serial version */
private static final long serialVersionUID = -4077085769279709076L;
@@ -124,7 +130,7 @@
// Check for non-Attribute objects
//
- checkTypeSafe(list);
+ adding(list);
// Build the List<Attribute>
//
@@ -132,6 +138,56 @@
}
/**
+ * <p>Constructs an {@code AttributeList} containing the elements of
+ * the {@code Map} specified, in the order in which they appear in the
+ * {@code Map}'s {@link Map#entrySet entrySet}. For each <em>{@code
+ * key}</em> and <em>{@code value}</em> in the {@code Map}, the constructed
+ * {@code AttributeList} will contain {@link Attribute#Attribute
+ * Attribute(<em>key</em>, <em>value</em>)}.</p>
+ *
+ * @param map the {@code Map} defining the elements of the new
+ * {@code AttributeList}.
+ */
+ public AttributeList(Map<String, ?> map) {
+ for (Map.Entry<String, ?> entry : map.entrySet())
+ add(new Attribute(entry.getKey(), entry.getValue()));
+ typeSafe = true;
+ }
+
+ /**
+ * <p>Return a {@code Map} that is a snapshot of the values in this
+ * {@code AttributeList}. Each key in the {@code Map} is the {@linkplain
+ * Attribute#getName() name} of an {@code Attribute} in the list, and each
+ * value is the corresponding {@linkplain Attribute#getValue() value} of
+ * that {@code Attribute}. The {@code AttributeList} and the {@code Map}
+ * are unrelated after the call, that is, changes to one do not affect the
+ * other.</p>
+ *
+ * <p>If the {@code AttributeList} contains more than one {@code Attribute}
+ * with the same name, then the {@code Map} will contain an entry
+ * for that name where the value is that of the last of those {@code
+ * Attribute}s.</p>
+ *
+ * @return the new {@code Map}.
+ *
+ * @throws IllegalArgumentException if this {@code AttributeList} contains
+ * an element that is not an {@code Attribute}.
+ */
+ public Map<String, Object> toMap() {
+ Map<String, Object> map = new LinkedHashMap<String, Object>();
+
+ // We can't call adding(this) because we're not necessarily typeSafe
+ if (tainted)
+ throw new IllegalArgumentException("AttributeList contains non-Attribute");
+
+ for (Object x : this) {
+ Attribute a = (Attribute) x;
+ map.put(a.getName(), a.getValue());
+ }
+ return map;
+ }
+
+ /**
* Return a view of this list as a {@code List<Attribute>}.
* Changes to the returned value are reflected by changes
* to the original {@code AttributeList} and vice versa.
@@ -154,11 +210,9 @@
*/
@SuppressWarnings("unchecked")
public List<Attribute> asList() {
- if (!typeSafe) {
- if (tainted)
- checkTypeSafe(this);
- typeSafe = true;
- }
+ typeSafe = true;
+ if (tainted)
+ adding((Collection<?>) this); // will throw IllegalArgumentException
return (List<Attribute>) (List<?>) this;
}
@@ -175,7 +229,7 @@
* Inserts the attribute specified as an element at the position specified.
* Elements with an index greater than or equal to the current position are
* shifted up. If the index is out of range (index < 0 || index >
- * size() a RuntimeOperationsException should be raised, wrapping the
+ * size()) a RuntimeOperationsException should be raised, wrapping the
* java.lang.IndexOutOfBoundsException thrown.
*
* @param object The <CODE>Attribute</CODE> object to be inserted.
@@ -245,8 +299,7 @@
public boolean addAll(int index, AttributeList list) {
try {
return super.addAll(index, list);
- }
- catch (IndexOutOfBoundsException e) {
+ } catch (IndexOutOfBoundsException e) {
throw new RuntimeOperationsException(e,
"The specified index is out of range");
}
@@ -258,96 +311,77 @@
* been called on this instance.
*/
+ /**
+ * {@inheritDoc}
+ * @throws IllegalArgumentException if this {@code AttributeList} is
+ * <a href="#type-safe">type-safe</a> and {@code element} is not an
+ * {@code Attribute}.
+ */
@Override
- public boolean add(Object o) {
- if (!tainted)
- tainted = isTainted(o);
- if (typeSafe)
- checkTypeSafe(o);
- return super.add(o);
+ public boolean add(Object element) {
+ adding(element);
+ return super.add(element);
}
+ /**
+ * {@inheritDoc}
+ * @throws IllegalArgumentException if this {@code AttributeList} is
+ * <a href="#type-safe">type-safe</a> and {@code element} is not an
+ * {@code Attribute}.
+ */
@Override
public void add(int index, Object element) {
- if (!tainted)
- tainted = isTainted(element);
- if (typeSafe)
- checkTypeSafe(element);
+ adding(element);
super.add(index, element);
}
+ /**
+ * {@inheritDoc}
+ * @throws IllegalArgumentException if this {@code AttributeList} is
+ * <a href="#type-safe">type-safe</a> and {@code c} contains an
+ * element that is not an {@code Attribute}.
+ */
@Override
public boolean addAll(Collection<?> c) {
- if (!tainted)
- tainted = isTainted(c);
- if (typeSafe)
- checkTypeSafe(c);
+ adding(c);
return super.addAll(c);
}
- @Override
- public boolean addAll(int index, Collection<?> c) {
- if (!tainted)
- tainted = isTainted(c);
- if (typeSafe)
- checkTypeSafe(c);
- return super.addAll(index, c);
- }
-
- @Override
- public Object set(int index, Object element) {
- if (!tainted)
- tainted = isTainted(element);
- if (typeSafe)
- checkTypeSafe(element);
- return super.set(index, element);
- }
-
/**
- * IllegalArgumentException if o is a non-Attribute object.
+ * {@inheritDoc}
+ * @throws IllegalArgumentException if this {@code AttributeList} is
+ * <a href="#type-safe">type-safe</a> and {@code c} contains an
+ * element that is not an {@code Attribute}.
*/
- private static void checkTypeSafe(Object o) {
- try {
- o = (Attribute) o;
- } catch (ClassCastException e) {
- throw new IllegalArgumentException(e);
- }
- }
-
- /**
- * IllegalArgumentException if c contains any non-Attribute objects.
- */
- private static void checkTypeSafe(Collection<?> c) {
- try {
- Attribute a;
- for (Object o : c)
- a = (Attribute) o;
- } catch (ClassCastException e) {
- throw new IllegalArgumentException(e);
- }
+ @Override
+ public boolean addAll(int index, Collection<?> c) {
+ adding(c);
+ return super.addAll(index, c);
}
/**
- * Returns true if o is a non-Attribute object.
+ * {@inheritDoc}
+ * @throws IllegalArgumentException if this {@code AttributeList} is
+ * <a href="#type-safe">type-safe</a> and {@code element} is not an
+ * {@code Attribute}.
*/
- private static boolean isTainted(Object o) {
- try {
- checkTypeSafe(o);
- } catch (IllegalArgumentException e) {
- return true;
- }
- return false;
+ @Override
+ public Object set(int index, Object element) {
+ adding(element);
+ return super.set(index, element);
}
- /**
- * Returns true if c contains any non-Attribute objects.
- */
- private static boolean isTainted(Collection<?> c) {
- try {
- checkTypeSafe(c);
- } catch (IllegalArgumentException e) {
- return true;
- }
- return false;
+ private void adding(Object x) {
+ if (x == null || x instanceof Attribute)
+ return;
+ if (typeSafe)
+ throw new IllegalArgumentException("Not an Attribute: " + x);
+ else
+ tainted = true;
+ }
+
+ private void adding(Collection<?> c) {
+ for (Object x : c)
+ adding(x);
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/javax/management/ClientContext.java Thu Nov 13 11:15:01 2008 -0800
@@ -0,0 +1,1091 @@
+/*
+ * 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. 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 javax.management;
+
+import com.sun.jmx.interceptor.SingleMBeanForwarder;
+import com.sun.jmx.namespace.RoutingConnectionProxy;
+import com.sun.jmx.namespace.RoutingProxy;
+import com.sun.jmx.namespace.RoutingServerProxy;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+import java.util.concurrent.Callable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR;
+import javax.management.namespace.JMXNamespaces;
+import javax.management.namespace.JMXNamespace;
+import javax.management.namespace.JMXNamespaceMBean;
+import javax.management.namespace.MBeanServerSupport;
+import javax.management.remote.IdentityMBeanServerForwarder;
+import javax.management.remote.MBeanServerForwarder;
+
+/**
+ * <p>Methods to communicate a client context to MBeans. A context is
+ * a {@literal Map<String, String>} that is provided by the client and
+ * that an MBean can consult using the {@link #getContext()} method.
+ * The context is set on a per-thread basis and can be consulted by any
+ * code that the target MBean calls within the thread.</p>
+ *
+ * <p>One common usage of client context is to communicate the client's
+ * {@link Locale} to MBeans. For example, if an MBean has a String attribute
+ * {@code LastProblemDescription}, the value of that attribute could be
+ * a description of the last problem encountered by the MBean, translated
+ * into the client's locale. Different clients accessing this attribute
+ * from different locales would each see the appropriate version for their
+ * locale.</p>
+ *
+ * <p>The locale case is sufficiently important that it has a special
+ * shorthand, the {@link #getLocale()} method. This method calls
+ * <code>{@link #getContext()}.get({@link #LOCALE_KEY})</code> and converts the
+ * resultant String into a Locale object.</p>
+ *
+ * <p>Here is what an MBean with a localized {@code LastProblemDescription}
+ * attribute might look like:</p>
+ *
+ * <pre>
+ * public class LocaleSensitive implements LocaleSensitiveMBean {
+ * ...
+ * public String getLastProblemDescription() {
+ * Locale loc = {@link #getLocale() ClientContext.getLocale()};
+ * ResourceBundle rb = ResourceBundle.getBundle("MyResources", loc);
+ * String resourceKey = getLastProblemResourceKey();
+ * return rb.getString(resourceKey);
+ * }
+ * ...
+ * }
+ * </pre>
+ *
+ * <p>Here is how a client can communicate its locale to the target
+ * MBean:</p>
+ *
+ * <pre>
+ * JMXConnector connector = JMXConnectorFactory.connect(url);
+ * MBeanServerConnection connection = connector.getMBeanServerConnection();
+ * <b>MBeanServerConnection localizedConnection =
+ * {@link #withLocale(MBeanServerConnection, Locale)
+ * ClientContext.withLocale}(connection, Locale.getDefault());</b>
+ * String problem = localizedConnection.getAttribute(
+ * objectName, "LastProblemDescription");
+ * </pre>
+ *
+ * <p>In the more general case where the client wants to communicate context
+ * other than the locale, it can use {@link #withContext(MBeanServerConnection,
+ * String, String) withContext} instead of {@code withLocale}, and the target
+ * MBean can retrieve the context using {@link #getContext()}.</p>
+ *
+ *
+ * <h3 id="remote-use">Remote use of contexts</h3>
+ *
+ * <p>The various {@code with*} methods, for example {@link
+ * #withLocale(javax.management.MBeanServer, java.util.Locale) withLocale},
+ * transmit the context of each request by encoding it in the ObjectName of
+ * the request. For example, if a client creates a connection in the
+ * French locale like this...</p>
+ *
+ * <pre>
+ * MBeanServerConnection mbsc = ...;
+ * Locale french = new Locale("fr");
+ * MBeanServerConnection localizedConnection = ClientContext.withLocale(mbsc, french);
+ * </pre>
+ *
+ * <p>...or, equivalently, like this...</p>
+ *
+ * <pre>
+ * MBeanServerConnection localizedConnection =
+ * ClientContext.withContext(mbsc, {@link #LOCALE_KEY "jmx.locale"}, "fr");
+ * </pre>
+ *
+ * <p>...then the context associates {@code "jmx.locale"} with {@code "fr"}
+ * and a request such as<br>
+ * {@code localizedConnection.getAttribute("java.lang:type=Runtime", "Name")}<br>
+ * is translated into<br>
+ * {@code mbsc.getAttribute("jmx.context//jmx.locale=fr//java.lang:Runtime", "Name")}.<br>
+ * A special {@linkplain javax.management.namespace namespace} {@code jmx.context//}
+ * extracts the context from the string {@code jmx.locale=fr} and establishes
+ * it in the thread that will do<br>
+ * {@code getAttribute("java.lang:Runtime", "Name")}.</p>
+ *
+ * <p>The details of how contexts are encoded into ObjectNames are explained
+ * in the {@link #encode encode} method.</p>
+ *
+ * <p>The namespace {@code jmx.context//} just mentioned is only needed by
+ * remote clients, since local clients can set the context directly using
+ * {@link #doWithContext doWithContext}. Accordingly, this namespace is not
+ * present by default in the {@code MBeanServer}. Instead, it is
+ * <em>simulated</em> by the standard RMI connector using a special
+ * {@link MBeanServerForwarder}. If you are using this connector, you do not
+ * need to do anything special. Other connectors may or may not simulate this
+ * namespace in the same way. If the connector server returns true from the
+ * method {@link
+ * javax.management.remote.JMXConnectorServer#supportsSystemMBeanServerForwarder()
+ * supportsSystemMBeanServerForwarder} then it does simulate the namespace.
+ * If you are using another connector, or if you want to be able to use the
+ * {@code with*} methods locally, then you can install the {@code
+ * MBeanServerForwarder} yourself as described in the method {@link
+ * #newContextForwarder newContextForwarder}.</p>
+ */
+public class ClientContext {
+ /**
+ * <p>The context key for the client locale. The string associated with
+ * this key is an encoded locale such as {@code en_US} which could be
+ * returned by {@link Locale#toString()}.</p>
+ */
+ public static final String LOCALE_KEY = "jmx.locale";
+
+ private static final Logger LOG =
+ Logger.getLogger("javax.management.context");
+
+ /**
+ * <p>The namespace that implements contexts, {@value}.</p>
+ */
+ public static final String
+ NAMESPACE = "jmx.context";
+ private static final String NAMESPACE_PLUS_SEP =
+ NAMESPACE + NAMESPACE_SEPARATOR;
+ static final ObjectName CLIENT_CONTEXT_NAMESPACE_HANDLER =
+ ObjectName.valueOf(NAMESPACE_PLUS_SEP + ":" +
+ JMXNamespace.TYPE_ASSIGNMENT);
+ private static final ObjectName NAMESPACE_HANDLER_WITHOUT_NAMESPACE =
+ ObjectName.valueOf(":" + JMXNamespace.TYPE_ASSIGNMENT);
+
+ private static final ThreadLocal<Map<String, String>> contextThreadLocal =
+ new InheritableThreadLocal<Map<String, String>>() {
+ @Override
+ protected Map<String, String> initialValue() {
+ return Collections.emptyMap();
+ }
+ };
+
+ /** There are no instances of this class. */
+ private ClientContext() {
+ }
+
+ /**
+ * <p>Get the client context associated with the current thread.
+ *
+ * @return the client context associated with the current thread.
+ * This may be an empty Map, but it cannot be null. The returned
+ * Map cannot be modified.
+ */
+ public static Map<String, String> getContext() {
+ return Collections.unmodifiableMap(contextThreadLocal.get());
+ }
+
+ /**
+ * <p>Get the client locale associated with the current thread.
+ * If the client context includes the {@value #LOCALE_KEY} key
+ * then the returned value is the Locale encoded in that key.
+ * Otherwise the returned value is the {@linkplain Locale#getDefault()
+ * default locale}.
+ *
+ * @return the client locale.
+ */
+ public static Locale getLocale() {
+ String localeS = getContext().get(LOCALE_KEY);
+ if (localeS == null)
+ return Locale.getDefault();
+ // Parse the locale string. Why isn't there a method in Locale for this?
+ String language, country, variant;
+ int ui = localeS.indexOf('_');
+ if (ui < 0) {
+ language = localeS;
+ country = variant = "";
+ } else {
+ language = localeS.substring(0, ui);
+ localeS = localeS.substring(ui + 1);
+ ui = localeS.indexOf('_');
+ if (ui < 0) {
+ country = localeS;
+ variant = "";
+ } else {
+ country = localeS.substring(0, ui);
+ variant = localeS.substring(ui + 1);
+ }
+ }
+ return new Locale(language, country, variant);
+ }
+
+ /**
+ * <p>Execute the given {@code task} with the client context set to
+ * the given Map. This Map will be the result of {@link #getContext()}
+ * within the {@code task}.</p>
+ *
+ * <p>The {@code task} may include nested calls to {@code doWithContext}.
+ * The value returned by {@link #getContext} at any point is the Map
+ * provided to the most recent {@code doWithContext} (in the current thread)
+ * that has not yet returned.</p>
+ *
+ * <p>The {@link #getContext()} method returns the same value immediately
+ * after a call to this method as immediately before. In other words,
+ * {@code doWithContext} only affects the context during the execution of
+ * the {@code task}.</p>
+ *
+ * <p>As an example, suppose you want to get an attribute with whatever
+ * context has already been set, plus the locale set to "fr". You could
+ * write this:</p>
+ *
+ * <pre>
+ * {@code Map<String, String>} context =
+ * new {@code HashMap<String, String>}(ClientContext.getContext());
+ * context.put(ClientContext.LOCALE_KEY, "fr");
+ * String lastProblemDescription =
+ * ClientContext.doWithContext(context, new {@code Callable<String>}() {
+ * public String call() {
+ * return (String) mbeanServer.getAttribute(mbean, "LastProblemDescription");
+ * }
+ * });
+ * </pre>
+ *
+ * @param <T> the type of value that the task will return. This type
+ * parameter is usually inferred from the type of the {@code task}
+ * parameter. For example, if {@code task} is a {@code Callable<String>}
+ * then {@code T} is {@code String}. If the task does not return a value,
+ * use a {@code Callable<Void>} and return null from its
+ * {@link Callable#call call} method.
+ * @param context the context to use while executing {@code task}.
+ * @param task the task to run with the {@code key}={@code value}
+ * binding.
+ * @return the result of {@link Callable#call() task.call()}.
+ * @throws IllegalArgumentException if either parameter is null, or
+ * if any key in {@code context} is null or empty, or if any value
+ * in {@code context} is null.
+ * @throws Exception If {@link Callable#call() task.call()} throws an
+ * exception, {@code doWithContext} throws the same exception.
+ */
+ public static <T> T doWithContext(Map<String, String> context, Callable<T> task)
+ throws Exception {
+ if (context == null || task == null)
+ throw new IllegalArgumentException("Null parameter");
+ Map<String, String> contextCopy = new TreeMap<String, String>(context);
+ validateContext(contextCopy);
+ Map<String, String> oldContextMap = contextThreadLocal.get();
+ try {
+ contextThreadLocal.set(contextCopy);
+ return task.call();
+ } finally {
+ contextThreadLocal.set(oldContextMap);
+ }
+ }
+
+ private static void validateContext(Map<String, String> context) {
+ for (Map.Entry<String, String> entry : context.entrySet()) {
+ // If the user passes a raw Map rather than a Map<String, String>,
+ // entries could contain objects other than Strings. If so,
+ // we'll get a ClassCastException here.
+ String key = entry.getKey();
+ String value = entry.getValue();
+ if (key == null || value == null)
+ throw new IllegalArgumentException("Null key or value in context");
+ if (key.equals(""))
+ throw new IllegalArgumentException("Empty key in context");
+ }
+ }
+
+ /**
+ * <p>Return an MBeanServer object that is equivalent to the given
+ * MBeanServer object except that operations on MBeans run with
+ * the given Locale in their {@linkplain #getContext() thread context}.
+ * Note that this will only work if the given MBeanServer supports
+ * contexts, as described <a href="#remote-use">above</a>.</p>
+ *
+ * <p>This method is equivalent to {@link #withContext(MBeanServer,
+ * String, String) withContext}<code>(mbs, {@value LOCALE_KEY},
+ * locale.toString())</code>.</p>
+ *
+ * @throws IllegalArgumentException if either parameter is null, or if
+ * {@code mbs} does not support contexts. In the second case only,
+ * the cause of the {@code IllegalArgumentException} will be an {@link
+ * InstanceNotFoundException}.
+ */
+ public static MBeanServer withLocale(MBeanServer mbs, Locale locale) {
+ return withLocale(mbs, MBeanServer.class, locale);
+ }
+
+ /**
+ * <p>Return an MBeanServerConnection object that is equivalent to the given
+ * MBeanServerConnection object except that operations on MBeans run with
+ * the given Locale in their {@linkplain #getContext() thread context}.
+ * Note that this will only work if the given MBeanServerConnection supports
+ * contexts, as described <a href="#remote-use">above</a>.</p>
+ *
+ * <p>This method is equivalent to {@link #withContext(MBeanServerConnection,
+ * String, String) withContext}<code>(mbs, {@value LOCALE_KEY},
+ * locale.toString())</code>.</p>
+ *
+ * @throws IllegalArgumentException if either parameter is null, or if
+ * the communication with {@code mbsc} fails, or if {@code mbsc} does not
+ * support contexts. If the communication with {@code mbsc} fails, the
+ * {@linkplain Throwable#getCause() cause} of this exception will be an
+ * {@code IOException}. If {@code mbsc} does not support contexts, the
+ * cause will be an {@link InstanceNotFoundException}.
+ */
+ public static MBeanServerConnection withLocale(
+ MBeanServerConnection mbsc, Locale locale) {
+ return withLocale(mbsc, MBeanServerConnection.class, locale);
+ }
+
+ private static <T extends MBeanServerConnection> T withLocale(
+ T mbsc, Class<T> mbscClass, Locale locale) {
+ if (locale == null)
+ throw new IllegalArgumentException("Null locale");
+ return withContext(mbsc, mbscClass, LOCALE_KEY, locale.toString());
+ }
+
+ /**
+ * <p>Return an MBeanServer object that is equivalent to the given
+ * MBeanServer object except that operations on MBeans run with
+ * the given key bound to the given value in their {@linkplain
+ * #getContext() thread context}.
+ * Note that this will only work if the given MBeanServer supports
+ * contexts, as described <a href="#remote-use">above</a>.</p>
+ *
+ * @param mbs the original MBeanServer.
+ * @param key the key to bind in the context of MBean operations
+ * in the returned MBeanServer object.
+ * @param value the value to bind to the key in the context of MBean
+ * operations in the returned MBeanServer object.
+ * @throws IllegalArgumentException if any parameter is null, or
+ * if {@code key} is the empty string, or if {@code mbs} does not support
+ * contexts. In the last case only, the cause of the {@code
+ * IllegalArgumentException} will be an {@link InstanceNotFoundException}.
+ */
+ public static MBeanServer withContext(
+ MBeanServer mbs, String key, String value) {
+ return withContext(mbs, MBeanServer.class, key, value);
+ }
+
+ /**
+ * <p>Return an MBeanServerConnection object that is equivalent to the given
+ * MBeanServerConnection object except that operations on MBeans run with
+ * the given key bound to the given value in their {@linkplain
+ * #getContext() thread context}.
+ * Note that this will only work if the given MBeanServerConnection supports
+ * contexts, as described <a href="#remote-use">above</a>.</p>
+ *
+ * @param mbsc the original MBeanServerConnection.
+ * @param key the key to bind in the context of MBean operations
+ * in the returned MBeanServerConnection object.
+ * @param value the value to bind to the key in the context of MBean
+ * operations in the returned MBeanServerConnection object.
+ * @throws IllegalArgumentException if any parameter is null, or
+ * if {@code key} is the empty string, or if the communication with {@code
+ * mbsc} fails, or if {@code mbsc} does not support contexts. If
+ * the communication with {@code mbsc} fails, the {@linkplain
+ * Throwable#getCause() cause} of this exception will be an {@code
+ * IOException}. If {@code mbsc} does not support contexts, the cause will
+ * be an {@link InstanceNotFoundException}.
+ */
+ public static MBeanServerConnection withContext(
+ MBeanServerConnection mbsc, String key, String value) {
+ return withContext(mbsc, MBeanServerConnection.class, key, value);
+ }
+
+
+ /**
+ * <p>Returns an MBeanServerConnection object that is equivalent to the
+ * given MBeanServerConnection object except that remote operations on
+ * MBeans run with the context that has been established by the client
+ * using {@link #doWithContext doWithContext}. Note that this will
+ * only work if the remote system supports contexts, as described <a
+ * href="#remote-use">above</a>.</p>
+ *
+ * <p>For example, suppose the remote system does support contexts, and you
+ * have created a {@code JMXConnector} like this:</p>
+ *
+ * <pre>
+ * JMXServiceURL url = ...;
+ * JMXConnector client = JMXConnectorFactory.connect(url);
+ * MBeanServerConnection mbsc = client.getMBeanServerConnection();
+ * <b>mbsc = ClientContext.withDynamicContext(mbsc);</b>
+ * </pre>
+ *
+ * <p>Then if you do this...</p>
+ *
+ * <pre>
+ * MBeanInfo mbi = ClientContext.doWithContext(
+ * Collections.singletonMap(ClientContext.LOCALE_KEY, "fr"),
+ * new {@code Callable<MBeanInfo>}() {
+ * public MBeanInfo call() {
+ * return mbsc.getMBeanInfo(objectName);
+ * }
+ * });
+ * </pre>
+ *
+ * <p>...then the context with the locale set to "fr" will be in place
+ * when the {@code getMBeanInfo} is executed on the remote MBean Server.</p>
+ *
+ * @param mbsc the original MBeanServerConnection.
+ *
+ * @throws IllegalArgumentException if the {@code mbsc} parameter is null,
+ * or if the communication with {@code mbsc} fails, or if {@code mbsc}
+ * does not support contexts. If the communication with {@code mbsc}
+ * fails, the {@linkplain Throwable#getCause() cause} of this exception
+ * will be an {@code IOException}. If {@code mbsc} does not support
+ * contexts, the cause will be an {@link InstanceNotFoundException}.
+ */
+ public static MBeanServerConnection withDynamicContext(
+ MBeanServerConnection mbsc) {
+ // Probe mbsc to get the right exception if it's incommunicado or
+ // doesn't support namespaces.
+ JMXNamespaces.narrowToNamespace(mbsc, NAMESPACE);
+ return (MBeanServerConnection) Proxy.newProxyInstance(
+ MBeanServerConnection.class.getClassLoader(),
+ new Class<?>[] {MBeanServerConnection.class},
+ new DynamicContextIH(mbsc));
+ }
+
+ private static class DynamicContextIH implements InvocationHandler {
+ private final MBeanServerConnection mbsc;
+
+ public DynamicContextIH(MBeanServerConnection mbsc) {
+ this.mbsc = mbsc;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable {
+ MBeanServerConnection dynMBSC = withContext(
+ mbsc, MBeanServerConnection.class, getContext(), false);
+ try {
+ return method.invoke(dynMBSC, args);
+ } catch (InvocationTargetException e) {
+ throw e.getCause();
+ }
+ }
+ }
+
+ private static <T extends MBeanServerConnection> T withContext(
+ T mbsc, Class<T> mbscClass, String key, String value) {
+ return withContext(
+ mbsc, mbscClass, Collections.singletonMap(key, value), true);
+ }
+
+ private static <T extends MBeanServerConnection> T withContext(
+ T mbsc, Class<T> mbscClass, Map<String, String> context,
+ boolean probe) {
+ if (mbsc == null || context == null)
+ throw new IllegalArgumentException("Null parameter");
+ if (context.isEmpty())
+ return mbsc;
+ validateContext(context);
+ Map<String, String> contextMap = null;
+ if (mbsc.getClass() == RoutingServerProxy.class ||
+ mbsc.getClass() == RoutingProxy.class) {
+ RoutingProxy<?> nsp = (RoutingProxy<?>) mbsc;
+ String where = nsp.getSourceNamespace();
+ if (where.startsWith(NAMESPACE_PLUS_SEP)) {
+ /* Try to merge the existing context namespace with the
+ * new one. If it doesn't work, we fall back to just
+ * prefixing jmx.context//key=value, which
+ * might lead to a name like jmx.c//k1=v1//jmx.c//k2=v2//d:k=v.
+ */
+ String encodedContext =
+ where.substring(NAMESPACE_PLUS_SEP.length());
+ if (encodedContext.indexOf(NAMESPACE_SEPARATOR) < 0) {
+ contextMap = stringToMapOrNull(encodedContext);
+ if (contextMap != null) {
+ contextMap.putAll(context);
+ mbsc = mbscClass.cast(nsp.source());
+ }
+ }
+ }
+ }
+ if (contextMap == null)
+ contextMap = context;
+ String contextDir = NAMESPACE_PLUS_SEP + mapToString(contextMap);
+ if (mbscClass == MBeanServer.class) {
+ return mbscClass.cast(RoutingServerProxy.cd(
+ (MBeanServer) mbsc, contextDir, probe));
+ } else if (mbscClass == MBeanServerConnection.class) {
+ return mbscClass.cast(RoutingConnectionProxy.cd(
+ mbsc, contextDir, probe));
+ } else
+ throw new AssertionError("Bad MBSC: " + mbscClass);
+ }
+
+ /**
+ * <p>Returns an encoded context prefix for ObjectNames.
+ * If the given context is empty, {@code ""} is returned.
+ * Otherwise, this method returns a string of the form
+ * {@code "jmx.context//key=value;key=value;..."}.
+ * For example, if the context has keys {@code "jmx.locale"}
+ * and {@code "xid"} with respective values {@code "fr"}
+ * and {@code "1234"}, this method will return
+ * {@code "jmx.context//jmx.locale=fr;xid=1234"} or
+ * {@code "jmx.context//xid=1234;jmx.locale=fr"}.</p>
+ *
+ * <p>Each key and each value in the encoded string is subject to
+ * encoding as if by the method {@link URLEncoder#encode(String, String)}
+ * with a character encoding of {@code "UTF-8"}, but with the additional
+ * encoding of any {@code *} character as {@code "%2A"}. This ensures
+ * that keys and values can contain any character. Without encoding,
+ * characters such as {@code =} and {@code :} would pose problems.</p>
+ *
+ * @param context the context to encode.
+ *
+ * @return the context in encoded form.
+ *
+ * @throws IllegalArgumentException if the {@code context} parameter
+ * is null or if it contains a null key or value.
+ **/
+ public static String encode(Map<String, String> context) {
+ if (context == null)
+ throw new IllegalArgumentException("Null context");
+ if (context.isEmpty())
+ return "";
+ StringBuilder sb = new StringBuilder();
+ for (Map.Entry<String, String> entry : context.entrySet()) {
+ String key = entry.getKey();
+ String value = entry.getValue();
+ if (key == null || value == null)
+ throw new IllegalArgumentException("Null key or value");
+ if (sb.length() > 0)
+ sb.append(";");
+ sb.append(encode(key)).append("=").append(encode(value));
+ }
+ sb.insert(0, NAMESPACE_PLUS_SEP);
+ return sb.toString();
+ }
+
+ /**
+ * <p>Create a new {@link MBeanServerForwarder} that applies the context
+ * received from a client to the current thread. A client using
+ * one of the various {@code with*} methods (for example {@link
+ * #withContext(MBeanServerConnection, String, String) withContext}) will
+ * encode that context into the {@code ObjectName} of each
+ * {@code MBeanServer} request. The object returned by this method
+ * decodes the context from that {@code ObjectName} and applies it
+ * as described for {@link #doWithContext doWithContext} while performing
+ * the {@code MBeanServer} request using the {@code ObjectName} without
+ * the encoded context.</p>
+ *
+ * <p>This forwarder can be used in a number of ways:</p>
+ *
+ * <ul>
+ * <li>
+ * <p>To add context decoding to a local {@code MBeanServer}, you can
+ * write:</p>
+ * <pre>
+ * MBeanServer mbs = {@link
+ * java.lang.management.ManagementFactory#getPlatformMBeanServer()
+ * ManagementFactory.getPlatformMBeanServer()}; // for example
+ * mbs = ClientContext.newContextForwarder(mbs, null);
+ * </pre>
+ *
+ * <li>
+ * <p>To add context decoding to a {@linkplain
+ * javax.management.remote.JMXConnectorServer connector server}:</p>
+ * <pre>
+ * JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(...);
+ * MBeanServer nextMBS = cs.getMBeanServer();
+ * MBeanServerForwarder mbsf = ClientContext.newContextForwarder(nextMBS, null);
+ * cs.{@link
+ * javax.management.remote.JMXConnectorServer#setMBeanServerForwarder
+ * setMBeanServerForwarder}(mbsf);
+ * </pre>
+ *
+ * <li>
+ * <p>For connectors, such as the standard RMI connector, that support
+ * a {@linkplain
+ * javax.management.remote.JMXConnectorServer#getSystemMBeanServerForwarder
+ * system chain} of {@code MBeanServerForwarder}s, this forwarder will
+ * be installed in that chain by default. See
+ * {@link javax.management.remote.JMXConnectorServer#CONTEXT_FORWARDER
+ * JMXConnectorServer.CONTEXT_FORWARDER}.
+ * </p>
+ *
+ * </ul>
+ *
+ * @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 include a context 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 that contain
+ * an encoded context should be sent once the context has been decoded.
+ * For example, if the request is {@link MBeanServer#getAttribute
+ * getAttribute}{@code ("jmx.context//jmx.locale=fr//java.lang:type=Runtime",
+ * "Name")}, then the {@linkplain #getContext() context} of the thread
+ * executing that request will have {@code "jmx.locale"} set to {@code "fr"}
+ * while executing {@code loopMBS.getAttribute("java.lang:type=Runtime",
+ * "Name")}. 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 decodes client context
+ * from {@code ObjectName}s.
+ */
+ /*
+ * What we're building here is confusing enough to need a diagram.
+ * The MBSF that we return is actually the composition of two forwarders:
+ * the first one simulates the existence of the MBean
+ * jmx.context//:type=JMXNamespace, and the second one simulates the
+ * existence of the namespace jmx.context//. Furthermore, that namespace
+ * loops back to the composed forwarder, so that something like
+ * jmx.context//foo=bar//jmxcontext//baz=buh will work. And the loopback
+ * goes through yet another forwarder, which simulates the existence of
+ * (e.g.) jmx.context//foo=bar//:type=JMXNamespace, which is needed
+ * notably so that narrowToNamespace will work.
+ *
+ * | +--------------------------------------------------+
+ * v v |
+ * +----------------+ |
+ * | Handler MBSF |->accesses to jmx.context//:type=JMXNamespace |
+ * +----------------+ (handled completely here) +-------------------+
+ * | | 2nd Handler MBSF |
+ * v +-------------------+
+ * +----------------+ ^
+ * | Namespace MBSF |->accesses to jmx.context//**-------------------+
+ * +----------------+ (after attaching context to thread)
+ * |
+ * v accesses to anything else
+ *
+ * And finally, we need to ensure that from the outside the composed object
+ * looks like a single forwarder, so that its get/setMBeanServer methods
+ * will do the expected thing. That's what the anonymous subclass is for.
+ */
+ public static MBeanServerForwarder newContextForwarder(
+ MBeanServer nextMBS, MBeanServer loopMBS) {
+ final MBeanServerForwarder mbsWrapper =
+ new IdentityMBeanServerForwarder(nextMBS);
+ DynamicMBean handlerMBean = new StandardMBean(
+ new JMXNamespace(mbsWrapper), JMXNamespaceMBean.class, false);
+ SingleMBeanForwarder handlerForwarder = new SingleMBeanForwarder(
+ CLIENT_CONTEXT_NAMESPACE_HANDLER, handlerMBean, true) {
+ @Override
+ public MBeanServer getMBeanServer() {
+ return ((MBeanServerForwarder) super.getMBeanServer()).getMBeanServer();
+ }
+
+ @Override
+ public void setMBeanServer(MBeanServer mbs1) {
+ MBeanServerForwarder mbsf1 = (MBeanServerForwarder)
+ super.getMBeanServer();
+ if (mbsf1 != null)
+ mbsf1.setMBeanServer(mbs1);
+ else
+ super.setMBeanServer(mbs1);
+ mbsWrapper.setMBeanServer(mbs1);
+ }
+ };
+ if (loopMBS == null)
+ loopMBS = handlerForwarder;
+ ContextInvocationHandler contextIH =
+ new ContextInvocationHandler(nextMBS, loopMBS);
+ MBeanServerForwarder contextForwarder = newForwarderProxy(contextIH);
+ handlerForwarder.setMBeanServer(contextForwarder);
+ return handlerForwarder;
+ }
+
+ /**
+ * <p>Create a new {@link MBeanServerForwarder} that localizes
+ * descriptions in {@code MBeanInfo} instances returned by
+ * {@link MBeanServer#getMBeanInfo getMBeanInfo}. The {@code
+ * MBeanServerForwarder} returned by this method passes all {@code
+ * MBeanServer} methods through unchanged to the supplied object, {@code
+ * mbs}, with the exception of {@code getMBeanInfo}. To handle {@code
+ * getMBeanInfo(objectName)}, it calls {@code mbs.getMBeanInfo(objectName)}
+ * to get an {@code MBeanInfo}, {@code mbi}; it calls {@link
+ * MBeanServer#getClassLoaderFor mbs.getClassLoaderFor(objectName)} to
+ * get a {@code ClassLoader}, {@code cl}; and it calls {@link
+ * #getLocale} to get a {@code Locale}, {@code locale}. The order
+ * of these three calls is not specified. Then the result is {@code
+ * mbi.localizeDescriptions(locale, loader)}.</p>
+ *
+ * <p>This forwarder can be used in a number of ways:</p>
+ *
+ * <ul>
+ * <li>
+ * <p>To add description localization to a local {@code MBeanServer}, you
+ * can write:</p>
+ *
+ * <pre>
+ * MBeanServer mbs = {@link
+ * java.lang.management.ManagementFactory#getPlatformMBeanServer()
+ * ManagementFactory.getPlatformMBeanServer()}; // for example
+ * mbs = ClientContext.newLocalizeMBeanInfoForwarder(mbs);
+ * </pre>
+ *
+ * <li>
+ * <p>To add description localization to a {@linkplain
+ * javax.management.remote.JMXConnectorServer connector server}, you will
+ * need to add both a {@linkplain #newContextForwarder context forwarder}
+ * and a localization forwarder, for example like this:</p>
+ *
+ * <pre>
+ * JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(...);
+ * MBeanServer nextMBS = cs.getMBeanServer();
+ * MBeanServerForwarder localizeMBSF =
+ * ClientContext.newLocalizeMBeanInfoForwarder(nextMBS);
+ * MBeanServerForwarder contextMBSF =
+ * ClientContext.newContextForwarder(localizeMBSF, null);
+ * cs.{@link
+ * javax.management.remote.JMXConnectorServer#setMBeanServerForwarder
+ * setMBeanServerForwarder}(contextMBSF);
+ * </pre>
+ *
+ * <p>Notice that the context forwarder must run before the localization
+ * forwarder, so that the locale is correctly established when the latter
+ * runs. So the {@code nextMBS} parameter of the context forwarder must
+ * be the localization forwarder, and not vice versa.</p>
+ *
+ * <li>
+ * <p>For connectors, such as the standard RMI connector, that support
+ * a {@linkplain
+ * javax.management.remote.JMXConnectorServer#getSystemMBeanServerForwarder
+ * system chain} of {@code MBeanServerForwarder}s, the context forwarder and
+ * the localization forwarder will be installed in that chain, in the right
+ * order, if you include
+ * {@link
+ * javax.management.remote.JMXConnectorServer#LOCALIZE_MBEAN_INFO_FORWARDER
+ * LOCALIZE_MBEAN_INFO_FORWARDER} in the environment {@code Map} with
+ * the value {@code "true"}, for example like this:</p>
+ * </p>
+ * <pre>
+ * MBeanServer mbs = ...;
+ * JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://...");
+ * {@code Map<String, Object>} env = new {@code HashMap<String, Object>}();
+ * env.put(JMXConnectorServer.LOCALIZE_MBEAN_INFO_FORWARDER, "true");
+ * JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(
+ * url, env, mbs);
+ * </pre>
+ *
+ * </ul>
+ *
+ * @param mbs the next {@code MBeanServer} in the chain of
+ * forwarders, which might be another {@code MBeanServerForwarder}
+ * or a plain {@code MBeanServer}. 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.
+ *
+ * @return a new {@code MBeanServerForwarder} that localizes descriptions
+ * in the result of {@code getMBeanInfo}.
+ */
+ public static MBeanServerForwarder newLocalizeMBeanInfoForwarder(
+ MBeanServer mbs) {
+ return new IdentityMBeanServerForwarder(mbs) {
+ @Override
+ public MBeanInfo getMBeanInfo(ObjectName name)
+ throws InstanceNotFoundException, IntrospectionException,
+ ReflectionException {
+ MBeanInfo mbi = super.getMBeanInfo(name);
+ Locale locale = getLocale();
+ ClassLoader loader = getClassLoaderFor(name);
+ return mbi.localizeDescriptions(locale, loader);
+ }
+ };
+ }
+
+ private static MBeanServerForwarder newForwarderProxy(InvocationHandler ih) {
+ return (MBeanServerForwarder) Proxy.newProxyInstance(
+ MBeanServerForwarder.class.getClassLoader(),
+ new Class<?>[] {MBeanServerForwarder.class},
+ ih);
+ }
+
+ // A proxy connection that will strip the 'contextDir' at input (routing),
+ // and put it back at output (createMBean / registerMBean / query* /
+ // getObjectInstance). Usually RoutingProxy / RoutingServerProxy are used
+ // the other way round (they are used for 'cd' - where they need to add
+ // something at input and remove it at output).
+ // For 'cd' operations we create RoutingProxys with a non empty sourceDir,
+ // and a possibly non-empty targetDir. This is the only case where we use
+ // RoutingProxies with an empty sourceDir (sourceDir is what we add at input
+ // and remove at output, targetDir is what we remove at input and add at
+ // output.
+ //
+ // Note that using a transient ContextRoutingConnection
+ // is possible only because RoutingProxys don't rewrite
+ // notifications sources - otherwise we would have to
+ // keep the ContextRoutingConnection - just to preserve
+ // the 'wrapping listeners'
+ //
+ private static final class ContextRoutingConnection
+ extends RoutingServerProxy {
+ public ContextRoutingConnection(MBeanServer source,
+ String contextDir) {
+ super(source, "", contextDir, false);
+ }
+
+ // Not really needed - but this is safer and more optimized.
+ // See RoutingProxy for more details.
+ //
+ @Override
+ public Integer getMBeanCount() {
+ return source().getMBeanCount();
+ }
+
+ // Not really needed - but this is safer and more optimized.
+ // See RoutingProxy for more details.
+ //
+ @Override
+ public String[] getDomains() {
+ return source().getDomains();
+ }
+
+ // Not really needed - but this is safer and more optimized.
+ // See RoutingProxy for more details.
+ //
+ @Override
+ public String getDefaultDomain() {
+ return source().getDefaultDomain();
+ }
+
+ }
+
+ private static class ContextInvocationHandler implements InvocationHandler {
+ /*
+ * MBeanServer requests that don't include jmx.context//foo=bar//
+ * are forwarded to forwardMBS, which is the unadorned MBeanServer
+ * that knows nothing about the context namespace.
+ * MBeanServer requests that do include this prefix will
+ * usually (depending on the value of the loopMBS parameter to
+ * newContextForwarder) loop back to the combined MBeanServerForwarder
+ * that first implements
+ * jmx.context//:type=JMXNamespace and then implements
+ * jmx.context//foo=bar//. The reason is that it is valid
+ * to have jmx.context//foo=bar//jmx.context//baz=buh//, although
+ * usually that will be combined into jmx.context//foo=bar;baz=buh//.
+ *
+ * Before forwarding to loopMBS, we must check for :type=JMXNamespace
+ * so that jmx.context//foo=bar//:type=JMXNamespace will exist. Its
+ * existence is partial because it must remain "invisible": it should
+ * not show up in queryNames or getMBeanCount even though it does
+ * accept getAttribute and isRegistered and all other methods that
+ * reference a single MBean.
+ */
+ private MBeanServer forwardMBS;
+ private final MBeanServer loopMBS;
+ private static final MBeanServer emptyMBS = new MBeanServerSupport() {
+ @Override
+ public DynamicMBean getDynamicMBeanFor(ObjectName name)
+ throws InstanceNotFoundException {
+ throw new InstanceNotFoundException(name.toString());
+ }
+
+ @Override
+ protected Set<ObjectName> getNames() {
+ return Collections.emptySet();
+ }
+ };
+
+ ContextInvocationHandler(MBeanServer forwardMBS, MBeanServer loopMBS) {
+ this.forwardMBS = forwardMBS;
+ DynamicMBean handlerMBean = new StandardMBean(
+ new JMXNamespace(loopMBS), JMXNamespaceMBean.class, false);
+ MBeanServerForwarder handlerMBS = new SingleMBeanForwarder(
+ NAMESPACE_HANDLER_WITHOUT_NAMESPACE, handlerMBean, false);
+ handlerMBS.setMBeanServer(loopMBS);
+ this.loopMBS = handlerMBS;
+ }
+
+ public Object invoke(Object proxy, final Method method, final Object[] args)
+ throws Throwable {
+ String methodName = method.getName();
+ Class<?>[] paramTypes = method.getParameterTypes();
+
+ // If this is a method from MBeanServerForwarder, handle it here.
+ // There are only two such methods: getMBeanServer() and
+ // setMBeanServer(mbs).
+ if (methodName.equals("getMBeanServer"))
+ return forwardMBS;
+ else if (methodName.equals("setMBeanServer")) {
+ this.forwardMBS = (MBeanServer) args[0];
+ return null;
+ }
+
+ // It is a method from MBeanServer.
+ // Find the first parameter whose declared type is ObjectName,
+ // and see if it is in the context namespace. If so we need to
+ // trigger the logic for that namespace. If not, we simply
+ // forward to the next MBeanServer in the chain. This logic
+ // depends on the fact that if a method in the MBeanServer interface
+ // has a "routing" ObjectName parameter, it is always the first
+ // parameter of that type. Conversely, if a method has an
+ // ObjectName parameter, then it makes sense to "route" that
+ // method. Except for deserialize and instantiate, but if we
+ // recognize a context namespace in those methods' ObjectName
+ // parameters it is pretty harmless.
+ int objectNameI = -1;
+ for (int i = 0; i < paramTypes.length; i++) {
+ if (paramTypes[i] == ObjectName.class) {
+ objectNameI = i;
+ break;
+ }
+ }
+
+ if (objectNameI < 0)
+ return invoke(method, forwardMBS, args);
+
+ ObjectName target = (ObjectName) args[objectNameI];
+ if (target == null ||
+ !target.getDomain().startsWith(NAMESPACE_PLUS_SEP))
+ return invoke(method, forwardMBS, args);
+
+ String domain = target.getDomain().substring(NAMESPACE_PLUS_SEP.length());
+
+ // The method routes through the (simulated) context namespace.
+ // Decode the context after it, e.g. jmx.context//jmx.locale=fr//...
+ // If there is no context part, we can throw an exception,
+ // because a forwarder has already handled the unique MBean
+ // jmx.context//:type=JMXNamespace.
+ int sep = domain.indexOf(NAMESPACE_SEPARATOR);
+ if (sep < 0)
+ return invoke(method, emptyMBS, args); // throw exception
+ final String encodedContext = domain.substring(0, sep);
+
+ if (method.getName().startsWith("query") &&
+ (encodedContext.contains("*") || encodedContext.contains("?"))) {
+ // Queries like jmx.context//*//d:k=v return
+ // an empty set, consistent with "real" namespaces.
+ return Collections.EMPTY_SET;
+ }
+
+ Map<String, String> ctx = new TreeMap<String, String>(getContext());
+ ctx.putAll(stringToMap(encodedContext));
+
+ return doWithContext(ctx, new Callable<Object>() {
+ public Object call() throws Exception {
+ // Create a proxy connection that will strip
+ // "jmx.context//" + encodedContext + "//" on input,
+ // and put it back on output.
+ //
+ // Note that using a transient ContextRoutingConnection
+ // is possible only because it doesn't rewrite
+ // notification sources - otherwise we would have to
+ // keep the ContextRoutingConnection - just to preserve
+ // the 'wrapping listeners'
+ //
+ String namespace = NAMESPACE_PLUS_SEP + encodedContext;
+ final ContextRoutingConnection route =
+ new ContextRoutingConnection(loopMBS, namespace);
+
+ if (LOG.isLoggable(Level.FINE))
+ LOG.fine("context="+encodedContext);
+ if (LOG.isLoggable(Level.FINER))
+ LOG.finer(method.getName()+""+
+ ((args==null)?"()":(""+Arrays.asList(args))));
+
+ return invoke(method, route, args);
+ }
+ });
+ }
+
+ private static Object invoke(Method method, Object target, Object[] args)
+ throws Exception {
+ try {
+ return method.invoke(target, args);
+ } catch (InvocationTargetException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof Error)
+ throw (Error) cause;
+ throw (Exception) cause;
+ }
+ }
+ }
+
+ private static String mapToString(Map<String, String> map) {
+ StringBuilder sb = new StringBuilder();
+ for (Map.Entry<String, String> entry : map.entrySet()) {
+ String key = encode(entry.getKey());
+ String value = encode(entry.getValue());
+ if (sb.length() > 0)
+ sb.append(";");
+ sb.append(key).append("=").append(value);
+ }
+ return sb.toString();
+ }
+
+ private static Map<String, String> stringToMap(String encodedContext) {
+ Map<String, String> map = stringToMapOrNull(encodedContext);
+ if (map == null) {
+ throw new IllegalArgumentException(
+ "Invalid encoded context: " + encodedContext);
+ }
+ return map;
+ }
+
+ private static Map<String, String> stringToMapOrNull(String encodedContext) {
+ Map<String, String> map = new LinkedHashMap<String, String>();
+ StringTokenizer stok = new StringTokenizer(encodedContext, ";");
+ while (stok.hasMoreTokens()) {
+ String tok = stok.nextToken();
+ int eq = tok.indexOf('=');
+ if (eq < 0)
+ return null;
+ String key = decode(tok.substring(0, eq));
+ if (key.equals(""))
+ return null;
+ String value = decode(tok.substring(eq + 1));
+ map.put(key, value);
+ }
+ return map;
+ }
+
+ private static String encode(String s) {
+ try {
+ s = URLEncoder.encode(s, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e); // Should not happen
+ }
+ return s.replace("*", "%2A");
+ // The * character is left intact in URL encodings, but for us it
+ // is special (an ObjectName wildcard) so we must map it.
+ // We are assuming that URLDecoder will decode it the same way as any
+ // other hex escape.
+ }
+
+ private static String decode(String s) {
+ try {
+ return URLDecoder.decode(s, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
--- a/jdk/src/share/classes/javax/management/Descriptor.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/javax/management/Descriptor.java Thu Nov 13 11:15:01 2008 -0800
@@ -35,8 +35,8 @@
// 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 @@
* 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 @@
* 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 @@
* <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>
*
--- a/jdk/src/share/classes/javax/management/JMX.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/javax/management/JMX.java Thu Nov 13 11:15:01 2008 -0800
@@ -30,6 +30,7 @@
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.Proxy;
import java.util.Map;
import java.util.TreeMap;
+import javax.management.namespace.JMXNamespaces;
import javax.management.openmbean.MXBeanMappingFactory;
/**
@@ -60,6 +62,21 @@
*/
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.
@@ -79,6 +96,12 @@
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 @@
* <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>
*
@@ -808,4 +830,80 @@
((DynamicWrapperMBean) mbean).getWrappedObject() : mbean;
return (MBeanInjector.injectsSendNotification(resource));
}
+
+ /**
+ * <p>Return the version of the JMX specification that a (possibly remote)
+ * MBean Server is using. The JMX specification described in this
+ * documentation is version 2.0. The earlier versions that might be
+ * reported by this method are 1.0, 1.1, 1.2, and 1.4. (There is no 1.3.)
+ * All of these versions and all future versions can be compared using
+ * {@link String#compareTo(String)}. So, for example, to tell if
+ * {@code mbsc} is running at least version 2.0 you can write:</p>
+ *
+ * <pre>
+ * String version = JMX.getSpecificationVersion(mbsc, null);
+ * boolean atLeast2dot0 = (version.compareTo("2.0") >= 0);
+ * </pre>
+ *
+ * <p>A remote MBean Server might be running an earlier version of the
+ * JMX API, and in that case <a href="package-summary.html#interop">certain
+ * features</a> might not be available in it.</p>
+ *
+ * <p>The version of the MBean Server {@code mbsc} is not necessarily
+ * the version of all namespaces within that MBean Server, for example
+ * if some of them use {@link javax.management.namespace.JMXRemoteNamespace
+ * JMXRemoteNamespace}. To determine the version of the namespace
+ * that a particular MBean is in, give its name as the {@code mbeanName}
+ * parameter.</p>
+ *
+ * @param mbsc a connection to an MBean Server.
+ *
+ * @param mbeanName the name of an MBean within that MBean Server, or null.
+ * If non-null, the namespace of this name, as determined by
+ * {@link JMXNamespaces#getContainingNamespace
+ * JMXNamespaces.getContainingNamespace}, is the one whose specification
+ * version will be returned.
+ *
+ * @return the JMX specification version reported by that MBean Server.
+ *
+ * @throws IllegalArgumentException if {@code mbsc} is null, or if
+ * {@code mbeanName} includes a wildcard character ({@code *} or {@code ?})
+ * in its namespace.
+ *
+ * @throws IOException if the version cannot be obtained, either because
+ * there is a communication problem or because the remote MBean Server
+ * does not have the appropriate {@linkplain
+ * MBeanServerDelegateMBean#getSpecificationVersion() attribute}.
+ *
+ * @see <a href="package-summary.html#interop">Interoperability between
+ * versions of the JMX specification</a>
+ * @see MBeanServerDelegateMBean#getSpecificationVersion
+ */
+ public static String getSpecificationVersion(
+ MBeanServerConnection mbsc, ObjectName mbeanName)
+ throws IOException {
+ if (mbsc == null)
+ throw new IllegalArgumentException("Null MBeanServerConnection");
+
+ String namespace;
+ if (mbeanName == null)
+ namespace = "";
+ else
+ namespace = JMXNamespaces.getContainingNamespace(mbeanName);
+ if (namespace.contains("*") || namespace.contains("?")) {
+ throw new IllegalArgumentException(
+ "ObjectName contains namespace wildcard: " + mbeanName);
+ }
+
+ try {
+ if (namespace.length() > 0)
+ mbsc = JMXNamespaces.narrowToNamespace(mbsc, namespace);
+ return (String) mbsc.getAttribute(
+ MBeanServerDelegate.DELEGATE_NAME, "SpecificationVersion");
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ }
}
--- a/jdk/src/share/classes/javax/management/MBeanInfo.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/javax/management/MBeanInfo.java Thu Nov 13 11:15:01 2008 -0800
@@ -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.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 @@
* <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 @@
return (Descriptor) nonNullDescriptor(descriptor).clone();
}
+ @Override
public String toString() {
return
getClass().getName() + "[" +
@@ -505,6 +514,7 @@
* @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 @@
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 @@
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");
+ }
+ }
}
--- a/jdk/src/share/classes/javax/management/MBeanServerConnection.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/javax/management/MBeanServerConnection.java Thu Nov 13 11:15:01 2008 -0800
@@ -532,8 +532,30 @@
/**
- * Enables the values of several attributes of a named MBean. The MBean
- * is identified by its object name.
+ * <p>Retrieves the values of several attributes of a named MBean. The MBean
+ * is identified by its object name.</p>
+ *
+ * <p>If one or more attributes cannot be retrieved for some reason, they
+ * will be omitted from the returned {@code AttributeList}. The caller
+ * should check that the list is the same size as the {@code attributes}
+ * array. To discover what problem prevented a given attribute from being
+ * retrieved, call {@link #getAttribute getAttribute} for that attribute.</p>
+ *
+ * <p>Here is an example of calling this method and checking that it
+ * succeeded in retrieving all the requested attributes:</p>
+ *
+ * <pre>
+ * String[] attrNames = ...;
+ * AttributeList list = mbeanServerConnection.getAttributes(objectName, attrNames);
+ * if (list.size() == attrNames.length)
+ * System.out.println("All attributes were retrieved successfully");
+ * else {
+ * {@code List<String>} missing = new {@code ArrayList<String>}(<!--
+ * -->{@link java.util.Arrays#asList Arrays.asList}(attrNames));
+ * missing.removeAll(list.toMap().keySet());
+ * System.out.println("Did not retrieve: " + missing);
+ * }
+ * </pre>
*
* @param name The object name of the MBean from which the
* attributes are retrieved.
@@ -557,6 +579,7 @@
throws InstanceNotFoundException, ReflectionException,
IOException;
+
/**
* Sets the value of a specific attribute of a named MBean. The MBean
* is identified by its object name.
@@ -592,10 +615,36 @@
ReflectionException, IOException;
-
/**
- * Sets the values of several attributes of a named MBean. The MBean is
- * identified by its object name.
+ * <p>Sets the values of several attributes of a named MBean. The MBean is
+ * identified by its object name.</p>
+ *
+ * <p>If one or more attributes cannot be set for some reason, they will be
+ * omitted from the returned {@code AttributeList}. The caller should check
+ * that the input {@code AttributeList} is the same size as the output one.
+ * To discover what problem prevented a given attribute from being retrieved,
+ * it will usually be possible to call {@link #setAttribute setAttribute}
+ * for that attribute, although this is not guaranteed to work. (For
+ * example, the values of two attributes may have been rejected because
+ * they were inconsistent with each other. Setting one of them alone might
+ * be allowed.)<p>
+ *
+ * <p>Here is an example of calling this method and checking that it
+ * succeeded in setting all the requested attributes:</p>
+ *
+ * <pre>
+ * AttributeList inputAttrs = ...;
+ * AttributeList outputAttrs = mbeanServerConnection.setAttributes(<!--
+ * -->objectName, inputAttrs);
+ * if (inputAttrs.size() == outputAttrs.size())
+ * System.out.println("All attributes were set successfully");
+ * else {
+ * {@code List<String>} missing = new {@code ArrayList<String>}(<!--
+ * -->inputAttrs.toMap().keySet());
+ * missing.removeAll(outputAttrs.toMap().keySet());
+ * System.out.println("Did not set: " + missing);
+ * }
+ * </pre>
*
* @param name The object name of the MBean within which the
* attributes are to be set.
@@ -622,7 +671,39 @@
throws InstanceNotFoundException, ReflectionException, IOException;
/**
- * Invokes an operation on an MBean.
+ * <p>Invokes an operation on an MBean.</p>
+ *
+ * <p>Because of the need for a {@code signature} to differentiate
+ * possibly-overloaded operations, it is much simpler to invoke operations
+ * through an {@linkplain JMX#newMBeanProxy(MBeanServerConnection, ObjectName,
+ * Class) MBean proxy} where possible. For example, suppose you have a
+ * Standard MBean interface like this:</p>
+ *
+ * <pre>
+ * public interface FooMBean {
+ * public int countMatches(String[] patterns, boolean ignoreCase);
+ * }
+ * </pre>
+ *
+ * <p>The {@code countMatches} operation can be invoked as follows:</p>
+ *
+ * <pre>
+ * String[] myPatterns = ...;
+ * int count = (Integer) mbeanServerConnection.invoke(
+ * objectName,
+ * "countMatches",
+ * new Object[] {myPatterns, true},
+ * new String[] {String[].class.getName(), boolean.class.getName()});
+ * </pre>
+ *
+ * <p>Alternatively, it can be invoked through a proxy as follows:</p>
+ *
+ * <pre>
+ * String[] myPatterns = ...;
+ * FooMBean fooProxy = JMX.newMBeanProxy(
+ * mbeanServerConnection, objectName, FooMBean.class);
+ * int count = fooProxy.countMatches(myPatterns, true);
+ * </pre>
*
* @param name The object name of the MBean on which the method is
* to be invoked.
@@ -630,7 +711,8 @@
* @param params An array containing the parameters to be set when
* the operation is invoked
* @param signature An array containing the signature of the
- * operation. The class objects will be loaded using the same
+ * operation, an array of class names in the format returned by
+ * {@link Class#getName()}. The class objects will be loaded using the same
* class loader as the one used for loading the MBean on which the
* operation was invoked.
*
--- a/jdk/src/share/classes/javax/management/MBeanServerNotification.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/javax/management/MBeanServerNotification.java Thu Nov 13 11:15:01 2008 -0800
@@ -27,15 +27,70 @@
/**
- * 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>
+ *
+ * <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 {
--- a/jdk/src/share/classes/javax/management/Notification.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/javax/management/Notification.java Thu Nov 13 11:15:01 2008 -0800
@@ -54,7 +54,7 @@
* @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
@@ -244,6 +244,26 @@
}
/**
+ * <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.
*
* @param source the new source for this object.
@@ -285,8 +305,10 @@
/**
* 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,15 +339,26 @@
/**
* 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.
*
* @return The user data object. It is used for whatever data
@@ -355,6 +388,7 @@
*
* @return A String representation of this notification.
*/
+ @Override
public String toString() {
return super.toString()+"[type="+type+"][message="+message+"]";
}
--- a/jdk/src/share/classes/javax/management/QueryNotificationFilter.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/javax/management/QueryNotificationFilter.java Thu Nov 13 11:15:01 2008 -0800
@@ -26,7 +26,6 @@
package javax.management;
import com.sun.jmx.mbeanserver.NotificationMBeanSupport;
-import com.sun.jmx.mbeanserver.Util;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -43,6 +42,11 @@
* on both the client and the server in the remote case, so using this class
* instead is recommended where possible.</p>
*
+ * <p>Because this class was introduced in version 2.0 of the JMX API,
+ * it may not be present on a remote JMX agent that is running an earlier
+ * version. The method {@link JMX#getSpecificationVersion
+ * JMX.getSpecificationVersion} can be used to determine the remote version.</p>
+ *
* <p>This class uses the {@linkplain Query Query API} to specify the
* filtering logic. For example, to select only notifications where the
* {@linkplain Notification#getType() type} is {@code "com.example.mytype"},
--- a/jdk/src/share/classes/javax/management/event/EventClient.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/javax/management/event/EventClient.java Thu Nov 13 11:15:01 2008 -0800
@@ -29,7 +29,6 @@
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.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 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 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 @@
* 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 @@
}
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 @@
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 @@
// 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 @@
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 @@
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();
}
--- a/jdk/src/share/classes/javax/management/event/EventClientDelegate.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/javax/management/event/EventClientDelegate.java Thu Nov 13 11:15:01 2008 -0800
@@ -149,6 +149,7 @@
// 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 @@
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 @@
AccessController.doPrivileged(
new PrivilegedAction<EventClientDelegate>() {
public EventClientDelegate run() {
- return getEventClientDelegate(Forwarder.this);
+ return getEventClientDelegate(loopMBS);
}
});
DynamicMBean mbean = new StandardMBean(
@@ -208,11 +213,46 @@
* 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 @@
// 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 @@
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;
--- a/jdk/src/share/classes/javax/management/event/EventClientDelegateMBean.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/javax/management/event/EventClientDelegateMBean.java Thu Nov 13 11:15:01 2008 -0800
@@ -51,7 +51,8 @@
* 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 @@
*
* <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>
*
--- a/jdk/src/share/classes/javax/management/event/EventRelay.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/javax/management/event/EventRelay.java Thu Nov 13 11:15:01 2008 -0800
@@ -27,7 +27,6 @@
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
--- a/jdk/src/share/classes/javax/management/event/package-info.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/javax/management/event/package-info.java Thu Nov 13 11:15:01 2008 -0800
@@ -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>
*
--- a/jdk/src/share/classes/javax/management/namespace/JMXNamespaces.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/javax/management/namespace/JMXNamespaces.java Thu Nov 13 11:15:01 2008 -0800
@@ -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
@@ -69,23 +67,6 @@
/**
- * 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}
* of the given parent.
@@ -96,15 +77,18 @@
* 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 @@
* 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 @@
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+
--- a/jdk/src/share/classes/javax/management/namespace/JMXRemoteNamespace.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/javax/management/namespace/JMXRemoteNamespace.java Thu Nov 13 11:15:01 2008 -0800
@@ -27,10 +27,10 @@
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 javax.management.AttributeChangeNotification;
+import javax.management.ClientContext;
import javax.management.InstanceNotFoundException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanNotificationInfo;
@@ -220,17 +221,26 @@
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 @@
}
}
- 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 @@
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 @@
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);
--- a/jdk/src/share/classes/javax/management/openmbean/CompositeDataSupport.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/javax/management/openmbean/CompositeDataSupport.java Thu Nov 13 11:15:01 2008 -0800
@@ -33,12 +33,14 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
// jmx import
+import java.util.TreeSet;
//
@@ -60,16 +62,15 @@
* respective values.
* A {@link SortedMap} is used for faster retrieval of elements.
*/
- private SortedMap<String, Object> contents = new TreeMap<String, Object>();
+ private final SortedMap<String, Object> contents;
/**
* @serial The <i>composite type </i> of this <i>composite data</i> instance.
*/
- private CompositeType compositeType;
+ private final CompositeType compositeType;
/**
- * <p>
- * Constructs a <tt>CompositeDataSupport</tt> instance with the specified
+ * <p>Constructs a <tt>CompositeDataSupport</tt> instance with the specified
* <tt>compositeType</tt>, whose item values
* are specified by <tt>itemValues[]</tt>, in the same order as in
* <tt>itemNames[]</tt>.
@@ -79,41 +80,124 @@
* The items contained in this <tt>CompositeDataSupport</tt> instance are
* internally stored in a <tt>TreeMap</tt>,
* thus sorted in ascending lexicographic order of their names, for faster
- * retrieval of individual item values.
- * <p>
- * The constructor checks that all the constraints listed below for each
+ * retrieval of individual item values.</p>
+ *
+ * <p>The constructor checks that all the constraints listed below for each
* parameter are satisfied,
- * and throws the appropriate exception if they are not.
- * <p>
- * @param compositeType the <i>composite type </i> of this <i>composite
- * data</i> instance;
- * must not be null.
- * <p>
- * @param itemNames <tt>itemNames</tt> must list, in any order, all the
- * item names defined in <tt>compositeType</tt>;
- * the order in which the names are listed, is used to
- * match values in <tt>itemValues[]</tt>;
- * must not be null or empty.
+ * and throws the appropriate exception if they are not.</p>
+ *
+ * @param compositeType the <i>composite type </i> of this <i>composite
+ * data</i> instance; must not be null.
+ *
+ * @param itemNames <tt>itemNames</tt> must list, in any order, all the
+ * item names defined in <tt>compositeType</tt>; the order in which the
+ * names are listed, is used to match values in <tt>itemValues[]</tt>; must
+ * not be null.
+ *
+ * @param itemValues the values of the items, listed in the same order as
+ * their respective names in <tt>itemNames</tt>; each item value can be
+ * null, but if it is non-null it must be a valid value for the open type
+ * defined in <tt>compositeType</tt> for the corresponding item; must be of
+ * the same size as <tt>itemNames</tt>; must not be null.
+ *
+ * @throws IllegalArgumentException <tt>compositeType</tt> is null, or
+ * <tt>itemNames[]</tt> or <tt>itemValues[]</tt> is null or empty, or one
+ * of the elements in <tt>itemNames[]</tt> is a null or empty string, or
+ * <tt>itemNames[]</tt> and <tt>itemValues[]</tt> are not of the same size.
+ *
+ * @throws OpenDataException <tt>itemNames[]</tt> or
+ * <tt>itemValues[]</tt>'s size differs from the number of items defined in
+ * <tt>compositeType</tt>, or one of the elements in <tt>itemNames[]</tt>
+ * does not exist as an item name defined in <tt>compositeType</tt>, or one
+ * of the elements in <tt>itemValues[]</tt> is not a valid value for the
+ * corresponding item as defined in <tt>compositeType</tt>.
+ */
+ public CompositeDataSupport(
+ CompositeType compositeType, String[] itemNames, Object[] itemValues)
+ throws OpenDataException {
+ this(makeMap(itemNames, itemValues), compositeType);
+ }
+
+ private static SortedMap<String, Object> makeMap(
+ String[] itemNames, Object[] itemValues)
+ throws OpenDataException {
+
+ if (itemNames == null || itemValues == null)
+ throw new IllegalArgumentException("Null itemNames or itemValues");
+ if (itemNames.length != itemValues.length) {
+ throw new IllegalArgumentException(
+ "Different lengths: itemNames[" + itemNames.length +
+ "], itemValues[" + itemValues.length + "]");
+ }
+
+ SortedMap<String, Object> map = new TreeMap<String, Object>();
+ for (int i = 0; i < itemNames.length; i++) {
+ String name = itemNames[i];
+ if (name == null || name.equals(""))
+ throw new IllegalArgumentException("Null or empty item name");
+ if (map.containsKey(name))
+ throw new OpenDataException("Duplicate item name " + name);
+ map.put(itemNames[i], itemValues[i]);
+ }
+
+ return map;
+ }
+
+ /**
* <p>
- * @param itemValues the values of the items, listed in the same order as
- * their respective names in <tt>itemNames</tt>;
- * each item value can be null, but if it is non-null it must be
- * a valid value for the open type defined in <tt>compositeType</tt> for the corresponding item;
- * must be of the same size as <tt>itemNames</tt>; must not be null or empty.
- * <p>
- * @throws IllegalArgumentException <tt>compositeType</tt> is null, or <tt>itemNames[]</tt> or <tt>itemValues[]</tt> is null or empty,
- * or one of the elements in <tt>itemNames[]</tt> is a null or empty string,
- * or <tt>itemNames[]</tt> and <tt>itemValues[]</tt> are not of the same size.
- * <p>
- * @throws OpenDataException <tt>itemNames[]</tt> or <tt>itemValues[]</tt>'s size differs from
- * the number of items defined in <tt>compositeType</tt>,
- * or one of the elements in <tt>itemNames[]</tt> does not exist as an item name defined in <tt>compositeType</tt>,
- * or one of the elements in <tt>itemValues[]</tt> is not a valid value for the corresponding item
- * as defined in <tt>compositeType</tt>.
- * <p>
+ * Constructs a <tt>CompositeDataSupport</tt> instance with the specified <tt>compositeType</tt>, whose item names and corresponding values
+ * are given by the mappings in the map <tt>items</tt>.
+ * This constructor converts the keys to a string array and the values to an object array and calls
+ * <tt>CompositeDataSupport(javax.management.openmbean.CompositeType, java.lang.String[], java.lang.Object[])</tt>.
+ *
+ * @param compositeType the <i>composite type </i> of this <i>composite data</i> instance;
+ * must not be null.
+ * @param items the mappings of all the item names to their values;
+ * <tt>items</tt> must contain all the item names defined in <tt>compositeType</tt>;
+ * must not be null.
+ *
+ * @throws IllegalArgumentException <tt>compositeType</tt> is null, or
+ * <tt>items</tt> is null, or one of the keys in <tt>items</tt> is a null
+ * or empty string.
+ * @throws OpenDataException <tt>items</tt>' size differs from the
+ * number of items defined in <tt>compositeType</tt>, or one of the
+ * keys in <tt>items</tt> does not exist as an item name defined in
+ * <tt>compositeType</tt>, or one of the values in <tt>items</tt>
+ * is not a valid value for the corresponding item as defined in
+ * <tt>compositeType</tt>.
+ * @throws ArrayStoreException one or more keys in <tt>items</tt> is not of
+ * the class <tt>java.lang.String</tt>.
+ *
+ * @see #toMap
*/
- public CompositeDataSupport(CompositeType compositeType, String[] itemNames, Object[] itemValues)
- throws OpenDataException {
+ public CompositeDataSupport(CompositeType compositeType,
+ Map<String,?> items)
+ throws OpenDataException {
+ this(makeMap(items), compositeType);
+ }
+
+ private static SortedMap<String, Object> makeMap(Map<String, ?> items) {
+ if (items == null)
+ throw new IllegalArgumentException("Null items map");
+ if (items.containsKey(null) || items.containsKey(""))
+ throw new IllegalArgumentException("Null or empty item name");
+
+ SortedMap<String, Object> map = new TreeMap<String, Object>();
+ for (Object key : items.keySet()) {
+ if (!(key instanceof String)) {
+ throw new ArrayStoreException("Item name is not string: " + key);
+ // This can happen because of erasure. The particular
+ // exception is a historical artifact - an implementation
+ // detail that leaked into the API.
+ }
+ map.put((String) key, items.get(key));
+ }
+ return map;
+ }
+
+ private CompositeDataSupport(
+ SortedMap<String, Object> items, CompositeType compositeType)
+ throws OpenDataException {
// Check compositeType is not null
//
@@ -122,126 +206,42 @@
}
// item names defined in compositeType:
- Set<String> namesSet = compositeType.keySet();
-
- // Check the array itemNames is not null or empty (length!=0) and
- // that there is no null element or empty string in it
- //
- checkForNullElement(itemNames, "itemNames");
- checkForEmptyString(itemNames, "itemNames");
+ Set<String> namesFromType = compositeType.keySet();
+ Set<String> namesFromItems = items.keySet();
- // Check the array itemValues is not null or empty (length!=0)
- // (NOTE: we allow null values as array elements)
- //
- if ( (itemValues == null) || (itemValues.length == 0) ) {
- throw new IllegalArgumentException("Argument itemValues[] cannot be null or empty.");
- }
-
- // Check that the sizes of the 2 arrays itemNames and itemValues are the same
- //
- if (itemNames.length != itemValues.length) {
- throw new IllegalArgumentException("Array arguments itemNames[] and itemValues[] "+
- "should be of same length (got "+ itemNames.length +
- " and "+ itemValues.length +").");
+ // This is just a comparison, but we do it this way for a better
+ // exception message.
+ if (!namesFromType.equals(namesFromItems)) {
+ Set<String> extraFromType = new TreeSet<String>(namesFromType);
+ extraFromType.removeAll(namesFromItems);
+ Set<String> extraFromItems = new TreeSet<String>(namesFromItems);
+ extraFromItems.removeAll(namesFromType);
+ if (!extraFromType.isEmpty() || !extraFromItems.isEmpty()) {
+ throw new OpenDataException(
+ "Item names do not match CompositeType: " +
+ "names in items but not in CompositeType: " + extraFromItems +
+ "; names in CompositeType but not in items: " + extraFromType);
+ }
}
- // Check the size of the 2 arrays is equal to the number of items defined in compositeType
- //
- if (itemNames.length != namesSet.size()) {
- throw new OpenDataException("The size of array arguments itemNames[] and itemValues[] should be equal to the number of items defined"+
- " in argument compositeType (found "+ itemNames.length +" elements in itemNames[] and itemValues[],"+
- " expecting "+ namesSet.size() +" elements according to compositeType.");
- }
-
- // Check parameter itemNames[] contains all names defined in the compositeType of this instance
- //
- if ( ! Arrays.asList(itemNames).containsAll(namesSet) ) {
- throw new OpenDataException("Argument itemNames[] does not contain all names defined in the compositeType of this instance.");
- }
-
- // Check each element of itemValues[], if not null, is of the open type defined for the corresponding item
- //
- OpenType<?> itemType;
- for (int i=0; i<itemValues.length; i++) {
- itemType = compositeType.getType(itemNames[i]);
- if ( (itemValues[i] != null) && (! itemType.isValue(itemValues[i])) ) {
- throw new OpenDataException("Argument's element itemValues["+ i +"]=\""+ itemValues[i] +"\" is not a valid value for"+
- " this item (itemName="+ itemNames[i] +",itemType="+ itemType +").");
+ // Check each value, if not null, is of the open type defined for the
+ // corresponding item
+ for (String name : namesFromType) {
+ Object value = items.get(name);
+ if (value != null) {
+ OpenType<?> itemType = compositeType.getType(name);
+ if (!itemType.isValue(value)) {
+ throw new OpenDataException(
+ "Argument value of wrong type for item " + name +
+ ": value " + value + ", type " + itemType);
+ }
}
}
// Initialize internal fields: compositeType and contents
//
this.compositeType = compositeType;
- for (int i=0; i<itemNames.length; i++) {
- this.contents.put(itemNames[i], itemValues[i]);
- }
- }
-
- /**
- * <p>
- * Constructs a <tt>CompositeDataSupport</tt> instance with the specified <tt>compositeType</tt>, whose item names and corresponding values
- * are given by the mappings in the map <tt>items</tt>.
- * This constructor converts the keys to a string array and the values to an object array and calls
- * <tt>CompositeDataSupport(javax.management.openmbean.CompositeType, java.lang.String[], java.lang.Object[])</tt>.
- * <p>
- * @param compositeType the <i>composite type </i> of this <i>composite data</i> instance;
- * must not be null.
- * <p>
- * @param items the mappings of all the item names to their values;
- * <tt>items</tt> must contain all the item names defined in <tt>compositeType</tt>;
- * must not be null or empty.
- * <p>
- * @throws IllegalArgumentException <tt>compositeType</tt> is null, or <tt>items</tt> is null or empty,
- * or one of the keys in <tt>items</tt> is a null or empty string,
- * or one of the values in <tt>items</tt> is null.
- * <p>
- * @throws OpenDataException <tt>items</tt>' size differs from the number of items defined in <tt>compositeType</tt>,
- * or one of the keys in <tt>items</tt> does not exist as an item name defined in <tt>compositeType</tt>,
- * or one of the values in <tt>items</tt> is not a valid value for the corresponding item
- * as defined in <tt>compositeType</tt>.
- * <p>
- * @throws ArrayStoreException one or more keys in <tt>items</tt> is not of the class <tt>java.lang.String</tt>.
- * <p>
- */
- public CompositeDataSupport(CompositeType compositeType,
- Map<String,?> items)
- throws OpenDataException {
-
-
- // Let the other constructor do the job, as the call to another constructor must be the first call
- //
- this( compositeType,
- (items==null ? null : items.keySet().toArray(new String[items.size()])), // may raise an ArrayStoreException
- (items==null ? null : items.values().toArray()) );
- }
-
- /**
- *
- */
- private static void checkForNullElement(Object[] arg, String argName) {
- if ( (arg == null) || (arg.length == 0) ) {
- throw new IllegalArgumentException(
- "Argument "+ argName +"[] cannot be null or empty.");
- }
- for (int i=0; i<arg.length; i++) {
- if (arg[i] == null) {
- throw new IllegalArgumentException(
- "Argument's element "+ argName +"["+ i +"] cannot be null.");
- }
- }
- }
-
- /**
- *
- */
- private static void checkForEmptyString(String[] arg, String argName) {
- for (int i=0; i<arg.length; i++) {
- if (arg[i].trim().equals("")) {
- throw new IllegalArgumentException(
- "Argument's element "+ argName +"["+ i +"] cannot be an empty string.");
- }
- }
+ this.contents = items;
}
/**
@@ -329,6 +329,54 @@
}
/**
+ * <p>Returns a Map representing the contents of the given CompositeData.
+ * Each item in the CompositeData is represented by an entry in the map,
+ * where the name and value of the item are the key and value of the entry.
+ * The returned value is modifiable but modifications to it have no effect
+ * on the original CompositeData.</p>
+ *
+ * <p>For example, if you have a CompositeData {@code cd1} and you want
+ * to produce another CompositeData {@code cd2} which is the same except
+ * that the value of its {@code id} item has been changed to 253, you
+ * could write:</p>
+ *
+ * <pre>
+ * CompositeData cd1 = ...;
+ * {@code Map<String, Object>} map = CompositeDataSupport.toMap(cd1);
+ * assert(map.get("id") instanceof Integer);
+ * map.put("id", 253);
+ * CompositeData cd2 = {@link #CompositeDataSupport(CompositeType, Map)
+ * new CompositeDataSupport}(cd1.getCompositeType(), map);
+ * </pre>
+ *
+ * <p>Logically, this method would be a method in the {@link CompositeData}
+ * interface, but cannot be for compatibility reasons.</p>
+ *
+ * @param cd the CompositeData to convert to a Map.
+ *
+ * @return a Map that is a copy of the contents of {@code cd}.
+ *
+ * @throws IllegalArgumentException if {@code cd} is null.
+ *
+ * @see #CompositeDataSupport(CompositeType, Map)
+ */
+ public static Map<String, Object> toMap(CompositeData cd) {
+ if (cd == null)
+ throw new IllegalArgumentException("Null argument");
+
+ // If we really wanted, we could check whether cd is a
+ // CompositeDataSupport and return a copy of cd.contents if so,
+ // but I don't think that would be substantially faster.
+ Map<String, Object> map = new LinkedHashMap<String, Object>();
+ CompositeType ct = cd.getCompositeType();
+ for (String key : ct.keySet()) {
+ Object value = cd.get(key);
+ map.put(key, value);
+ }
+ return map;
+ }
+
+ /**
* Compares the specified <var>obj</var> parameter with this
* <code>CompositeDataSupport</code> instance for equality.
* <p>
--- a/jdk/src/share/classes/javax/management/package.html Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/javax/management/package.html Thu Nov 13 11:15:01 2008 -0800
@@ -1,7 +1,7 @@
<html>
-<head>
-<title>javax.management package</title>
-<!--
+ <head>
+ <title>javax.management package</title>
+ <!--
Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -24,37 +24,37 @@
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.
--->
-</head>
-<body bgcolor="white">
- <p>Provides the core classes for the Java Management Extensions.</p>
+ -->
+ </head>
+ <body bgcolor="white">
+ <p>Provides the core classes for the Java Management Extensions.</p>
- <p>The Java Management Extensions
- (JMX<sup><font size="-1">TM</font></sup>) API is a standard
- API for management and monitoring. Typical uses include:</p>
+ <p>The Java Management Extensions
+ (JMX<sup><font size="-1">TM</font></sup>) API is a standard
+ API for management and monitoring. Typical uses include:</p>
- <ul>
- <li>consulting and changing application configuration</li>
+ <ul>
+ <li>consulting and changing application configuration</li>
- <li>accumulating statistics about application behavior and
- making them available</li>
+ <li>accumulating statistics about application behavior and
+ making them available</li>
- <li>notifying of state changes and erroneous conditions.</li>
- </ul>
+ <li>notifying of state changes and erroneous conditions.</li>
+ </ul>
- <p>The JMX API can also be used as part of a solution for
- managing systems, networks, and so on.</p>
+ <p>The JMX API can also be used as part of a solution for
+ managing systems, networks, and so on.</p>
- <p>The API includes remote access, so a remote management
- program can interact with a running application for these
- purposes.</p>
+ <p>The API includes remote access, so a remote management
+ program can interact with a running application for these
+ purposes.</p>
- <h2>MBeans</h2>
+ <h2>MBeans</h2>
- <p>The fundamental notion of the JMX API is the <em>MBean</em>.
- An MBean is a named <em>managed object</em> representing a
- resource. It has a <em>management interface</em> consisting
- of:</p>
+ <p>The fundamental notion of the JMX API is the <em>MBean</em>.
+ An MBean is a named <em>managed object</em> representing a
+ resource. It has a <em>management interface</em> consisting
+ of:</p>
<ul>
<li>named and typed attributes that can be read and/or
@@ -92,40 +92,40 @@
<pre>
public interface ConfigurationMBean {
- public int getCacheSize();
- public void setCacheSize(int size);
- public long getLastChangedTime();
- public void save();
+ public int getCacheSize();
+ public void setCacheSize(int size);
+ public long getLastChangedTime();
+ public void save();
}
- </pre>
+ </pre>
- <p>The methods <code>getCacheSize</code> and
- <code>setCacheSize</code> define a read-write attribute of
- type <code>int</code> called <code>CacheSize</code> (with an
- initial capital, unlike the JavaBeans convention).</p>
+ <p>The methods <code>getCacheSize</code> and
+ <code>setCacheSize</code> define a read-write attribute of
+ type <code>int</code> called <code>CacheSize</code> (with an
+ initial capital, unlike the JavaBeans convention).</p>
- <p>The method <code>getLastChangedTime</code> defines an
- attribute of type <code>long</code> called
- <code>LastChangedTime</code>. This is a read-only attribute,
- since there is no method <code>setLastChangedTime</code>.</p>
+ <p>The method <code>getLastChangedTime</code> defines an
+ attribute of type <code>long</code> called
+ <code>LastChangedTime</code>. This is a read-only attribute,
+ since there is no method <code>setLastChangedTime</code>.</p>
- <p>The method <code>save</code> defines an operation called
- <code>save</code>. It is not an attribute, since its name
- does not begin with <code>get</code>, <code>set</code>, or
- <code>is</code>.</p>
+ <p>The method <code>save</code> defines an operation called
+ <code>save</code>. It is not an attribute, since its name
+ does not begin with <code>get</code>, <code>set</code>, or
+ <code>is</code>.</p>
- <p>The exact naming patterns for Standard MBeans are detailed in
- the <a href="#spec">JMX Specification</a>.</p>
+ <p>The exact naming patterns for Standard MBeans are detailed in
+ the <a href="#spec">JMX Specification</a>.</p>
- <p>There are two ways to make a Java object that is an MBean
- with this management interface. One is for the object to be
- of a class that has exactly the same name as the Java
- interface but without the <code>MBean</code> suffix. So in
- the example the object would be of the class
- <code>Configuration</code>, in the same Java package as
- <code>ConfigurationMBean</code>. The second way is to use the
- {@link javax.management.StandardMBean StandardMBean}
- class.</p>
+ <p>There are two ways to make a Java object that is an MBean
+ with this management interface. One is for the object to be
+ of a class that has exactly the same name as the Java
+ interface but without the <code>MBean</code> suffix. So in
+ the example the object would be of the class
+ <code>Configuration</code>, in the same Java package as
+ <code>ConfigurationMBean</code>. The second way is to use the
+ {@link javax.management.StandardMBean StandardMBean}
+ class.</p>
<h3 id="stdannot">Defining Standard MBeans with annotations</h3>
@@ -272,37 +272,37 @@
<pre>
int cacheSize = mbs.getAttribute(name, "CacheSize");
{@link javax.management.Attribute Attribute} newCacheSize =
- new Attribute("CacheSize", new Integer(2000));
+ new Attribute("CacheSize", new Integer(2000));
mbs.setAttribute(name, newCacheSize);
mbs.invoke(name, "save", new Object[0], new Class[0]);
- </pre>
+ </pre>
<p id="proxy">Alternatively, if you have a Java interface that
corresponds to the management interface for the MBean, you can use an
<em>MBean proxy</em> like this:</p>
- <pre>
+ <pre>
ConfigurationMBean conf =
{@link javax.management.JMX#newMBeanProxy
JMX.newMBeanProxy}(mbs, name, ConfigurationMBean.class);
int cacheSize = conf.getCacheSize();
conf.setCacheSize(2000);
conf.save();
- </pre>
+ </pre>
- <p>Using an MBean proxy is just a convenience. The second
- example ends up calling the same <code>MBeanServer</code>
- operations as the first one.</p>
+ <p>Using an MBean proxy is just a convenience. The second
+ example ends up calling the same <code>MBeanServer</code>
+ operations as the first one.</p>
- <p>An MBean Server can be queried for MBeans whose names match
- certain patterns and/or whose attributes meet certain
- constraints. Name patterns are constructed using the {@link
- javax.management.ObjectName ObjectName} class and constraints
- are constructed using the {@link javax.management.Query Query}
- class. The methods {@link
- javax.management.MBeanServer#queryNames queryNames} and {@link
- javax.management.MBeanServer#queryMBeans queryMBeans} then
- perform the query.</p>
+ <p>An MBean Server can be queried for MBeans whose names match
+ certain patterns and/or whose attributes meet certain
+ constraints. Name patterns are constructed using the {@link
+ javax.management.ObjectName ObjectName} class and constraints
+ are constructed using the {@link javax.management.Query Query}
+ class. The methods {@link
+ javax.management.MBeanServer#queryNames queryNames} and {@link
+ javax.management.MBeanServer#queryMBeans queryMBeans} then
+ perform the query.</p>
<h3>MBean lifecycle and resource injection</h3>
@@ -407,6 +407,92 @@
So for example an SNMP GET operation might result in a
<code>getAttribute</code> on the MBean Server.</p>
+ <h3 id="interop">Interoperability between versions of the JMX
+ specification</h3>
+
+ <p>When a client connects to a server using the JMX Remote
+ API, it is possible that they do not have the same version
+ of the JMX specification. The version of the JMX
+ specification described here is version 2.0. Previous
+ versions were 1.0, 1.1, 1.2, and 1.4. (There was no 1.3.)
+ The standard JMX Remote API is defined to work with version
+ 1.2 onwards, so in standards-based deployment the only
+ interoperability questions that arise concern version 1.2
+ onwards.</p>
+
+ <p>Every version of the JMX specification continues to
+ implement the features of previous versions. So when the
+ client is running an earlier version than the server, there
+ should not be any interoperability concerns. The only
+ exception is the unlikely one where a pre-2.0 client used
+ the string {@code //} in the domain part of an {@link
+ javax.management.ObjectName ObjectName}.</p>
+
+ <p>When the client is running a later version than the server,
+ certain newer features may not be available, as detailed in
+ the next sections. The method {@link
+ javax.management.JMX#getSpecificationVersion
+ JMX.getSpecificationVersion} can be used to determine the
+ server version to check if required features are
+ available.</p>
+
+ <h4 id="interop-1.4">If the remote MBean Server is 1.4</h4>
+
+ <ul>
+
+ <li><p>You cannot use {@link
+ javax.management.QueryNotificationFilter
+ QueryNotificationFilter} in {@link
+ javax.management.MBeanServerConnection#addNotificationListener
+ addNotificationListener} since this class did not exist
+ in 1.4.</p>
+
+ <li><p>In an attribute in a query, you cannot access values
+ inside complex types using dot syntax, for example
+ {@link javax.management.Query#attr Query.attr}{@code
+ ("HeapMemoryUsage.used")}.</p>
+
+ <li><p>The packages {@link javax.management.event} and
+ {@link javax.management.namespace} did not exist in 1.4,
+ so you cannot remotely create instances of the MBeans
+ they define.</p>
+
+ <li><p>Even if the remote MBean Server is 2.0, you cannot in
+ general suppose that {@link
+ javax.management.event.EventClient EventClient} or
+ {@link javax.management.ClientContext ClientContext}
+ will work there without first checking. If the remote
+ MBean Server is 1.4 then those checks will return false.
+ An attempt to use these features without checking will
+ fail in the same way as for a remote 2.0 that is not
+ configured to support them.</p>
+ </ul>
+
+ <h4 id="interop-1.2">If the remote MBean Server is 1.2</h4>
+
+ <p><b>In addition to the above</b>,</p>
+
+ <ul>
+
+ <li><p>You cannot use wildcards in a key property of an
+ {@link javax.management.ObjectName ObjectName}, for
+ example {@code domain:type=Foo,name=*}. Wildcards that
+ match whole properties are still allowed, for example
+ {@code *:*} or {@code *:type=Foo,*}.</p>
+
+ <li><p>You cannot use {@link
+ javax.management.Query#isInstanceOf Query.isInstanceOf}
+ in a query.</p>
+
+ <li><p>You cannot use dot syntax such as {@code
+ HeapMemoryUsage.used} in the {@linkplain
+ javax.management.monitor.Monitor#setObservedAttribute
+ observed attribute} of a monitor, as described in the
+ documentation for the {@link javax.management.monitor}
+ package.</p>
+
+ </ul>
+
<p id="spec">
@see <a href="{@docRoot}/../technotes/guides/jmx/index.html">
Java SE 6 Platform documentation on JMX technology</a>
--- a/jdk/src/share/classes/javax/management/remote/JMXConnectorFactory.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/javax/management/remote/JMXConnectorFactory.java Thu Nov 13 11:15:01 2008 -0800
@@ -322,10 +322,12 @@
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 @@
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 @@
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 @@
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 @@
private static String protocol2package(String protocol) {
return protocol.replace('+', '.').replace('-', '_');
}
+
}
--- a/jdk/src/share/classes/javax/management/remote/JMXConnectorServer.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/javax/management/remote/JMXConnectorServer.java Thu Nov 13 11:15:01 2008 -0800
@@ -33,6 +33,7 @@
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;
@@ -103,6 +104,56 @@
/**
* <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}
* MBean. The value associated with this attribute, if any, must
* be a string that is equal to {@code "true"} or {@code "false"},
@@ -155,7 +206,7 @@
* 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 @@
* 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 @@
* 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 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>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>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 @@
* 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
- */
- 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>
+ * @throws UnsupportedOperationException if {@link
+ * #supportsSystemMBeanServerForwarder} returns false.
*
- * <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>
+ * @see #supportsSystemMBeanServerForwarder
+ * @see #setMBeanServerForwarder
*
- * <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()
+ * @since 1.7
*/
- 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 @@
*
* @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 @@
* <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 @@
*
* @return the array of possible notifications.
*/
+ @Override
public MBeanNotificationInfo[] getNotificationInfo() {
final String[] types = {
JMXConnectionNotification.OPENED,
@@ -684,30 +764,29 @@
* 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 @@
* 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 @@
private MBeanServer userMBeanServer;
- private MBeanServer systemMBeanServer;
+ private final MBeanServerForwarder systemMBeanServerForwarder =
+ new IdentityMBeanServerForwarder();
/**
* The name used to registered this server in an MBeanServer.
--- a/jdk/src/share/classes/javax/management/remote/JMXConnectorServerMBean.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/javax/management/remote/JMXConnectorServerMBean.java Thu Nov 13 11:15:01 2008 -0800
@@ -132,7 +132,7 @@
*
* <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 @@
* 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);
--- a/jdk/src/share/classes/javax/management/remote/rmi/RMIConnector.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/javax/management/remote/rmi/RMIConnector.java Thu Nov 13 11:15:01 2008 -0800
@@ -134,14 +134,14 @@
public class RMIConnector implements JMXConnector, Serializable, JMXAddressable {
private static final ClassLogger logger =
- new ClassLogger("javax.management.remote.rmi", "RMIConnector");
+ new ClassLogger("javax.management.remote.rmi", "RMIConnector");
private static final long serialVersionUID = 817323035842634473L;
private RMIConnector(RMIServer rmiServer, JMXServiceURL address,
- Map<String, ?> environment) {
+ Map<String, ?> environment) {
if (rmiServer == null && address == null) throw new
- IllegalArgumentException("rmiServer and jmxServiceURL both null");
+ IllegalArgumentException("rmiServer and jmxServiceURL both null");
initTransients();
@@ -257,7 +257,7 @@
}
public synchronized void connect(Map<String,?> environment)
- throws IOException {
+ throws IOException {
final boolean tracing = logger.traceOn();
String idstr = (tracing?"["+this.toString()+"]":null);
@@ -274,8 +274,8 @@
if (tracing) logger.trace("connect",idstr + " connecting...");
final Map<String, Object> usemap =
- new HashMap<String, Object>((this.env==null) ?
- Collections.<String, Object>emptyMap() : this.env);
+ new HashMap<String, Object>((this.env==null) ?
+ Collections.<String, Object>emptyMap() : this.env);
if (environment != null) {
@@ -315,7 +315,7 @@
defaultClassLoader = EnvHelp.resolveClientClassLoader(usemap);
usemap.put(JMXConnectorFactory.DEFAULT_CLASS_LOADER,
- defaultClassLoader);
+ defaultClassLoader);
rmiNotifClient = new RMINotifClient(defaultClassLoader, usemap);
@@ -333,12 +333,12 @@
eventServiceEnabled = EnvHelp.eventServiceEnabled(env);
Notification connectedNotif =
- new JMXConnectionNotification(JMXConnectionNotification.OPENED,
- this,
- connectionId,
- clientNotifSeqNo++,
- "Successful connection",
- null);
+ new JMXConnectionNotification(JMXConnectionNotification.OPENED,
+ this,
+ connectionId,
+ clientNotifSeqNo++,
+ "Successful connection",
+ null);
sendNotification(connectedNotif);
// whether or not event service
@@ -363,7 +363,7 @@
if (terminated || !connected) {
if (logger.traceOn())
logger.trace("getConnectionId","["+this.toString()+
- "] not connected.");
+ "] not connected.");
throw new IOException("Not connected");
}
@@ -374,23 +374,23 @@
}
public synchronized MBeanServerConnection getMBeanServerConnection()
- throws IOException {
+ throws IOException {
return getMBeanServerConnection(null);
}
public synchronized MBeanServerConnection
- getMBeanServerConnection(Subject delegationSubject)
+ getMBeanServerConnection(Subject delegationSubject)
throws IOException {
if (terminated) {
if (logger.traceOn())
logger.trace("getMBeanServerConnection","[" + this.toString() +
- "] already closed.");
+ "] already closed.");
throw new IOException("Connection closed");
} else if (!connected) {
if (logger.traceOn())
logger.trace("getMBeanServerConnection","[" + this.toString() +
- "] is not connected.");
+ "] is not connected.");
throw new IOException("Not connected");
}
@@ -405,7 +405,7 @@
rmbsc, EventClientDelegateMBean.OBJECT_NAME,
EventClientDelegateMBean.class);
EventClient ec = new EventClient(ecd, null, defaultExecutor(), null,
- EventClient.DEFAULT_LEASE_TIMEOUT);
+ EventClient.DEFAULT_REQUESTED_LEASE_TIME);
rmbsc = EventConnection.Factory.make(rmbsc, ec);
ec.addEventClientListener(
@@ -433,17 +433,17 @@
}
public void
- addConnectionNotificationListener(NotificationListener listener,
- NotificationFilter filter,
- Object handback) {
+ addConnectionNotificationListener(NotificationListener listener,
+ NotificationFilter filter,
+ Object handback) {
if (listener == null)
throw new NullPointerException("listener");
connectionBroadcaster.addNotificationListener(listener, filter,
- handback);
+ handback);
}
public void
- removeConnectionNotificationListener(NotificationListener listener)
+ removeConnectionNotificationListener(NotificationListener listener)
throws ListenerNotFoundException {
if (listener == null)
throw new NullPointerException("listener");
@@ -451,14 +451,14 @@
}
public void
- removeConnectionNotificationListener(NotificationListener listener,
- NotificationFilter filter,
- Object handback)
+ removeConnectionNotificationListener(NotificationListener listener,
+ NotificationFilter filter,
+ Object handback)
throws ListenerNotFoundException {
if (listener == null)
throw new NullPointerException("listener");
connectionBroadcaster.removeNotificationListener(listener, filter,
- handback);
+ handback);
}
private void sendNotification(Notification n) {
@@ -526,11 +526,11 @@
try {
rmiNotifClient.terminate();
if (tracing) logger.trace("close",idstr +
- " RMI Notification client terminated.");
+ " RMI Notification client terminated.");
} catch (RuntimeException x) {
closeException = x;
if (tracing) logger.trace("close",idstr +
- " Failed to terminate RMI Notification client: " + x);
+ " Failed to terminate RMI Notification client: " + x);
if (debug) logger.debug("close",x);
}
}
@@ -544,7 +544,7 @@
} catch (IOException e) {
closeException = e;
if (tracing) logger.trace("close",idstr +
- " Failed to close RMIServer: " + e);
+ " Failed to close RMIServer: " + e);
if (debug) logger.debug("close",e);
}
}
@@ -559,12 +559,12 @@
if (savedConnectionId != null) {
Notification closedNotif =
- new JMXConnectionNotification(JMXConnectionNotification.CLOSED,
- this,
- savedConnectionId,
- clientNotifSeqNo++,
- "Client has been closed",
- null);
+ new JMXConnectionNotification(JMXConnectionNotification.CLOSED,
+ this,
+ savedConnectionId,
+ clientNotifSeqNo++,
+ "Client has been closed",
+ null);
sendNotification(closedNotif);
}
@@ -572,13 +572,13 @@
//
if (closeException != null) {
if (tracing) logger.trace("close",idstr + " failed to close: " +
- closeException);
+ closeException);
if (closeException instanceof IOException)
throw (IOException) closeException;
if (closeException instanceof RuntimeException)
throw (RuntimeException) closeException;
final IOException x =
- new IOException("Failed to close: " + closeException);
+ new IOException("Failed to close: " + closeException);
throw EnvHelp.initCause(x,closeException);
}
}
@@ -593,7 +593,7 @@
final boolean debug = logger.debugOn();
if (debug)
logger.debug("addListenerWithSubject",
- "(ObjectName,MarshalledObject,Subject)");
+ "(ObjectName,MarshalledObject,Subject)");
final ObjectName[] names = new ObjectName[] {name};
final MarshalledObject<NotificationFilter>[] filters =
@@ -603,11 +603,11 @@
};
final Integer[] listenerIDs =
- addListenersWithSubjects(names,filters,delegationSubjects,
- reconnect);
+ addListenersWithSubjects(names,filters,delegationSubjects,
+ reconnect);
if (debug) logger.debug("addListenerWithSubject","listenerID="
- + listenerIDs[0]);
+ + listenerIDs[0]);
return listenerIDs[0];
}
@@ -620,24 +620,24 @@
final boolean debug = logger.debugOn();
if (debug)
- logger.debug("addListenersWithSubjects",
- "(ObjectName[],MarshalledObject[],Subject[])");
+ logger.debug("addListenersWithSubjects",
+ "(ObjectName[],MarshalledObject[],Subject[])");
final ClassLoader old = pushDefaultClassLoader();
Integer[] listenerIDs = null;
try {
listenerIDs = connection.addNotificationListeners(names,
- filters,
- delegationSubjects);
+ filters,
+ delegationSubjects);
} catch (NoSuchObjectException noe) {
// maybe reconnect
if (reconnect) {
communicatorAdmin.gotIOException(noe);
listenerIDs = connection.addNotificationListeners(names,
- filters,
- delegationSubjects);
+ filters,
+ delegationSubjects);
} else {
throw noe;
}
@@ -671,65 +671,65 @@
}
public ObjectInstance createMBean(String className,
- ObjectName name)
+ ObjectName name)
throws ReflectionException,
- InstanceAlreadyExistsException,
- MBeanRegistrationException,
- MBeanException,
- NotCompliantMBeanException,
- IOException {
+ InstanceAlreadyExistsException,
+ MBeanRegistrationException,
+ MBeanException,
+ NotCompliantMBeanException,
+ IOException {
if (logger.debugOn())
logger.debug("createMBean(String,ObjectName)",
- "className=" + className + ", name=" +
- name);
+ "className=" + className + ", name=" +
+ name);
final ClassLoader old = pushDefaultClassLoader();
try {
return connection.createMBean(className,
- name,
- delegationSubject);
+ name,
+ delegationSubject);
} catch (IOException ioe) {
communicatorAdmin.gotIOException(ioe);
return connection.createMBean(className,
- name,
- delegationSubject);
+ name,
+ delegationSubject);
} finally {
popDefaultClassLoader(old);
}
}
public ObjectInstance createMBean(String className,
- ObjectName name,
- ObjectName loaderName)
- throws ReflectionException,
- InstanceAlreadyExistsException,
- MBeanRegistrationException,
- MBeanException,
- NotCompliantMBeanException,
- InstanceNotFoundException,
- IOException {
+ ObjectName name,
+ ObjectName loaderName)
+ throws ReflectionException,
+ InstanceAlreadyExistsException,
+ MBeanRegistrationException,
+ MBeanException,
+ NotCompliantMBeanException,
+ InstanceNotFoundException,
+ IOException {
if (logger.debugOn())
logger.debug("createMBean(String,ObjectName,ObjectName)",
- "className=" + className + ", name="
- + name + ", loaderName="
- + loaderName + ")");
+ "className=" + className + ", name="
+ + name + ", loaderName="
+ + loaderName + ")");
final ClassLoader old = pushDefaultClassLoader();
try {
return connection.createMBean(className,
- name,
- loaderName,
- delegationSubject);
+ name,
+ loaderName,
+ delegationSubject);
} catch (IOException ioe) {
communicatorAdmin.gotIOException(ioe);
return connection.createMBean(className,
- name,
- loaderName,
- delegationSubject);
+ name,
+ loaderName,
+ delegationSubject);
} finally {
popDefaultClassLoader(old);
@@ -737,90 +737,90 @@
}
public ObjectInstance createMBean(String className,
- ObjectName name,
- Object params[],
- String signature[])
+ ObjectName name,
+ Object params[],
+ String signature[])
throws ReflectionException,
- InstanceAlreadyExistsException,
- MBeanRegistrationException,
- MBeanException,
- NotCompliantMBeanException,
- IOException {
+ InstanceAlreadyExistsException,
+ MBeanRegistrationException,
+ MBeanException,
+ NotCompliantMBeanException,
+ IOException {
if (logger.debugOn())
- logger.debug("createMBean(String,ObjectName,Object[],String[])",
- "className=" + className + ", name="
- + name + ", params="
- + objects(params) + ", signature="
- + strings(signature));
+ logger.debug("createMBean(String,ObjectName,Object[],String[])",
+ "className=" + className + ", name="
+ + name + ", params="
+ + objects(params) + ", signature="
+ + strings(signature));
final MarshalledObject<Object[]> sParams =
new MarshalledObject<Object[]>(params);
final ClassLoader old = pushDefaultClassLoader();
try {
return connection.createMBean(className,
- name,
- sParams,
- signature,
- delegationSubject);
+ name,
+ sParams,
+ signature,
+ delegationSubject);
} catch (IOException ioe) {
communicatorAdmin.gotIOException(ioe);
return connection.createMBean(className,
- name,
- sParams,
- signature,
- delegationSubject);
+ name,
+ sParams,
+ signature,
+ delegationSubject);
} finally {
popDefaultClassLoader(old);
}
}
public ObjectInstance createMBean(String className,
- ObjectName name,
- ObjectName loaderName,
- Object params[],
- String signature[])
+ ObjectName name,
+ ObjectName loaderName,
+ Object params[],
+ String signature[])
throws ReflectionException,
- InstanceAlreadyExistsException,
- MBeanRegistrationException,
- MBeanException,
- NotCompliantMBeanException,
- InstanceNotFoundException,
- IOException {
+ InstanceAlreadyExistsException,
+ MBeanRegistrationException,
+ MBeanException,
+ NotCompliantMBeanException,
+ InstanceNotFoundException,
+ IOException {
if (logger.debugOn()) logger.debug(
- "createMBean(String,ObjectName,ObjectName,Object[],String[])",
- "className=" + className + ", name=" + name + ", loaderName="
- + loaderName + ", params=" + objects(params)
- + ", signature=" + strings(signature));
+ "createMBean(String,ObjectName,ObjectName,Object[],String[])",
+ "className=" + className + ", name=" + name + ", loaderName="
+ + loaderName + ", params=" + objects(params)
+ + ", signature=" + strings(signature));
final MarshalledObject<Object[]> sParams =
new MarshalledObject<Object[]>(params);
final ClassLoader old = pushDefaultClassLoader();
try {
return connection.createMBean(className,
- name,
- loaderName,
- sParams,
- signature,
- delegationSubject);
+ name,
+ loaderName,
+ sParams,
+ signature,
+ delegationSubject);
} catch (IOException ioe) {
communicatorAdmin.gotIOException(ioe);
return connection.createMBean(className,
- name,
- loaderName,
- sParams,
- signature,
- delegationSubject);
+ name,
+ loaderName,
+ sParams,
+ signature,
+ delegationSubject);
} finally {
popDefaultClassLoader(old);
}
}
public void unregisterMBean(ObjectName name)
- throws InstanceNotFoundException,
- MBeanRegistrationException,
- IOException {
+ throws InstanceNotFoundException,
+ MBeanRegistrationException,
+ IOException {
if (logger.debugOn())
logger.debug("unregisterMBean", "name=" + name);
@@ -837,8 +837,8 @@
}
public ObjectInstance getObjectInstance(ObjectName name)
- throws InstanceNotFoundException,
- IOException {
+ throws InstanceNotFoundException,
+ IOException {
if (logger.debugOn())
logger.debug("getObjectInstance", "name=" + name);
@@ -855,10 +855,10 @@
}
public Set<ObjectInstance> queryMBeans(ObjectName name,
- QueryExp query)
- throws IOException {
+ QueryExp query)
+ throws IOException {
if (logger.debugOn()) logger.debug("queryMBeans",
- "name=" + name + ", query=" + query);
+ "name=" + name + ", query=" + query);
final MarshalledObject<QueryExp> sQuery =
new MarshalledObject<QueryExp>(query);
@@ -875,10 +875,10 @@
}
public Set<ObjectName> queryNames(ObjectName name,
- QueryExp query)
+ QueryExp query)
throws IOException {
if (logger.debugOn()) logger.debug("queryNames",
- "name=" + name + ", query=" + query);
+ "name=" + name + ", query=" + query);
final MarshalledObject<QueryExp> sQuery =
new MarshalledObject<QueryExp>(query);
@@ -895,7 +895,7 @@
}
public boolean isRegistered(ObjectName name)
- throws IOException {
+ throws IOException {
if (logger.debugOn())
logger.debug("isRegistered", "name=" + name);
@@ -912,7 +912,7 @@
}
public Integer getMBeanCount()
- throws IOException {
+ throws IOException {
if (logger.debugOn()) logger.debug("getMBeanCount", "");
final ClassLoader old = pushDefaultClassLoader();
@@ -928,53 +928,53 @@
}
public Object getAttribute(ObjectName name,
- String attribute)
+ String attribute)
throws MBeanException,
- AttributeNotFoundException,
- InstanceNotFoundException,
- ReflectionException,
- IOException {
+ AttributeNotFoundException,
+ InstanceNotFoundException,
+ ReflectionException,
+ IOException {
if (logger.debugOn()) logger.debug("getAttribute",
- "name=" + name + ", attribute="
- + attribute);
+ "name=" + name + ", attribute="
+ + attribute);
final ClassLoader old = pushDefaultClassLoader();
try {
return connection.getAttribute(name,
- attribute,
- delegationSubject);
+ attribute,
+ delegationSubject);
} catch (IOException ioe) {
communicatorAdmin.gotIOException(ioe);
return connection.getAttribute(name,
- attribute,
- delegationSubject);
+ attribute,
+ delegationSubject);
} finally {
popDefaultClassLoader(old);
}
}
public AttributeList getAttributes(ObjectName name,
- String[] attributes)
+ String[] attributes)
throws InstanceNotFoundException,
- ReflectionException,
- IOException {
+ ReflectionException,
+ IOException {
if (logger.debugOn()) logger.debug("getAttributes",
- "name=" + name + ", attributes="
- + strings(attributes));
+ "name=" + name + ", attributes="
+ + strings(attributes));
final ClassLoader old = pushDefaultClassLoader();
try {
return connection.getAttributes(name,
- attributes,
- delegationSubject);
+ attributes,
+ delegationSubject);
} catch (IOException ioe) {
communicatorAdmin.gotIOException(ioe);
return connection.getAttributes(name,
- attributes,
- delegationSubject);
+ attributes,
+ delegationSubject);
} finally {
popDefaultClassLoader(old);
}
@@ -982,20 +982,20 @@
public void setAttribute(ObjectName name,
- Attribute attribute)
- throws InstanceNotFoundException,
- AttributeNotFoundException,
- InvalidAttributeValueException,
- MBeanException,
- ReflectionException,
- IOException {
+ Attribute attribute)
+ throws InstanceNotFoundException,
+ AttributeNotFoundException,
+ InvalidAttributeValueException,
+ MBeanException,
+ ReflectionException,
+ IOException {
if (logger.debugOn()) logger.debug("setAttribute",
- "name=" + name + ", attribute="
- + attribute);
+ "name=" + name + ", attribute="
+ + attribute);
final MarshalledObject<Attribute> sAttribute =
- new MarshalledObject<Attribute>(attribute);
+ new MarshalledObject<Attribute>(attribute);
final ClassLoader old = pushDefaultClassLoader();
try {
connection.setAttribute(name, sAttribute, delegationSubject);
@@ -1009,28 +1009,28 @@
}
public AttributeList setAttributes(ObjectName name,
- AttributeList attributes)
- throws InstanceNotFoundException,
- ReflectionException,
- IOException {
+ AttributeList attributes)
+ throws InstanceNotFoundException,
+ ReflectionException,
+ IOException {
if (logger.debugOn()) logger.debug("setAttributes",
- "name=" + name + ", attributes="
- + attributes);
+ "name=" + name + ", attributes="
+ + attributes);
final MarshalledObject<AttributeList> sAttributes =
- new MarshalledObject<AttributeList>(attributes);
+ new MarshalledObject<AttributeList>(attributes);
final ClassLoader old = pushDefaultClassLoader();
try {
return connection.setAttributes(name,
- sAttributes,
- delegationSubject);
+ sAttributes,
+ delegationSubject);
} catch (IOException ioe) {
communicatorAdmin.gotIOException(ioe);
return connection.setAttributes(name,
- sAttributes,
- delegationSubject);
+ sAttributes,
+ delegationSubject);
} finally {
popDefaultClassLoader(old);
}
@@ -1038,37 +1038,37 @@
public Object invoke(ObjectName name,
- String operationName,
- Object params[],
- String signature[])
+ String operationName,
+ Object params[],
+ String signature[])
throws InstanceNotFoundException,
- MBeanException,
- ReflectionException,
- IOException {
+ MBeanException,
+ ReflectionException,
+ IOException {
if (logger.debugOn()) logger.debug("invoke",
- "name=" + name
- + ", operationName=" + operationName
- + ", params=" + objects(params)
- + ", signature=" + strings(signature));
+ "name=" + name
+ + ", operationName=" + operationName
+ + ", params=" + objects(params)
+ + ", signature=" + strings(signature));
final MarshalledObject<Object[]> sParams =
new MarshalledObject<Object[]>(params);
final ClassLoader old = pushDefaultClassLoader();
try {
return connection.invoke(name,
- operationName,
- sParams,
- signature,
- delegationSubject);
+ operationName,
+ sParams,
+ signature,
+ delegationSubject);
} catch (IOException ioe) {
communicatorAdmin.gotIOException(ioe);
return connection.invoke(name,
- operationName,
- sParams,
- signature,
- delegationSubject);
+ operationName,
+ sParams,
+ signature,
+ delegationSubject);
} finally {
popDefaultClassLoader(old);
}
@@ -1076,7 +1076,7 @@
public String getDefaultDomain()
- throws IOException {
+ throws IOException {
if (logger.debugOn()) logger.debug("getDefaultDomain", "");
final ClassLoader old = pushDefaultClassLoader();
@@ -1107,10 +1107,10 @@
}
public MBeanInfo getMBeanInfo(ObjectName name)
- throws InstanceNotFoundException,
- IntrospectionException,
- ReflectionException,
- IOException {
+ throws InstanceNotFoundException,
+ IntrospectionException,
+ ReflectionException,
+ IOException {
if (logger.debugOn()) logger.debug("getMBeanInfo", "name=" + name);
final ClassLoader old = pushDefaultClassLoader();
@@ -1127,41 +1127,41 @@
public boolean isInstanceOf(ObjectName name,
- String className)
+ String className)
throws InstanceNotFoundException,
- IOException {
+ IOException {
if (logger.debugOn())
logger.debug("isInstanceOf", "name=" + name +
- ", className=" + className);
+ ", className=" + className);
final ClassLoader old = pushDefaultClassLoader();
try {
return connection.isInstanceOf(name,
- className,
- delegationSubject);
+ className,
+ delegationSubject);
} catch (IOException ioe) {
communicatorAdmin.gotIOException(ioe);
return connection.isInstanceOf(name,
- className,
- delegationSubject);
+ className,
+ delegationSubject);
} finally {
popDefaultClassLoader(old);
}
}
public void addNotificationListener(ObjectName name,
- ObjectName listener,
- NotificationFilter filter,
- Object handback)
+ ObjectName listener,
+ NotificationFilter filter,
+ Object handback)
throws InstanceNotFoundException,
- IOException {
+ IOException {
if (logger.debugOn())
logger.debug("addNotificationListener" +
- "(ObjectName,ObjectName,NotificationFilter,Object)",
- "name=" + name + ", listener=" + listener
- + ", filter=" + filter + ", handback=" + handback);
+ "(ObjectName,ObjectName,NotificationFilter,Object)",
+ "name=" + name + ", listener=" + listener
+ + ", filter=" + filter + ", handback=" + handback);
final MarshalledObject<NotificationFilter> sFilter =
new MarshalledObject<NotificationFilter>(filter);
@@ -1170,64 +1170,64 @@
final ClassLoader old = pushDefaultClassLoader();
try {
connection.addNotificationListener(name,
- listener,
- sFilter,
- sHandback,
- delegationSubject);
+ listener,
+ sFilter,
+ sHandback,
+ delegationSubject);
} catch (IOException ioe) {
communicatorAdmin.gotIOException(ioe);
connection.addNotificationListener(name,
- listener,
- sFilter,
- sHandback,
- delegationSubject);
+ listener,
+ sFilter,
+ sHandback,
+ delegationSubject);
} finally {
popDefaultClassLoader(old);
}
}
public void removeNotificationListener(ObjectName name,
- ObjectName listener)
+ ObjectName listener)
throws InstanceNotFoundException,
- ListenerNotFoundException,
- IOException {
+ ListenerNotFoundException,
+ IOException {
if (logger.debugOn()) logger.debug("removeNotificationListener" +
- "(ObjectName,ObjectName)",
- "name=" + name
- + ", listener=" + listener);
+ "(ObjectName,ObjectName)",
+ "name=" + name
+ + ", listener=" + listener);
final ClassLoader old = pushDefaultClassLoader();
try {
connection.removeNotificationListener(name,
- listener,
- delegationSubject);
+ listener,
+ delegationSubject);
} catch (IOException ioe) {
communicatorAdmin.gotIOException(ioe);
connection.removeNotificationListener(name,
- listener,
- delegationSubject);
+ listener,
+ delegationSubject);
} finally {
popDefaultClassLoader(old);
}
}
public void removeNotificationListener(ObjectName name,
- ObjectName listener,
- NotificationFilter filter,
- Object handback)
+ ObjectName listener,
+ NotificationFilter filter,
+ Object handback)
throws InstanceNotFoundException,
- ListenerNotFoundException,
- IOException {
+ ListenerNotFoundException,
+ IOException {
if (logger.debugOn())
logger.debug("removeNotificationListener" +
- "(ObjectName,ObjectName,NotificationFilter,Object)",
- "name=" + name
- + ", listener=" + listener
- + ", filter=" + filter
- + ", handback=" + handback);
+ "(ObjectName,ObjectName,NotificationFilter,Object)",
+ "name=" + name
+ + ", listener=" + listener
+ + ", filter=" + filter
+ + ", handback=" + handback);
final MarshalledObject<NotificationFilter> sFilter =
new MarshalledObject<NotificationFilter>(filter);
@@ -1236,18 +1236,18 @@
final ClassLoader old = pushDefaultClassLoader();
try {
connection.removeNotificationListener(name,
- listener,
- sFilter,
- sHandback,
- delegationSubject);
+ listener,
+ sFilter,
+ sHandback,
+ delegationSubject);
} catch (IOException ioe) {
communicatorAdmin.gotIOException(ioe);
connection.removeNotificationListener(name,
- listener,
- sFilter,
- sHandback,
- delegationSubject);
+ listener,
+ sFilter,
+ sHandback,
+ delegationSubject);
} finally {
popDefaultClassLoader(old);
}
@@ -1256,34 +1256,34 @@
// Specific Notification Handle ----------------------------------
public void addNotificationListener(ObjectName name,
- NotificationListener listener,
- NotificationFilter filter,
- Object handback)
+ NotificationListener listener,
+ NotificationFilter filter,
+ Object handback)
throws InstanceNotFoundException,
- IOException {
+ IOException {
final boolean debug = logger.debugOn();
if (debug)
logger.debug("addNotificationListener" +
- "(ObjectName,NotificationListener,"+
- "NotificationFilter,Object)",
- "name=" + name
- + ", listener=" + listener
- + ", filter=" + filter
- + ", handback=" + handback);
+ "(ObjectName,NotificationListener,"+
+ "NotificationFilter,Object)",
+ "name=" + name
+ + ", listener=" + listener
+ + ", filter=" + filter
+ + ", handback=" + handback);
final Integer listenerID =
- addListenerWithSubject(name,
- new MarshalledObject<NotificationFilter>(filter),
- delegationSubject,true);
+ addListenerWithSubject(name,
+ new MarshalledObject<NotificationFilter>(filter),
+ delegationSubject,true);
rmiNotifClient.addNotificationListener(listenerID, name, listener,
- filter, handback,
- delegationSubject);
+ filter, handback,
+ delegationSubject);
}
public void removeNotificationListener(ObjectName name,
- NotificationListener listener)
+ NotificationListener listener)
throws InstanceNotFoundException,
ListenerNotFoundException,
IOException {
@@ -1291,28 +1291,28 @@
final boolean debug = logger.debugOn();
if (debug) logger.debug("removeNotificationListener"+
- "(ObjectName,NotificationListener)",
- "name=" + name
- + ", listener=" + listener);
+ "(ObjectName,NotificationListener)",
+ "name=" + name
+ + ", listener=" + listener);
final Integer[] ret =
- rmiNotifClient.removeNotificationListener(name, listener);
+ rmiNotifClient.removeNotificationListener(name, listener);
if (debug) logger.debug("removeNotificationListener",
- "listenerIDs=" + objects(ret));
+ "listenerIDs=" + objects(ret));
final ClassLoader old = pushDefaultClassLoader();
try {
connection.removeNotificationListeners(name,
- ret,
- delegationSubject);
+ ret,
+ delegationSubject);
} catch (IOException ioe) {
communicatorAdmin.gotIOException(ioe);
connection.removeNotificationListeners(name,
- ret,
- delegationSubject);
+ ret,
+ delegationSubject);
} finally {
popDefaultClassLoader(old);
}
@@ -1320,41 +1320,41 @@
}
public void removeNotificationListener(ObjectName name,
- NotificationListener listener,
- NotificationFilter filter,
- Object handback)
- throws InstanceNotFoundException,
- ListenerNotFoundException,
- IOException {
+ NotificationListener listener,
+ NotificationFilter filter,
+ Object handback)
+ throws InstanceNotFoundException,
+ ListenerNotFoundException,
+ IOException {
final boolean debug = logger.debugOn();
if (debug)
logger.debug("removeNotificationListener"+
- "(ObjectName,NotificationListener,"+
- "NotificationFilter,Object)",
- "name=" + name
- + ", listener=" + listener
- + ", filter=" + filter
- + ", handback=" + handback);
+ "(ObjectName,NotificationListener,"+
+ "NotificationFilter,Object)",
+ "name=" + name
+ + ", listener=" + listener
+ + ", filter=" + filter
+ + ", handback=" + handback);
final Integer ret =
- rmiNotifClient.removeNotificationListener(name, listener,
- filter, handback);
+ rmiNotifClient.removeNotificationListener(name, listener,
+ filter, handback);
if (debug) logger.debug("removeNotificationListener",
- "listenerID=" + ret);
+ "listenerID=" + ret);
final ClassLoader old = pushDefaultClassLoader();
try {
connection.removeNotificationListeners(name,
- new Integer[] {ret},
- delegationSubject);
+ new Integer[] {ret},
+ delegationSubject);
} catch (IOException ioe) {
communicatorAdmin.gotIOException(ioe);
connection.removeNotificationListeners(name,
- new Integer[] {ret},
- delegationSubject);
+ new Integer[] {ret},
+ delegationSubject);
} finally {
popDefaultClassLoader(old);
}
@@ -1369,16 +1369,16 @@
}
protected NotificationResult fetchNotifs(long clientSequenceNumber,
- int maxNotifications,
- long timeout)
+ int maxNotifications,
+ long timeout)
throws IOException, ClassNotFoundException {
IOException org;
while (true) { // used for a successful re-connection
try {
return connection.fetchNotifications(clientSequenceNumber,
- maxNotifications,
- timeout);
+ maxNotifications,
+ timeout);
} catch (IOException ioe) {
org = ioe;
@@ -1417,7 +1417,7 @@
clear we can do much better. */
if (ume.detail instanceof WriteAbortedException) {
WriteAbortedException wae =
- (WriteAbortedException) ume.detail;
+ (WriteAbortedException) ume.detail;
if (wae.detail instanceof IOException)
throw (IOException) wae.detail;
}
@@ -1435,11 +1435,11 @@
}
protected Integer addListenerForMBeanRemovedNotif()
- throws IOException, InstanceNotFoundException {
+ throws IOException, InstanceNotFoundException {
NotificationFilterSupport clientFilter =
- new NotificationFilterSupport();
+ new NotificationFilterSupport();
clientFilter.enableType(
- MBeanServerNotification.UNREGISTRATION_NOTIFICATION);
+ MBeanServerNotification.UNREGISTRATION_NOTIFICATION);
MarshalledObject<NotificationFilter> sFilter =
new MarshalledObject<NotificationFilter>(clientFilter);
@@ -1451,36 +1451,36 @@
final Subject[] subjects = new Subject[] {null};
try {
listenerIDs =
- connection.addNotificationListeners(names,
- filters,
- subjects);
+ connection.addNotificationListeners(names,
+ filters,
+ subjects);
} catch (IOException ioe) {
communicatorAdmin.gotIOException(ioe);
listenerIDs =
- connection.addNotificationListeners(names,
- filters,
- subjects);
+ connection.addNotificationListeners(names,
+ filters,
+ subjects);
}
return listenerIDs[0];
}
protected void removeListenerForMBeanRemovedNotif(Integer id)
- throws IOException, InstanceNotFoundException,
- ListenerNotFoundException {
+ throws IOException, InstanceNotFoundException,
+ ListenerNotFoundException {
try {
connection.removeNotificationListeners(
- MBeanServerDelegate.DELEGATE_NAME,
- new Integer[] {id},
- null);
+ MBeanServerDelegate.DELEGATE_NAME,
+ new Integer[] {id},
+ null);
} catch (IOException ioe) {
communicatorAdmin.gotIOException(ioe);
connection.removeNotificationListeners(
- MBeanServerDelegate.DELEGATE_NAME,
- new Integer[] {id},
- null);
+ MBeanServerDelegate.DELEGATE_NAME,
+ new Integer[] {id},
+ null);
}
}
@@ -1530,7 +1530,7 @@
// we should close the connection,
// but send a failed notif at first
final Notification failedNotif =
- new JMXConnectionNotification(
+ new JMXConnectionNotification(
JMXConnectionNotification.FAILED,
this,
connectionId,
@@ -1559,7 +1559,7 @@
will throw a ServerException at client side which wraps this
UnmarshalException.
No failed notif here.
- */
+ */
Throwable tt = ((ServerException)ioe).detail;
if (tt instanceof IOException) {
@@ -1600,11 +1600,11 @@
for (i=0;i<len;i++) {
clis[i] = new ClientListenerInfo(ids[i],
- names[i],
- listeners[i],
- filters[i],
- handbacks[i],
- subjects[i]);
+ names[i],
+ listeners[i],
+ filters[i],
+ handbacks[i],
+ subjects[i]);
}
rmiNotifClient.postReconnection(clis);
@@ -1623,15 +1623,15 @@
false);
clis[j++] = new ClientListenerInfo(id,
- names[i],
- listeners[i],
- filters[i],
- handbacks[i],
- subjects[i]);
+ names[i],
+ listeners[i],
+ filters[i],
+ handbacks[i],
+ subjects[i]);
} catch (InstanceNotFoundException infe) {
logger.warning("reconnectNotificationListeners",
- "Can't reconnect listener for " +
- names[i]);
+ "Can't reconnect listener for " +
+ names[i]);
}
}
@@ -1647,7 +1647,7 @@
protected void checkConnection() throws IOException {
if (logger.debugOn())
logger.debug("RMIClientCommunicatorAdmin-checkConnection",
- "Calling the method getDefaultDomain.");
+ "Calling the method getDefaultDomain.");
connection.getDefaultDomain(null);
}
@@ -1677,12 +1677,12 @@
connectionId = getConnectionId();
Notification reconnectedNotif =
- new JMXConnectionNotification(JMXConnectionNotification.OPENED,
- this,
- connectionId,
- clientNotifSeqNo++,
- "Reconnected to server",
- null);
+ new JMXConnectionNotification(JMXConnectionNotification.OPENED,
+ this,
+ connectionId,
+ clientNotifSeqNo++,
+ "Reconnected to server",
+ null);
sendNotification(reconnectedNotif);
}
@@ -1692,7 +1692,7 @@
close();
} catch (IOException ioe) {
logger.warning("RMIClientCommunicatorAdmin-doStop",
- "Failed to call the method close():" + ioe);
+ "Failed to call the method close():" + ioe);
logger.debug("RMIClientCommunicatorAdmin-doStop",ioe);
}
}
@@ -1785,15 +1785,15 @@
final Object orb = environment.get(EnvHelp.DEFAULT_ORB);
if (orb != null && !(orb instanceof ORB))
throw new IllegalArgumentException(EnvHelp.DEFAULT_ORB +
- " must be an instance of org.omg.CORBA.ORB.");
+ " must be an instance of org.omg.CORBA.ORB.");
if (orb != null) return (ORB)orb;
}
final ORB orb =
- (RMIConnector.orb==null)?null:RMIConnector.orb.get();
+ (RMIConnector.orb==null)?null:RMIConnector.orb.get();
if (orb != null) return orb;
final ORB newOrb =
- ORB.init((String[])null, (Properties)null);
+ ORB.init((String[])null, (Properties)null);
RMIConnector.orb = new WeakReference<ORB>(newOrb);
return newOrb;
}
@@ -1810,11 +1810,11 @@
* @see #RMIConnector(RMIServer,Map)
**/
private void readObject(java.io.ObjectInputStream s)
- throws IOException, ClassNotFoundException {
+ throws IOException, ClassNotFoundException {
s.defaultReadObject();
if (rmiServer == null && jmxServiceURL == null) throw new
- InvalidObjectException("rmiServer and jmxServiceURL both null");
+ InvalidObjectException("rmiServer and jmxServiceURL both null");
initTransients();
}
@@ -1851,9 +1851,9 @@
* @see #RMIConnector(RMIServer,Map)
**/
private void writeObject(java.io.ObjectOutputStream s)
- throws IOException {
+ throws IOException {
if (rmiServer == null && jmxServiceURL == null) throw new
- InvalidObjectException("rmiServer and jmxServiceURL both null.");
+ InvalidObjectException("rmiServer and jmxServiceURL both null.");
connectStub(this.rmiServer,env);
s.defaultWriteObject();
}
@@ -1892,21 +1892,21 @@
//--------------------------------------------------------------------
private static void checkStub(Remote stub,
- Class<?> stubClass) {
+ Class<?> stubClass) {
// Check remote stub is from the expected class.
//
if (stub.getClass() != stubClass) {
if (!Proxy.isProxyClass(stub.getClass())) {
throw new SecurityException(
- "Expecting a " + stubClass.getName() + " stub!");
+ "Expecting a " + stubClass.getName() + " stub!");
} else {
InvocationHandler handler = Proxy.getInvocationHandler(stub);
if (handler.getClass() != RemoteObjectInvocationHandler.class)
throw new SecurityException(
- "Expecting a dynamic proxy instance with a " +
- RemoteObjectInvocationHandler.class.getName() +
- " invocation handler!");
+ "Expecting a dynamic proxy instance with a " +
+ RemoteObjectInvocationHandler.class.getName() +
+ " invocation handler!");
else
stub = (Remote) handler;
}
@@ -1918,8 +1918,8 @@
RemoteRef ref = ((RemoteObject)stub).getRef();
if (ref.getClass() != UnicastRef2.class)
throw new SecurityException(
- "Expecting a " + UnicastRef2.class.getName() +
- " remote reference in stub!");
+ "Expecting a " + UnicastRef2.class.getName() +
+ " remote reference in stub!");
// Check RMIClientSocketFactory in stub is from the expected class
// "javax.rmi.ssl.SslRMIClientSocketFactory".
@@ -1928,8 +1928,8 @@
RMIClientSocketFactory csf = liveRef.getClientSocketFactory();
if (csf == null || csf.getClass() != SslRMIClientSocketFactory.class)
throw new SecurityException(
- "Expecting a " + SslRMIClientSocketFactory.class.getName() +
- " RMI client socket factory in stub!");
+ "Expecting a " + SslRMIClientSocketFactory.class.getName() +
+ " RMI client socket factory in stub!");
}
//--------------------------------------------------------------------
@@ -1937,8 +1937,8 @@
//--------------------------------------------------------------------
private RMIServer findRMIServer(JMXServiceURL directoryURL,
- Map<String, Object> environment)
- throws NamingException, IOException {
+ Map<String, Object> environment)
+ throws NamingException, IOException {
final boolean isIiop = RMIConnectorServer.isIiopURL(directoryURL,true);
if (isIiop) {
// Make sure java.naming.corba.orb is in the Map.
@@ -1956,7 +1956,7 @@
return findRMIServerIIOP(path.substring(5,end), environment, isIiop);
else {
final String msg = "URL path must begin with /jndi/ or /stub/ " +
- "or /ior/: " + path;
+ "or /ior/: " + path;
throw new MalformedURLException(msg);
}
}
@@ -1975,8 +1975,8 @@
* @exception NamingException if the stub couldn't be found.
**/
private RMIServer findRMIServerJNDI(String jndiURL, Map<String, ?> env,
- boolean isIiop)
- throws NamingException {
+ boolean isIiop)
+ throws NamingException {
InitialContext ctx = new InitialContext(EnvHelp.mapToHashtable(env));
@@ -1997,11 +1997,11 @@
private static RMIServer narrowIIOPServer(Object objref) {
try {
return (RMIServer)
- PortableRemoteObject.narrow(objref, RMIServer.class);
+ PortableRemoteObject.narrow(objref, RMIServer.class);
} catch (ClassCastException e) {
if (logger.traceOn())
logger.trace("narrowIIOPServer","Failed to narrow objref=" +
- objref + ": " + e);
+ objref + ": " + e);
if (logger.debugOn()) logger.debug("narrowIIOPServer",e);
return null;
}
@@ -2010,7 +2010,7 @@
private RMIServer findRMIServerIIOP(String ior, Map<String, ?> env, boolean isIiop) {
// could forbid "rmi:" URL here -- but do we need to?
final ORB orb = (ORB)
- env.get(EnvHelp.DEFAULT_ORB);
+ env.get(EnvHelp.DEFAULT_ORB);
final Object stub = orb.string_to_object(ior);
return (RMIServer) PortableRemoteObject.narrow(stub, RMIServer.class);
}
@@ -2023,15 +2023,15 @@
serialized = base64ToByteArray(base64);
} catch (IllegalArgumentException e) {
throw new MalformedURLException("Bad BASE64 encoding: " +
- e.getMessage());
+ e.getMessage());
}
final ByteArrayInputStream bin = new ByteArrayInputStream(serialized);
final ClassLoader loader = EnvHelp.resolveClientClassLoader(env);
final ObjectInputStream oin =
- (loader == null) ?
- new ObjectInputStream(bin) :
- new ObjectInputStreamWithLoader(bin, loader);
+ (loader == null) ?
+ new ObjectInputStream(bin) :
+ new ObjectInputStreamWithLoader(bin, loader);
final Object stub;
try {
stub = oin.readObject();
@@ -2044,7 +2044,7 @@
private static final class ObjectInputStreamWithLoader
extends ObjectInputStream {
ObjectInputStreamWithLoader(InputStream in, ClassLoader cl)
- throws IOException {
+ throws IOException {
super(in);
this.loader = cl;
}
@@ -2127,40 +2127,40 @@
RMIServer.class.getName() + "Impl_Stub";
private static final Class<?> rmiServerImplStubClass;
private static final String rmiConnectionImplStubClassName =
- RMIConnection.class.getName() + "Impl_Stub";
+ RMIConnection.class.getName() + "Impl_Stub";
private static final Class<?> rmiConnectionImplStubClass;
private static final String pRefClassName =
"com.sun.jmx.remote.internal.PRef";
private static final Constructor<?> proxyRefConstructor;
static {
final String pRefByteCodeString =
- "\312\376\272\276\0\0\0.\0\27\12\0\5\0\15\11\0\4\0\16\13\0\17\0"+
- "\20\7\0\21\7\0\22\1\0\6<init>\1\0\36(Ljava/rmi/server/RemoteRef;"+
- ")V\1\0\4Code\1\0\6invoke\1\0S(Ljava/rmi/Remote;Ljava/lang/reflec"+
- "t/Method;[Ljava/lang/Object;J)Ljava/lang/Object;\1\0\12Exception"+
- "s\7\0\23\14\0\6\0\7\14\0\24\0\25\7\0\26\14\0\11\0\12\1\0\40com/"+
- "sun/jmx/remote/internal/PRef\1\0$com/sun/jmx/remote/internal/Pr"+
- "oxyRef\1\0\23java/lang/Exception\1\0\3ref\1\0\33Ljava/rmi/serve"+
- "r/RemoteRef;\1\0\31java/rmi/server/RemoteRef\0!\0\4\0\5\0\0\0\0"+
- "\0\2\0\1\0\6\0\7\0\1\0\10\0\0\0\22\0\2\0\2\0\0\0\6*+\267\0\1\261"+
- "\0\0\0\0\0\1\0\11\0\12\0\2\0\10\0\0\0\33\0\6\0\6\0\0\0\17*\264\0"+
- "\2+,-\26\4\271\0\3\6\0\260\0\0\0\0\0\13\0\0\0\4\0\1\0\14\0\0";
+ "\312\376\272\276\0\0\0.\0\27\12\0\5\0\15\11\0\4\0\16\13\0\17\0"+
+ "\20\7\0\21\7\0\22\1\0\6<init>\1\0\36(Ljava/rmi/server/RemoteRef;"+
+ ")V\1\0\4Code\1\0\6invoke\1\0S(Ljava/rmi/Remote;Ljava/lang/reflec"+
+ "t/Method;[Ljava/lang/Object;J)Ljava/lang/Object;\1\0\12Exception"+
+ "s\7\0\23\14\0\6\0\7\14\0\24\0\25\7\0\26\14\0\11\0\12\1\0\40com/"+
+ "sun/jmx/remote/internal/PRef\1\0$com/sun/jmx/remote/internal/Pr"+
+ "oxyRef\1\0\23java/lang/Exception\1\0\3ref\1\0\33Ljava/rmi/serve"+
+ "r/RemoteRef;\1\0\31java/rmi/server/RemoteRef\0!\0\4\0\5\0\0\0\0"+
+ "\0\2\0\1\0\6\0\7\0\1\0\10\0\0\0\22\0\2\0\2\0\0\0\6*+\267\0\1\261"+
+ "\0\0\0\0\0\1\0\11\0\12\0\2\0\10\0\0\0\33\0\6\0\6\0\0\0\17*\264\0"+
+ "\2+,-\26\4\271\0\3\6\0\260\0\0\0\0\0\13\0\0\0\4\0\1\0\14\0\0";
final byte[] pRefByteCode =
- NoCallStackClassLoader.stringToBytes(pRefByteCodeString);
+ NoCallStackClassLoader.stringToBytes(pRefByteCodeString);
PrivilegedExceptionAction<Constructor<?>> action =
new PrivilegedExceptionAction<Constructor<?>>() {
public Constructor<?> run() throws Exception {
Class thisClass = RMIConnector.class;
ClassLoader thisLoader = thisClass.getClassLoader();
ProtectionDomain thisProtectionDomain =
- thisClass.getProtectionDomain();
+ thisClass.getProtectionDomain();
String[] otherClassNames = {ProxyRef.class.getName()};
ClassLoader cl =
- new NoCallStackClassLoader(pRefClassName,
- pRefByteCode,
- otherClassNames,
- thisLoader,
- thisProtectionDomain);
+ new NoCallStackClassLoader(pRefClassName,
+ pRefByteCode,
+ otherClassNames,
+ thisLoader,
+ thisProtectionDomain);
Class<?> c = cl.loadClass(pRefClassName);
return c.getConstructor(RemoteRef.class);
}
@@ -2171,8 +2171,8 @@
serverStubClass = Class.forName(rmiServerImplStubClassName);
} catch (Exception e) {
logger.error("<clinit>",
- "Failed to instantiate " +
- rmiServerImplStubClassName + ": " + e);
+ "Failed to instantiate " +
+ rmiServerImplStubClassName + ": " + e);
logger.debug("<clinit>",e);
serverStubClass = null;
}
@@ -2185,8 +2185,8 @@
constr = (Constructor<?>) AccessController.doPrivileged(action);
} catch (Exception e) {
logger.error("<clinit>",
- "Failed to initialize proxy reference constructor "+
- "for " + rmiConnectionImplStubClassName + ": " + e);
+ "Failed to initialize proxy reference constructor "+
+ "for " + rmiConnectionImplStubClassName + ": " + e);
logger.debug("<clinit>",e);
stubClass = null;
constr = null;
@@ -2196,9 +2196,9 @@
}
private static RMIConnection shadowJrmpStub(RemoteObject stub)
- throws InstantiationException, IllegalAccessException,
- InvocationTargetException, ClassNotFoundException,
- NoSuchMethodException {
+ throws InstantiationException, IllegalAccessException,
+ InvocationTargetException, ClassNotFoundException,
+ NoSuchMethodException {
RemoteRef ref = stub.getRef();
RemoteRef proxyRef = (RemoteRef)
proxyRefConstructor.newInstance(new Object[] {ref});
@@ -2206,7 +2206,7 @@
rmiConnectionImplStubClass.getConstructor(RemoteRef.class);
Object[] args = {proxyRef};
RMIConnection proxyStub = (RMIConnection)
- rmiConnectionImplStubConstructor.newInstance(args);
+ rmiConnectionImplStubConstructor.newInstance(args);
return proxyStub;
}
@@ -2326,55 +2326,55 @@
*/
private static final String iiopConnectionStubClassName =
- "org.omg.stub.javax.management.remote.rmi._RMIConnection_Stub";
+ "org.omg.stub.javax.management.remote.rmi._RMIConnection_Stub";
private static final String proxyStubClassName =
- "com.sun.jmx.remote.internal.ProxyStub";
+ "com.sun.jmx.remote.internal.ProxyStub";
private static final String pInputStreamClassName =
"com.sun.jmx.remote.internal.PInputStream";
private static final Class<?> proxyStubClass;
static {
final String proxyStubByteCodeString =
- "\312\376\272\276\0\0\0.\0)\12\0\14\0\26\7\0\27\12\0\14\0\30\12"+
- "\0\2\0\31\7\0\32\12\0\5\0\33\12\0\5\0\34\12\0\5\0\35\12\0\2\0"+
- "\36\12\0\14\0\37\7\0\40\7\0!\1\0\6<init>\1\0\3()V\1\0\4Code\1"+
- "\0\7_invoke\1\0K(Lorg/omg/CORBA/portable/OutputStream;)Lorg/o"+
- "mg/CORBA/portable/InputStream;\1\0\12Exceptions\7\0\"\1\0\15_"+
- "releaseReply\1\0'(Lorg/omg/CORBA/portable/InputStream;)V\14\0"+
- "\15\0\16\1\0(com/sun/jmx/remote/internal/PInputStream\14\0\20"+
- "\0\21\14\0\15\0\25\1\0+org/omg/CORBA/portable/ApplicationExce"+
- "ption\14\0#\0$\14\0%\0&\14\0\15\0'\14\0(\0$\14\0\24\0\25\1\0%"+
- "com/sun/jmx/remote/internal/ProxyStub\1\0<org/omg/stub/javax/"+
- "management/remote/rmi/_RMIConnection_Stub\1\0)org/omg/CORBA/p"+
- "ortable/RemarshalException\1\0\16getInputStream\1\0&()Lorg/om"+
- "g/CORBA/portable/InputStream;\1\0\5getId\1\0\24()Ljava/lang/S"+
- "tring;\1\09(Ljava/lang/String;Lorg/omg/CORBA/portable/InputSt"+
- "ream;)V\1\0\25getProxiedInputStream\0!\0\13\0\14\0\0\0\0\0\3\0"+
- "\1\0\15\0\16\0\1\0\17\0\0\0\21\0\1\0\1\0\0\0\5*\267\0\1\261\0"+
- "\0\0\0\0\1\0\20\0\21\0\2\0\17\0\0\0;\0\4\0\4\0\0\0'\273\0\2Y*"+
- "+\267\0\3\267\0\4\260M\273\0\2Y,\266\0\6\267\0\4N\273\0\5Y,\266"+
- "\0\7-\267\0\10\277\0\1\0\0\0\14\0\15\0\5\0\0\0\22\0\0\0\6\0\2"+
- "\0\5\0\23\0\1\0\24\0\25\0\1\0\17\0\0\0\36\0\2\0\2\0\0\0\22+\306"+
- "\0\13+\300\0\2\266\0\11L*+\267\0\12\261\0\0\0\0\0\0";
+ "\312\376\272\276\0\0\0.\0)\12\0\14\0\26\7\0\27\12\0\14\0\30\12"+
+ "\0\2\0\31\7\0\32\12\0\5\0\33\12\0\5\0\34\12\0\5\0\35\12\0\2\0"+
+ "\36\12\0\14\0\37\7\0\40\7\0!\1\0\6<init>\1\0\3()V\1\0\4Code\1"+
+ "\0\7_invoke\1\0K(Lorg/omg/CORBA/portable/OutputStream;)Lorg/o"+
+ "mg/CORBA/portable/InputStream;\1\0\12Exceptions\7\0\"\1\0\15_"+
+ "releaseReply\1\0'(Lorg/omg/CORBA/portable/InputStream;)V\14\0"+
+ "\15\0\16\1\0(com/sun/jmx/remote/internal/PInputStream\14\0\20"+
+ "\0\21\14\0\15\0\25\1\0+org/omg/CORBA/portable/ApplicationExce"+
+ "ption\14\0#\0$\14\0%\0&\14\0\15\0'\14\0(\0$\14\0\24\0\25\1\0%"+
+ "com/sun/jmx/remote/internal/ProxyStub\1\0<org/omg/stub/javax/"+
+ "management/remote/rmi/_RMIConnection_Stub\1\0)org/omg/CORBA/p"+
+ "ortable/RemarshalException\1\0\16getInputStream\1\0&()Lorg/om"+
+ "g/CORBA/portable/InputStream;\1\0\5getId\1\0\24()Ljava/lang/S"+
+ "tring;\1\09(Ljava/lang/String;Lorg/omg/CORBA/portable/InputSt"+
+ "ream;)V\1\0\25getProxiedInputStream\0!\0\13\0\14\0\0\0\0\0\3\0"+
+ "\1\0\15\0\16\0\1\0\17\0\0\0\21\0\1\0\1\0\0\0\5*\267\0\1\261\0"+
+ "\0\0\0\0\1\0\20\0\21\0\2\0\17\0\0\0;\0\4\0\4\0\0\0'\273\0\2Y*"+
+ "+\267\0\3\267\0\4\260M\273\0\2Y,\266\0\6\267\0\4N\273\0\5Y,\266"+
+ "\0\7-\267\0\10\277\0\1\0\0\0\14\0\15\0\5\0\0\0\22\0\0\0\6\0\2"+
+ "\0\5\0\23\0\1\0\24\0\25\0\1\0\17\0\0\0\36\0\2\0\2\0\0\0\22+\306"+
+ "\0\13+\300\0\2\266\0\11L*+\267\0\12\261\0\0\0\0\0\0";
final String pInputStreamByteCodeString =
- "\312\376\272\276\0\0\0.\0\36\12\0\7\0\17\11\0\6\0\20\12\0\21\0"+
- "\22\12\0\6\0\23\12\0\24\0\25\7\0\26\7\0\27\1\0\6<init>\1\0'(L"+
- "org/omg/CORBA/portable/InputStream;)V\1\0\4Code\1\0\10read_an"+
- "y\1\0\25()Lorg/omg/CORBA/Any;\1\0\12read_value\1\0)(Ljava/lan"+
- "g/Class;)Ljava/io/Serializable;\14\0\10\0\11\14\0\30\0\31\7\0"+
- "\32\14\0\13\0\14\14\0\33\0\34\7\0\35\14\0\15\0\16\1\0(com/sun"+
- "/jmx/remote/internal/PInputStream\1\0,com/sun/jmx/remote/inte"+
- "rnal/ProxyInputStream\1\0\2in\1\0$Lorg/omg/CORBA/portable/Inp"+
- "utStream;\1\0\"org/omg/CORBA/portable/InputStream\1\0\6narrow"+
- "\1\0*()Lorg/omg/CORBA_2_3/portable/InputStream;\1\0&org/omg/C"+
- "ORBA_2_3/portable/InputStream\0!\0\6\0\7\0\0\0\0\0\3\0\1\0\10"+
- "\0\11\0\1\0\12\0\0\0\22\0\2\0\2\0\0\0\6*+\267\0\1\261\0\0\0\0"+
- "\0\1\0\13\0\14\0\1\0\12\0\0\0\24\0\1\0\1\0\0\0\10*\264\0\2\266"+
- "\0\3\260\0\0\0\0\0\1\0\15\0\16\0\1\0\12\0\0\0\25\0\2\0\2\0\0\0"+
- "\11*\266\0\4+\266\0\5\260\0\0\0\0\0\0";
+ "\312\376\272\276\0\0\0.\0\36\12\0\7\0\17\11\0\6\0\20\12\0\21\0"+
+ "\22\12\0\6\0\23\12\0\24\0\25\7\0\26\7\0\27\1\0\6<init>\1\0'(L"+
+ "org/omg/CORBA/portable/InputStream;)V\1\0\4Code\1\0\10read_an"+
+ "y\1\0\25()Lorg/omg/CORBA/Any;\1\0\12read_value\1\0)(Ljava/lan"+
+ "g/Class;)Ljava/io/Serializable;\14\0\10\0\11\14\0\30\0\31\7\0"+
+ "\32\14\0\13\0\14\14\0\33\0\34\7\0\35\14\0\15\0\16\1\0(com/sun"+
+ "/jmx/remote/internal/PInputStream\1\0,com/sun/jmx/remote/inte"+
+ "rnal/ProxyInputStream\1\0\2in\1\0$Lorg/omg/CORBA/portable/Inp"+
+ "utStream;\1\0\"org/omg/CORBA/portable/InputStream\1\0\6narrow"+
+ "\1\0*()Lorg/omg/CORBA_2_3/portable/InputStream;\1\0&org/omg/C"+
+ "ORBA_2_3/portable/InputStream\0!\0\6\0\7\0\0\0\0\0\3\0\1\0\10"+
+ "\0\11\0\1\0\12\0\0\0\22\0\2\0\2\0\0\0\6*+\267\0\1\261\0\0\0\0"+
+ "\0\1\0\13\0\14\0\1\0\12\0\0\0\24\0\1\0\1\0\0\0\10*\264\0\2\266"+
+ "\0\3\260\0\0\0\0\0\1\0\15\0\16\0\1\0\12\0\0\0\25\0\2\0\2\0\0\0"+
+ "\11*\266\0\4+\266\0\5\260\0\0\0\0\0\0";
final byte[] proxyStubByteCode =
- NoCallStackClassLoader.stringToBytes(proxyStubByteCodeString);
+ NoCallStackClassLoader.stringToBytes(proxyStubByteCodeString);
final byte[] pInputStreamByteCode =
- NoCallStackClassLoader.stringToBytes(pInputStreamByteCodeString);
+ NoCallStackClassLoader.stringToBytes(pInputStreamByteCodeString);
final String[] classNames={proxyStubClassName, pInputStreamClassName};
final byte[][] byteCodes = {proxyStubByteCode, pInputStreamByteCode};
final String[] otherClassNames = {
@@ -2388,13 +2388,13 @@
Class thisClass = RMIConnector.class;
ClassLoader thisLoader = thisClass.getClassLoader();
ProtectionDomain thisProtectionDomain =
- thisClass.getProtectionDomain();
+ thisClass.getProtectionDomain();
ClassLoader cl =
- new NoCallStackClassLoader(classNames,
- byteCodes,
- otherClassNames,
- thisLoader,
- thisProtectionDomain);
+ new NoCallStackClassLoader(classNames,
+ byteCodes,
+ otherClassNames,
+ thisLoader,
+ thisProtectionDomain);
return cl.loadClass(proxyStubClassName);
}
};
@@ -2403,7 +2403,7 @@
stubClass = AccessController.doPrivileged(action);
} catch (Exception e) {
logger.error("<clinit>",
- "Unexpected exception making shadow IIOP stub class: "+e);
+ "Unexpected exception making shadow IIOP stub class: "+e);
logger.debug("<clinit>",e);
stubClass = null;
}
@@ -2411,15 +2411,15 @@
}
private static RMIConnection shadowIiopStub(Stub stub)
- throws InstantiationException, IllegalAccessException {
+ throws InstantiationException, IllegalAccessException {
Stub proxyStub = (Stub) proxyStubClass.newInstance();
proxyStub._set_delegate(stub._get_delegate());
return (RMIConnection) proxyStub;
}
private static RMIConnection getConnection(RMIServer server,
- Object credentials,
- boolean checkStub)
+ Object credentials,
+ boolean checkStub)
throws IOException {
RMIConnection c = server.newClient(credentials);
if (checkStub) checkStub(c, rmiConnectionImplStubClass);
@@ -2429,14 +2429,14 @@
if (c.getClass().getName().equals(iiopConnectionStubClassName))
return shadowIiopStub((Stub) c);
logger.trace("getConnection",
- "Did not wrap " + c.getClass() + " to foil " +
- "stack search for classes: class loading semantics " +
- "may be incorrect");
+ "Did not wrap " + c.getClass() + " to foil " +
+ "stack search for classes: class loading semantics " +
+ "may be incorrect");
} catch (Exception e) {
logger.error("getConnection",
- "Could not wrap " + c.getClass() + " to foil " +
- "stack search for classes: class loading semantics " +
- "may be incorrect: " + e);
+ "Could not wrap " + c.getClass() + " to foil " +
+ "stack search for classes: class loading semantics " +
+ "may be incorrect: " + e);
logger.debug("getConnection",e);
// so just return the original stub, which will work for all
// but the most exotic class loading situations
@@ -2449,7 +2449,7 @@
int numGroups = sLen/4;
if (4*numGroups != sLen)
throw new IllegalArgumentException(
- "String length must be a multiple of four.");
+ "String length must be a multiple of four.");
int missingBytesInLastGroup = 0;
int numFullGroups = numGroups;
if (sLen != 0) {
@@ -2535,21 +2535,21 @@
final ClassLoader old = t.getContextClassLoader();
if (defaultClassLoader != null)
AccessController.doPrivileged(new PrivilegedAction<Void>() {
- public Void run() {
- t.setContextClassLoader(defaultClassLoader);
- return null;
- }
- });
- return old;
+ public Void run() {
+ t.setContextClassLoader(defaultClassLoader);
+ return null;
+ }
+ });
+ return old;
}
private void popDefaultClassLoader(final ClassLoader old) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
- public Void run() {
- Thread.currentThread().setContextClassLoader(old);
- return null;
- }
- });
+ public Void run() {
+ Thread.currentThread().setContextClassLoader(old);
+ return null;
+ }
+ });
}
//--------------------------------------------------------------------
--- a/jdk/src/share/classes/javax/management/remote/rmi/RMIConnectorServer.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/javax/management/remote/rmi/RMIConnectorServer.java Thu Nov 13 11:15:01 2008 -0800
@@ -383,7 +383,7 @@
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 @@
else
rmiServer = newServer();
- rmiServer.setMBeanServer(getSystemMBeanServer());
+ rmiServer.setMBeanServer(getSystemMBeanServerForwarder());
rmiServer.setDefaultClassLoader(defaultClassLoader);
rmiServer.setRMIConnectorServer(this);
rmiServer.export();
@@ -592,31 +592,6 @@
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 @@
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);
--- a/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java Thu Nov 13 11:15:01 2008 -0800
@@ -435,8 +435,14 @@
if (streaming()) {
if (chunkLength != -1) {
requests.set ("Transfer-Encoding", "chunked");
- } else {
- requests.set ("Content-Length", String.valueOf(fixedContentLength));
+ } else { /* fixed content length */
+ if (fixedContentLengthLong != -1) {
+ requests.set ("Content-Length",
+ String.valueOf(fixedContentLengthLong));
+ } else if (fixedContentLength != -1) {
+ requests.set ("Content-Length",
+ String.valueOf(fixedContentLength));
+ }
}
} else if (poster != null) {
/* add Content-Length & POST/PUT data */
@@ -871,11 +877,17 @@
ps = (PrintStream)http.getOutputStream();
if (streaming()) {
if (strOutputStream == null) {
- if (fixedContentLength != -1) {
- strOutputStream = new StreamingOutputStream (ps, fixedContentLength);
- } else if (chunkLength != -1) {
- strOutputStream =
- new StreamingOutputStream (new ChunkedOutputStream (ps, chunkLength), -1);
+ if (chunkLength != -1) { /* chunked */
+ strOutputStream = new StreamingOutputStream(
+ new ChunkedOutputStream(ps, chunkLength), -1L);
+ } else { /* must be fixed content length */
+ long length = 0L;
+ if (fixedContentLengthLong != -1) {
+ length = fixedContentLengthLong;
+ } else if (fixedContentLength != -1) {
+ length = fixedContentLength;
+ }
+ strOutputStream = new StreamingOutputStream(ps, length);
}
}
return strOutputStream;
@@ -895,7 +907,8 @@
}
private boolean streaming () {
- return (fixedContentLength != -1) || (chunkLength != -1);
+ return (fixedContentLength != -1) || (fixedContentLengthLong != -1) ||
+ (chunkLength != -1);
}
/*
@@ -2619,8 +2632,8 @@
class StreamingOutputStream extends FilterOutputStream {
- int expected;
- int written;
+ long expected;
+ long written;
boolean closed;
boolean error;
IOException errorExcp;
@@ -2631,10 +2644,10 @@
* In the 2nd case, we make sure the expected number of
* of bytes are actually written
*/
- StreamingOutputStream (OutputStream os, int expectedLength) {
+ StreamingOutputStream (OutputStream os, long expectedLength) {
super (os);
expected = expectedLength;
- written = 0;
+ written = 0L;
closed = false;
error = false;
}
@@ -2643,7 +2656,7 @@
public void write (int b) throws IOException {
checkError();
written ++;
- if (expected != -1 && written > expected) {
+ if (expected != -1L && written > expected) {
throw new IOException ("too many bytes written");
}
out.write (b);
@@ -2658,7 +2671,7 @@
public void write (byte[] b, int off, int len) throws IOException {
checkError();
written += len;
- if (expected != -1 && written > expected) {
+ if (expected != -1L && written > expected) {
out.close ();
throw new IOException ("too many bytes written");
}
@@ -2691,7 +2704,7 @@
return;
}
closed = true;
- if (expected != -1) {
+ if (expected != -1L) {
/* not chunked */
if (written != expected) {
error = true;
--- a/jdk/src/share/classes/sun/net/www/protocol/https/HttpsURLConnectionImpl.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/sun/net/www/protocol/https/HttpsURLConnectionImpl.java Thu Nov 13 11:15:01 2008 -0800
@@ -527,6 +527,10 @@
delegate.setFixedLengthStreamingMode(contentLength);
}
+ public void setFixedLengthStreamingMode(long contentLength) {
+ delegate.setFixedLengthStreamingMode(contentLength);
+ }
+
public void setChunkedStreamingMode (int chunklen) {
delegate.setChunkedStreamingMode(chunklen);
}
--- a/jdk/src/share/classes/sun/security/jgss/GSSContextImpl.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/sun/security/jgss/GSSContextImpl.java Thu Nov 13 11:15:01 2008 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-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
@@ -284,7 +284,8 @@
ByteArrayOutputStream bos = new ByteArrayOutputStream(100);
acceptSecContext(new ByteArrayInputStream(inTok, offset, len),
bos);
- return bos.toByteArray();
+ byte[] out = bos.toByteArray();
+ return (out.length == 0) ? null : out;
}
public void acceptSecContext(InputStream inStream,
--- a/jdk/src/share/classes/sun/security/jgss/spnego/SpNegoContext.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/sun/security/jgss/spnego/SpNegoContext.java Thu Nov 13 11:15:01 2008 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2005-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
@@ -413,13 +413,14 @@
// pull out the mechanism token
byte[] accept_token = targToken.getResponseToken();
if (accept_token == null) {
- // return wth failure
- throw new GSSException(errorCode, -1,
- "mechansim token from server is null");
+ if (!isMechContextEstablished()) {
+ // return with failure
+ throw new GSSException(errorCode, -1,
+ "mechanism token from server is null");
+ }
+ } else {
+ mechToken = GSS_initSecContext(accept_token);
}
-
- mechToken = GSS_initSecContext(accept_token);
-
// verify MIC
if (!GSSUtil.useMSInterop()) {
byte[] micToken = targToken.getMechListMIC();
@@ -428,7 +429,6 @@
"verification of MIC on MechList Failed!");
}
}
-
if (isMechContextEstablished()) {
state = STATE_DONE;
retVal = mechToken;
@@ -556,9 +556,6 @@
// get the token for mechanism
byte[] accept_token = GSS_acceptSecContext(mechToken);
- if (accept_token == null) {
- valid = false;
- }
// verify MIC
if (!GSSUtil.useMSInterop() && valid) {
--- a/jdk/src/share/classes/sun/security/provider/certpath/OCSPResponse.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/classes/sun/security/provider/certpath/OCSPResponse.java Thu Nov 13 11:15:01 2008 -0800
@@ -151,6 +151,10 @@
private SingleResponse singleResponse;
+ // Maximum clock skew in milliseconds (10 minutes) allowed when checking
+ // validity of OCSP responses
+ private static final long MAX_CLOCK_SKEW = 600000;
+
// an array of all of the CRLReasons (used in SingleResponse)
private static CRLReason[] values = CRLReason.values();
@@ -583,7 +587,9 @@
}
}
- Date now = new Date();
+ long now = System.currentTimeMillis();
+ Date nowPlusSkew = new Date(now + MAX_CLOCK_SKEW);
+ Date nowMinusSkew = new Date(now - MAX_CLOCK_SKEW);
if (DEBUG != null) {
String until = "";
if (nextUpdate != null) {
@@ -593,8 +599,8 @@
thisUpdate + until);
}
// Check that the test date is within the validity interval
- if ((thisUpdate != null && now.before(thisUpdate)) ||
- (nextUpdate != null && now.after(nextUpdate))) {
+ if ((thisUpdate != null && nowPlusSkew.before(thisUpdate)) ||
+ (nextUpdate != null && nowMinusSkew.after(nextUpdate))) {
if (DEBUG != null) {
DEBUG.println("Response is unreliable: its validity " +
--- a/jdk/src/share/native/java/util/zip/zip_util.c Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/src/share/native/java/util/zip/zip_util.c Thu Nov 13 11:15:01 2008 -0800
@@ -273,8 +273,8 @@
/*
* Searches for end of central directory (END) header. The contents of
* the END header will be read and placed in endbuf. Returns the file
- * position of the END header, otherwise returns 0 if the END header
- * was not found or -1 if an error occurred.
+ * position of the END header, otherwise returns -1 if the END header
+ * was not found or an error occurred.
*/
static jlong
findEND(jzfile *zip, void *endbuf)
@@ -314,7 +314,7 @@
}
}
}
- return 0; /* END header not found */
+ return -1; /* END header not found */
}
/*
@@ -460,9 +460,8 @@
/*
* Reads zip file central directory. Returns the file position of first
- * CEN header, otherwise returns 0 if central directory not found or -1
- * if an error occurred. If zip->msg != NULL then the error was a zip
- * format error and zip->msg has the error text.
+ * CEN header, otherwise returns -1 if an error occured. If zip->msg != NULL
+ * then the error was a zip format error and zip->msg has the error text.
* Always pass in -1 for knownTotal; it's used for a recursive call.
*/
static jlong
@@ -488,9 +487,9 @@
/* Get position of END header */
if ((endpos = findEND(zip, endbuf)) == -1)
- return -1; /* system error */
+ return -1; /* no END header or system error */
- if (endpos == 0) return 0; /* END header not found */
+ if (endpos == 0) return 0; /* only END header present */
freeCEN(zip);
--- a/jdk/test/com/sun/net/httpserver/bugs/FixedLengthInputStream.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/test/com/sun/net/httpserver/bugs/FixedLengthInputStream.java Thu Nov 13 11:15:01 2008 -0800
@@ -23,7 +23,7 @@
/**
* @test
- * @bug 6756771
+ * @bug 6756771 6755625
* @summary com.sun.net.httpserver.HttpServer should handle POSTs larger than 2Gig
*/
@@ -44,34 +44,16 @@
{
static final long POST_SIZE = 4L * 1024L * 1024L * 1024L; // 4Gig
- /* Remove when CR 6755625 is fixed */
- static final String requestHeaders = ((new StringBuilder())
- .append("POST /flis/ HTTP/1.1\r\n")
- .append("User-Agent: Java/1.7.0\r\n")
- .append("Host: localhost\r\n")
- .append("Accept: text/html, image/gif, image/jpeg,")
- .append( " *; q=.2, */*; q=.2\r\n")
- .append("Content-Length: 4294967296\r\n\r\n")).toString();
-
void test(String[] args) throws IOException {
HttpServer httpServer = startHttpServer();
int port = httpServer.getAddress().getPort();
try {
- /* Uncomment & when CR 6755625 is fixed, remove socket code
URL url = new URL("http://localhost:" + port + "/flis/");
HttpURLConnection uc = (HttpURLConnection)url.openConnection();
uc.setDoOutput(true);
uc.setRequestMethod("POST");
uc.setFixedLengthStreamingMode(POST_SIZE);
OutputStream os = uc.getOutputStream();
- */
-
- Socket socket = new Socket("localhost", port);
- OutputStream os = socket.getOutputStream();
- PrintStream ps = new PrintStream(os);
- debug("Request: " + requestHeaders);
- ps.print(requestHeaders);
- ps.flush();
/* create a 32K byte array with data to POST */
int thirtyTwoK = 32 * 1024;
@@ -84,18 +66,12 @@
os.write(ba);
}
- /* Uncomment & when CR 6755625 is fixed, remove socket code
os.close();
InputStream is = uc.getInputStream();
while(is.read(ba) != -1);
is.close();
- */
- InputStream is = socket.getInputStream();
- is.read();
- socket.close();
-
- pass();
+ pass();
} finally {
httpServer.stop(0);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/security/cert/CertPathValidatorException/GetMessage.java Thu Nov 13 11:15:01 2008 -0800
@@ -0,0 +1,63 @@
+/*
+ * 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 6765046
+ * @summary check that getMessage(cause) returns cause.toString if cause != null
+ */
+
+import java.security.cert.CertPathValidatorException;
+
+public class GetMessage {
+ private static volatile boolean failed = false;
+ public static void main(String[] args) throws Exception {
+
+ Throwable[] causes = {
+ new Throwable(),
+ new Throwable("message"),
+ new Throwable("message", new Throwable()) };
+
+ for (Throwable cause: causes) {
+ CertPathValidatorException cpve =
+ new CertPathValidatorException(cause);
+
+ // from CertPathValidatorException(Throwable cause) spec:
+ // The detail message is set to (cause==null ? null : cause.toString() )
+ // (which typically contains the class and detail message of cause).
+ String expMsg = (cause == null ? null : cause.toString());
+ String actualMsg = cpve.getMessage();
+
+ boolean msgsEqual =
+ (expMsg == null ? actualMsg == null : expMsg.equals(actualMsg));
+ if (!msgsEqual) {
+ System.out.println("expected message:" + expMsg);
+ System.out.println("getMessage():" + actualMsg);
+ failed = true;
+ }
+ }
+ if (failed) {
+ throw new Exception("Some tests FAILED");
+ }
+ }
+}
--- a/jdk/test/java/util/zip/TestEmptyZip.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/test/java/util/zip/TestEmptyZip.java Thu Nov 13 11:15:01 2008 -0800
@@ -39,35 +39,24 @@
throw new Exception("failed to delete " + zipName);
}
- // Verify 0-length file cannot be read
f.createNewFile();
- ZipFile zf = null;
try {
- zf = new ZipFile(f);
- fail();
- } catch (Exception ex) {
- check(ex.getMessage().contains("zip file is empty"));
- } finally {
- if (zf != null) {
- zf.close();
- }
- }
+ // Verify 0-length file cannot be read
+ checkCannotRead(f);
- ZipInputStream zis = null;
- try {
- zis = new ZipInputStream(new FileInputStream(f));
- ZipEntry ze = zis.getNextEntry();
- check(ze == null);
- } catch (Exception ex) {
- unexpected(ex);
+ // Verify non-zip file cannot be read
+ OutputStream out = new FileOutputStream(f);
+ try {
+ out.write("class Foo { }".getBytes());
+ } finally {
+ out.close();
+ }
+ checkCannotRead(f);
+
} finally {
- if (zis != null) {
- zis.close();
- }
+ f.delete();
}
- f.delete();
-
// Verify 0-entries file can be written
write(f);
@@ -78,6 +67,29 @@
f.delete();
}
+ static void checkCannotRead(File f) throws IOException {
+ try {
+ new ZipFile(f).close();
+ fail();
+ } catch (ZipException ze) {
+ if (f.length() == 0) {
+ check(ze.getMessage().contains("zip file is empty"));
+ } else {
+ pass();
+ }
+ }
+ ZipInputStream zis = null;
+ try {
+ zis = new ZipInputStream(new FileInputStream(f));
+ ZipEntry ze = zis.getNextEntry();
+ check(ze == null);
+ } catch (IOException ex) {
+ unexpected(ex);
+ } finally {
+ if (zis != null) zis.close();
+ }
+ }
+
static void write(File f) throws Exception {
ZipOutputStream zos = null;
try {
--- a/jdk/test/javax/management/Introspector/AnnotationTest.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/test/javax/management/Introspector/AnnotationTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -39,7 +39,8 @@
/*
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 @@
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 @@
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 @@
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 @@
});
@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 @@
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 @@
}
@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 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 @@
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)
+ else
throw new Exception("TEST FAILED: " + failed);
- else
- System.out.println("Test disabled until 6221321 implemented");
}
private static void check(MBeanServer mbs, ObjectName on) throws Exception {
@@ -295,151 +433,4 @@
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);
- }
- }
- }
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/MBeanServer/AttributeListMapTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -0,0 +1,115 @@
+/*
+ * 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 6336968
+ * @summary Test AttributeList.toMap
+ * @author Eamonn McManus
+ */
+
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import javax.management.Attribute;
+import javax.management.AttributeList;
+
+public class AttributeListMapTest {
+
+ private static String failure;
+
+ public static void main(String[] args) throws Exception {
+ AttributeList attrs = new AttributeList(Arrays.asList(
+ new Attribute("Str", "Five"),
+ new Attribute("Int", 5),
+ new Attribute("Flt", 5.0)));
+
+ Map<String, Object> map = attrs.toMap();
+ final Map<String, Object> expectMap = new HashMap<String, Object>();
+ for (Attribute attr : attrs.asList())
+ expectMap.put(attr.getName(), attr.getValue());
+ assertEquals("Initial map", expectMap, map);
+ assertEquals("Initial map size", 3, map.size());
+ assertEquals("Name set", expectMap.keySet(), map.keySet());
+ assertEquals("Values", new HashSet<Object>(expectMap.values()),
+ new HashSet<Object>(map.values()));
+ assertEquals("Entry set", expectMap.entrySet(), map.entrySet());
+
+ AttributeList attrs2 = new AttributeList(map);
+ assertEquals("AttributeList from Map", attrs, attrs2);
+ // This assumes that the Map conserves the order of the attributes,
+ // which is not specified but true because we use LinkedHashMap.
+
+ // Check that toMap fails if the list contains non-Attribute elements.
+ AttributeList attrs3 = new AttributeList(attrs);
+ attrs3.add("Hello"); // allowed but curious
+ try {
+ map = attrs3.toMap();
+ fail("toMap succeeded on list with non-Attribute elements");
+ } catch (Exception e) {
+ assertEquals("Exception for toMap with non-Atttribute elements",
+ IllegalArgumentException.class, e.getClass());
+ }
+
+ // Check that the Map does not reflect changes made to the list after
+ // the Map was obtained.
+ AttributeList attrs4 = new AttributeList(attrs);
+ map = attrs4.toMap();
+ attrs4.add(new Attribute("Big", new BigInteger("5")));
+ assertEquals("Map after adding element to list", expectMap, map);
+
+ // Check that if there is more than one Attribute with the same name
+ // then toMap() chooses the last of them.
+ AttributeList attrs5 = new AttributeList(attrs);
+ attrs5.add(new Attribute("Str", "Cinq"));
+ map = attrs5.toMap();
+ assertEquals("Size of Map for list with duplicate attribute name",
+ 3, map.size());
+ Object value = map.get("Str");
+ assertEquals("Value of Str in Map for list with two values for it",
+ "Cinq", value);
+
+ if (failure == null)
+ System.out.println("TEST PASSED");
+ else
+ throw new Exception("TEST FAILED: " + failure);
+ }
+
+ private static void assertEquals(String what, Object expect, Object actual) {
+ if (eq(expect, actual))
+ System.out.println("OK: " + what);
+ else
+ fail(what + ": expected " + expect + ", got " + actual);
+ }
+
+ private static boolean eq(Object x, Object y) {
+ return (x == null) ? (y == null) : x.equals(y);
+ }
+
+ private static void fail(String why) {
+ System.out.println("FAIL: " + why);
+ failure = why;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/MBeanServer/AttributeListTypeSafeTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -0,0 +1,109 @@
+/*
+ * 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 6336968
+ * @summary Test adding non-Attribute values to an AttributeList.
+ * @author Eamonn McManus
+ */
+
+import java.util.Collections;
+import java.util.List;
+import javax.management.Attribute;
+import javax.management.AttributeList;
+
+public class AttributeListTypeSafeTest {
+
+ private static String failure;
+
+ public static void main(String[] args) throws Exception {
+ // Test calling asList after adding non-Attribute by various means
+ for (Op op : Op.values()) {
+ AttributeList alist = new AttributeList();
+ alist.add(new Attribute("foo", "bar"));
+ doOp(alist, op);
+ String what = "asList() after calling " + op + " with non-Attribute";
+ try {
+ List<Attribute> lista = alist.asList();
+ fail(what + ": succeeded but should not have");
+ } catch (IllegalArgumentException e) {
+ System.out.println("OK: " + what + ": got IllegalArgumentException");
+ }
+ }
+
+ // Test adding non-Attribute by various means after calling asList
+ for (Op op : Op.values()) {
+ AttributeList alist = new AttributeList();
+ List<Attribute> lista = alist.asList();
+ lista.add(new Attribute("foo", "bar"));
+ String what = op + " with non-Attribute after calling asList()";
+ try {
+ doOp(alist, op);
+ fail(what + ": succeeded but should not have");
+ } catch (IllegalArgumentException e) {
+ System.out.println("OK: " + what + ": got IllegalArgumentException");
+ }
+ }
+
+ if (failure == null)
+ System.out.println("TEST PASSED");
+ else
+ throw new Exception("TEST FAILED: " + failure);
+ }
+
+ private static enum Op {
+ ADD("add(Object)"), ADD_AT("add(int, Object)"),
+ ADD_ALL("add(Collection)"), ADD_ALL_AT("add(int, Collection)"),
+ SET("set(int, Object)");
+
+ private Op(String what) {
+ this.what = what;
+ }
+
+ @Override
+ public String toString() {
+ return what;
+ }
+
+ private final String what;
+ }
+
+ private static void doOp(AttributeList alist, Op op) {
+ Object x = "oops";
+ switch (op) {
+ case ADD: alist.add(x); break;
+ case ADD_AT: alist.add(0, x); break;
+ case ADD_ALL: alist.add(Collections.singleton(x)); break;
+ case ADD_ALL_AT: alist.add(0, Collections.singleton(x)); break;
+ case SET: alist.set(0, x); break;
+ default: throw new AssertionError("Case not covered");
+ }
+ }
+
+ private static void fail(String why) {
+ System.out.println("FAIL: " + why);
+ failure = why;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/context/ContextForwarderTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -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);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/context/ContextTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -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);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/context/LocaleAwareBroadcasterTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -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;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/context/LocaleTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -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);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/context/LocalizableTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -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;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/context/RemoteContextTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -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);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/context/localizable/MBeanDescriptions.properties Thu Nov 13 11:15:01 2008 -0800
@@ -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.
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/context/localizable/MBeanDescriptions_fr.java Thu Nov 13 11:15:01 2008 -0800
@@ -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},
+ };
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/context/localizable/Whatsit.java Thu Nov 13 11:15:01 2008 -0800
@@ -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;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/context/localizable/WhatsitMBean.java Thu Nov 13 11:15:01 2008 -0800
@@ -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);
+}
--- a/jdk/test/javax/management/eventService/CustomForwarderTest.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/test/javax/management/eventService/CustomForwarderTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -200,8 +200,7 @@
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
--- a/jdk/test/javax/management/eventService/EventClientExecutorTest.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/test/javax/management/eventService/EventClientExecutorTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -65,8 +65,7 @@
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);
--- a/jdk/test/javax/management/eventService/EventManagerTest.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/test/javax/management/eventService/EventManagerTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -98,7 +98,7 @@
succeed &= test(new EventClient(ecd,
new RMIPushEventRelay(ecd),
null, null,
- EventClient.DEFAULT_LEASE_TIMEOUT));
+ EventClient.DEFAULT_REQUESTED_LEASE_TIME));
conn.close();
server.stop();
--- a/jdk/test/javax/management/eventService/ListenerTest.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/test/javax/management/eventService/ListenerTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -99,7 +99,7 @@
succeed &= test(new EventClient(ecd,
new RMIPushEventRelay(ecd),
null, null,
- EventClient.DEFAULT_LEASE_TIMEOUT));
+ EventClient.DEFAULT_REQUESTED_LEASE_TIME));
conn.close();
server.stop();
--- a/jdk/test/javax/management/eventService/NotSerializableNotifTest.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/test/javax/management/eventService/NotSerializableNotifTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -95,7 +95,7 @@
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();
--- a/jdk/test/javax/management/eventService/UsingEventService.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/test/javax/management/eventService/UsingEventService.java Thu Nov 13 11:15:01 2008 -0800
@@ -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
--- a/jdk/test/javax/management/namespace/EventWithNamespaceControlTest.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/test/javax/management/namespace/EventWithNamespaceControlTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -85,6 +85,7 @@
}
}
+ @Override
public Map<String, ?> getServerMap() {
Map<String, ?> retValue = Collections.emptyMap();
return retValue;
--- a/jdk/test/javax/management/namespace/JMXNamespaceSecurityTest.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/test/javax/management/namespace/JMXNamespaceSecurityTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -51,6 +51,7 @@
import javax.management.namespace.JMXNamespace;
import javax.management.namespace.JMXNamespaces;
import javax.management.remote.JMXConnectorServer;
+import javax.management.ClientContext;
/**
*
--- a/jdk/test/javax/management/namespace/JMXNamespaceViewTest.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/test/javax/management/namespace/JMXNamespaceViewTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -42,6 +42,7 @@
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 @@
*/
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
*/
--- a/jdk/test/javax/management/namespace/JMXRemoteTargetNamespace.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/test/javax/management/namespace/JMXRemoteTargetNamespace.java Thu Nov 13 11:15:01 2008 -0800
@@ -68,13 +68,7 @@
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 @@
}
@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;
}
--- a/jdk/test/javax/management/namespace/NamespaceNotificationsTest.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/test/javax/management/namespace/NamespaceNotificationsTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -206,18 +206,10 @@
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());
--- a/jdk/test/javax/management/namespace/NullDomainObjectNameTest.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/test/javax/management/namespace/NullDomainObjectNameTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -155,7 +155,7 @@
// namespace.
//
RoutingServerProxy proxy =
- new RoutingServerProxy(sub, "", "faked", false);
+ new RoutingServerProxy(sub, "", "faked", true);
// These should fail because the ObjectName doesn't start
// with "faked//"
--- a/jdk/test/javax/management/namespace/NullObjectNameTest.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/test/javax/management/namespace/NullObjectNameTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -162,7 +162,7 @@
// 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()+
--- a/jdk/test/javax/management/openmbean/CompositeDataStringTest.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/test/javax/management/openmbean/CompositeDataStringTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -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 {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/openmbean/CompositeDataToMapTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -0,0 +1,116 @@
+/*
+ * 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 6750472 6752563
+ * @summary Test CompositeDataSupport.toMap.
+ * @author Eamonn McManus
+ * @run main/othervm -ea CompositeDataToMapTest
+ */
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+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 CompositeDataToMapTest {
+ private static class IdentityInvocationHandler implements InvocationHandler {
+ private final Object wrapped;
+
+ public IdentityInvocationHandler(Object wrapped) {
+ this.wrapped = wrapped;
+ }
+
+ public Object invoke(Object proxy, Method m, Object[] args)
+ throws Throwable {
+ try {
+ return m.invoke(wrapped, args);
+ } catch (InvocationTargetException e) {
+ throw e.getCause();
+ }
+ }
+ }
+
+ private static <T> T wrap(T x, Class<T> intf) {
+ InvocationHandler ih = new IdentityInvocationHandler(x);
+ return intf.cast(Proxy.newProxyInstance(
+ intf.getClassLoader(), new Class<?>[] {intf}, ih));
+ }
+
+ public static void main(String[] args) throws Exception {
+ if (!CompositeDataToMapTest.class.desiredAssertionStatus())
+ throw new AssertionError("Must be run with -ea");
+
+ CompositeType emptyCT = new CompositeType(
+ "empty", "empty", new String[0], new String[0], new OpenType<?>[0]);
+ CompositeData emptyCD = new CompositeDataSupport(
+ emptyCT, Collections.<String, Object>emptyMap());
+ assert CompositeDataSupport.toMap(emptyCD).isEmpty() :
+ "Empty CD produces empty Map";
+
+ CompositeData emptyCD2 = new CompositeDataSupport(
+ emptyCT, new String[0], new Object[0]);
+ assert emptyCD.equals(emptyCD2) : "Empty CD can be constructed two ways";
+
+ CompositeType namedNumberCT = new CompositeType(
+ "NamedNumber", "NamedNumber",
+ new String[] {"name", "number"},
+ new String[] {"name", "number"},
+ new OpenType<?>[] {SimpleType.STRING, SimpleType.INTEGER});
+ Map<String, Object> namedNumberMap = new HashMap<String, Object>();
+ namedNumberMap.put("name", "Deich");
+ namedNumberMap.put("number", 10);
+ CompositeData namedNumberCD = new CompositeDataSupport(
+ namedNumberCT, namedNumberMap);
+ assert CompositeDataSupport.toMap(namedNumberCD).equals(namedNumberMap) :
+ "Map survives passage through CompositeData";
+
+ namedNumberCD = wrap(namedNumberCD, CompositeData.class);
+ assert CompositeDataSupport.toMap(namedNumberCD).equals(namedNumberMap) :
+ "Map survives passage through wrapped CompositeData";
+
+ namedNumberMap = CompositeDataSupport.toMap(namedNumberCD);
+ namedNumberMap.put("name", "Ceathar");
+ namedNumberMap.put("number", 4);
+ namedNumberCD = new CompositeDataSupport(namedNumberCT, namedNumberMap);
+ assert CompositeDataSupport.toMap(namedNumberCD).equals(namedNumberMap) :
+ "Modified Map survives passage through CompositeData";
+
+ try {
+ namedNumberMap = CompositeDataSupport.toMap(null);
+ assert false : "Null toMap arg provokes exception";
+ } catch (Exception e) {
+ assert e instanceof IllegalArgumentException :
+ "Exception for null toMap arg is IllegalArgumentException";
+ }
+ }
+}
--- a/jdk/test/javax/management/remote/mandatory/connectorServer/ForwarderChainTest.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/test/javax/management/remote/mandatory/connectorServer/ForwarderChainTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -86,16 +86,20 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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);
--- a/jdk/test/javax/management/remote/mandatory/connectorServer/StandardForwardersTest.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/test/javax/management/remote/mandatory/connectorServer/StandardForwardersTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -26,6 +26,7 @@
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 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 @@
}
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 @@
// 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) {
--- a/jdk/test/javax/management/remote/mandatory/provider/ProviderTest.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/test/javax/management/remote/mandatory/provider/ProviderTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -46,6 +46,8 @@
/*
* 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 @@
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);
}
--- a/jdk/test/javax/management/remote/mandatory/subjectDelegation/SimpleStandard.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/test/javax/management/remote/mandatory/subjectDelegation/SimpleStandard.java Thu Nov 13 11:15:01 2008 -0800
@@ -75,7 +75,7 @@
* @return the current value of the "State" attribute.
*/
public String getState() {
- checkSubject();
+ checkSubject("getState");
return state;
}
@@ -85,7 +85,7 @@
* @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 @@
* @return the current value of the "NbChanges" attribute.
*/
public int getNbChanges() {
- checkSubject();
+ checkSubject("getNbChanges");
return nbChanges;
}
@@ -106,7 +106,7 @@
* attributes of the "SimpleStandard" standard MBean.
*/
public void reset() {
- checkSubject();
+ checkSubject("reset");
AttributeChangeNotification acn =
new AttributeChangeNotification(this,
0,
@@ -149,18 +149,18 @@
* 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);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/remote/mandatory/version/JMXSpecVersionTest.java Thu Nov 13 11:15:01 2008 -0800
@@ -0,0 +1,308 @@
+/*
+ * 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 6750008
+ * @summary Test JMX.getSpecificationVersion
+ * @author Eamonn McManus
+ */
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.ListIterator;
+import java.util.Set;
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.DynamicMBean;
+import javax.management.InstanceNotFoundException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.JMX;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.MBeanServerDelegate;
+import javax.management.MBeanServerDelegateMBean;
+import javax.management.MBeanServerFactory;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.management.StandardMBean;
+import javax.management.namespace.JMXNamespace;
+import javax.management.namespace.JMXNamespaces;
+import javax.management.namespace.MBeanServerSupport;
+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 JMXSpecVersionTest {
+ private static String failure;
+ private static final Object POISON_PILL = new Object();
+
+ private static class FakeDelegate implements DynamicMBean {
+ private final Object specVersion;
+ private final DynamicMBean delegate = new StandardMBean(
+ new MBeanServerDelegate(), MBeanServerDelegateMBean.class, false);
+
+ FakeDelegate(Object specVersion) {
+ this.specVersion = specVersion;
+ }
+
+ public Object getAttribute(String attribute)
+ throws AttributeNotFoundException, MBeanException,
+ ReflectionException {
+ if ("SpecificationVersion".equals(attribute)) {
+ if (specVersion == POISON_PILL)
+ throw new AttributeNotFoundException(attribute);
+ else
+ return specVersion;
+ } else
+ return delegate.getAttribute(attribute);
+ }
+
+ public void setAttribute(Attribute attribute)
+ throws AttributeNotFoundException, InvalidAttributeValueException,
+ MBeanException, ReflectionException {
+ delegate.setAttribute(attribute);
+ }
+
+ public AttributeList getAttributes(String[] attributes) {
+ AttributeList list = delegate.getAttributes(attributes);
+ for (ListIterator<Attribute> it = list.asList().listIterator();
+ it.hasNext(); ) {
+ Attribute attr = it.next();
+ if (attr.getName().equals("SpecificationVersion")) {
+ it.remove();
+ if (specVersion != POISON_PILL) {
+ attr = new Attribute(attr.getName(), specVersion);
+ it.add(attr);
+ }
+ }
+ }
+ return list;
+ }
+
+ public AttributeList setAttributes(AttributeList attributes) {
+ return delegate.setAttributes(attributes);
+ }
+
+ public Object invoke(String actionName, Object[] params,
+ String[] signature) throws MBeanException,
+ ReflectionException {
+ return delegate.invoke(actionName, params, signature);
+ }
+
+ public MBeanInfo getMBeanInfo() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+ }
+
+ private static class MBeanServerWithVersion extends MBeanServerSupport {
+ private final DynamicMBean delegate;
+
+ public MBeanServerWithVersion(Object specVersion) {
+ this.delegate = new FakeDelegate(specVersion);
+ }
+
+ @Override
+ public DynamicMBean getDynamicMBeanFor(ObjectName name)
+ throws InstanceNotFoundException {
+ if (MBeanServerDelegate.DELEGATE_NAME.equals(name))
+ return delegate;
+ else
+ throw new InstanceNotFoundException(name);
+ }
+
+ @Override
+ protected Set<ObjectName> getNames() {
+ return Collections.singleton(MBeanServerDelegate.DELEGATE_NAME);
+ }
+ }
+
+ private static class EmptyMBeanServer extends MBeanServerSupport {
+ @Override
+ public DynamicMBean getDynamicMBeanFor(ObjectName name) throws InstanceNotFoundException {
+ throw new InstanceNotFoundException(name);
+ }
+
+ @Override
+ protected Set<ObjectName> getNames() {
+ return Collections.emptySet();
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ MBeanServer mbs = MBeanServerFactory.newMBeanServer();
+ JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///");
+ JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(
+ url, null, mbs);
+ cs.start();
+
+ String realVersion = (String) mbs.getAttribute(
+ MBeanServerDelegate.DELEGATE_NAME, "SpecificationVersion");
+ assertEquals("Reported local version",
+ realVersion, JMX.getSpecificationVersion(mbs, null));
+ assertEquals("Reported local version >= \"2.0\"",
+ true, (realVersion.compareTo("2.0") >= 0));
+
+ JMXConnector cc = JMXConnectorFactory.connect(cs.getAddress());
+ MBeanServerConnection mbsc = cc.getMBeanServerConnection();
+ assertEquals("Reported remote version",
+ realVersion, JMX.getSpecificationVersion(mbsc, null));
+
+ cc.close();
+ try {
+ String brokenVersion = JMX.getSpecificationVersion(mbsc, null);
+ fail("JMX.getSpecificationVersion succeded over closed connection" +
+ " (returned " + brokenVersion + ")");
+ } catch (Exception e) {
+ assertEquals("Exception for closed connection",
+ IOException.class, e.getClass());
+ }
+
+ try {
+ String brokenVersion = JMX.getSpecificationVersion(
+ new EmptyMBeanServer(), null);
+ fail("JMX.getSpecificationVersion succeded with empty MBean Server" +
+ " (returned " + brokenVersion + ")");
+ } catch (Exception e) {
+ assertEquals("Exception for empty MBean Server",
+ IOException.class, e.getClass());
+ }
+
+ try {
+ String brokenVersion = JMX.getSpecificationVersion(null, null);
+ fail("JMX.getSpecificationVersion succeded with null MBean Server" +
+ " (returned " + brokenVersion + ")");
+ } catch (Exception e) {
+ assertEquals("Exception for null MBean Server",
+ IllegalArgumentException.class, e.getClass());
+ }
+
+ MBeanServer mbs1_2 = new MBeanServerWithVersion("1.2");
+ String version1_2 = JMX.getSpecificationVersion(mbs1_2, null);
+ assertEquals("Version for 1.2 MBean Server", "1.2", version1_2);
+
+ // It's completely nutty for an MBean Server to return null as the
+ // value of its spec version, and we don't actually say what happens
+ // in that case, but in fact we return the null to the caller.
+ MBeanServer mbs_null = new MBeanServerWithVersion(null);
+ String version_null = JMX.getSpecificationVersion(mbs_null, null);
+ assertEquals("Version for MBean Server that declares null spec version",
+ null, version_null);
+
+ try {
+ MBeanServer mbs1_2_float = new MBeanServerWithVersion(1.2f);
+ String version1_2_float =
+ JMX.getSpecificationVersion(mbs1_2_float, null);
+ fail("JMX.getSpecificationVersion succeeded with version 1.2f" +
+ " (returned " + version1_2_float + ")");
+ } catch (Exception e) {
+ assertEquals("Exception for non-string version (1.2f)",
+ IOException.class, e.getClass());
+ }
+
+ try {
+ MBeanServer mbs_missing = new MBeanServerWithVersion(POISON_PILL);
+ String version_missing =
+ JMX.getSpecificationVersion(mbs_missing, null);
+ fail("JMX.getSpecificationVersion succeeded with null version" +
+ " (returned " + version_missing + ")");
+ } catch (Exception e) {
+ assertEquals("Exception for missing version",
+ IOException.class, e.getClass());
+ }
+
+ ObjectName wildcardNamespaceName = new ObjectName("foo//*//bar//baz:k=v");
+ try {
+ String brokenVersion =
+ JMX.getSpecificationVersion(mbsc, wildcardNamespaceName);
+ fail("JMX.getSpecificationVersion succeeded with wildcard namespace" +
+ " (returned " + brokenVersion + ")");
+ } catch (Exception e) {
+ assertEquals("Exception for wildcard namespace",
+ IllegalArgumentException.class, e.getClass());
+ }
+
+ String sub1_2namespace = "blibby";
+ JMXNamespace sub1_2 = new JMXNamespace(mbs1_2);
+ ObjectName sub1_2name =
+ JMXNamespaces.getNamespaceObjectName(sub1_2namespace);
+ mbs.registerMBean(sub1_2, sub1_2name);
+ String sub1_2namespaceHandlerVersion =
+ JMX.getSpecificationVersion(mbs, sub1_2name);
+ assertEquals("Spec version of namespace handler",
+ realVersion, sub1_2namespaceHandlerVersion);
+ // The namespace handler is in the top-level namespace so its
+ // version should not be 1.2.
+
+ for (String nameInSub : new String[] {"*:*", "d:k=v"}) {
+ ObjectName subName = new ObjectName(sub1_2namespace + "//" + nameInSub);
+ String subVersion = JMX.getSpecificationVersion(mbs, subName);
+ assertEquals("Spec version in 1.2 namespace (" + nameInSub + ")",
+ "1.2", subVersion);
+ }
+
+ mbs.unregisterMBean(sub1_2name);
+ for (String noSuchNamespace : new String[] {
+ sub1_2namespace + "//*:*", sub1_2namespace + "//d:k=v",
+ }) {
+ try {
+ String brokenVersion = JMX.getSpecificationVersion(
+ mbs, new ObjectName(noSuchNamespace));
+ fail("JMX.getSpecificationVersion succeeded with missing " +
+ "namespace (" + noSuchNamespace + " -> " +
+ brokenVersion);
+ } catch (Exception e) {
+ assertEquals("Exception for missing namespace",
+ IOException.class, e.getClass());
+ }
+ }
+
+ if (failure != null)
+ throw new Exception("TEST FAILED: " + failure);
+ System.out.println("TEST PASSED");
+ }
+
+ private static void assertEquals(String what, Object expect, Object actual) {
+ if (equal(expect, actual))
+ System.out.println("OK: " + what + ": " + expect);
+ else
+ fail(what + ": expected " + expect + ", got " + actual);
+ }
+
+ private static boolean equal(Object x, Object y) {
+ if (x == null)
+ return (y == null);
+ else
+ return x.equals(y);
+ }
+
+ private static void fail(String why) {
+ System.out.println("FAILED: " + why);
+ failure = why;
+ }
+}
--- a/jdk/test/sun/security/krb5/auto/Context.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/test/sun/security/krb5/auto/Context.java Thu Nov 13 11:15:01 2008 -0800
@@ -109,13 +109,22 @@
out.s = new Subject();
Krb5LoginModule krb5 = new Krb5LoginModule();
Map<String, String> map = new HashMap<String, String>();
- map.put("tryFirstPass", "true");
+ Map<String, Object> shared = new HashMap<String, Object>();
+
+ if (pass != null) {
+ map.put("useFirstPass", "true");
+ shared.put("javax.security.auth.login.name", user);
+ shared.put("javax.security.auth.login.password", pass);
+ } else {
+ map.put("doNotPrompt", "true");
+ map.put("useTicketCache", "true");
+ if (user != null) {
+ map.put("principal", user);
+ }
+ }
if (storeKey) {
map.put("storeKey", "true");
}
- Map<String, Object> shared = new HashMap<String, Object>();
- shared.put("javax.security.auth.login.name", user);
- shared.put("javax.security.auth.login.password", pass);
krb5.initialize(out.s, null, shared, map);
krb5.login();
@@ -360,6 +369,10 @@
if (me.x.isEstablished()) {
me.f = true;
System.out.println(c.name + " side established");
+ if (input != null) {
+ throw new Exception("Context established but " +
+ "still receive token at " + c.name);
+ }
return null;
} else {
System.out.println(c.name + " call initSecContext");
@@ -374,6 +387,10 @@
if (me.x.isEstablished()) {
me.f = true;
System.out.println(s.name + " side established");
+ if (input != null) {
+ throw new Exception("Context established but " +
+ "still receive token at " + s.name);
+ }
return null;
} else {
System.out.println(s.name + " called acceptSecContext");
--- a/jdk/test/sun/security/krb5/auto/KDC.java Wed Jul 05 16:43:43 2017 +0200
+++ b/jdk/test/sun/security/krb5/auto/KDC.java Thu Nov 13 11:15:01 2008 -0800
@@ -32,6 +32,7 @@
import java.util.concurrent.*;
import sun.security.krb5.*;
import sun.security.krb5.internal.*;
+import sun.security.krb5.internal.ccache.CredentialsCache;
import sun.security.krb5.internal.crypto.KeyUsage;
import sun.security.krb5.internal.ktab.KeyTab;
import sun.security.util.DerInputStream;
@@ -765,7 +766,29 @@
DerOutputStream out = new DerOutputStream();
out.write(DerValue.createTag(DerValue.TAG_APPLICATION,
true, (byte)Krb5.KRB_AS_REP), asRep.asn1Encode());
- return out.toByteArray();
+ byte[] result = out.toByteArray();
+
+ // Added feature:
+ // Write the current issuing TGT into a ccache file specified
+ // by the system property below.
+ String ccache = System.getProperty("test.kdc.save.ccache");
+ if (ccache != null) {
+ asRep.encKDCRepPart = enc_part;
+ sun.security.krb5.internal.ccache.Credentials credentials =
+ new sun.security.krb5.internal.ccache.Credentials(asRep);
+ asReq.reqBody.cname.setRealm(getRealm());
+ CredentialsCache cache =
+ CredentialsCache.create(asReq.reqBody.cname, ccache);
+ if (cache == null) {
+ throw new IOException("Unable to create the cache file " +
+ ccache);
+ }
+ cache.update(credentials);
+ cache.save();
+ new File(ccache).deleteOnExit();
+ }
+
+ return result;
} catch (KrbException ke) {
ke.printStackTrace(System.out);
KRBError kerr = ke.getError();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/auto/LoginModuleOptions.java Thu Nov 13 11:15:01 2008 -0800
@@ -0,0 +1,184 @@
+/*
+ * 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 6765491
+ * @summary Krb5LoginModule a little too restrictive, and the doc is not clear.
+ */
+import com.sun.security.auth.module.Krb5LoginModule;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+public class LoginModuleOptions {
+
+ private static final String NAME = "javax.security.auth.login.name";
+ private static final String PWD = "javax.security.auth.login.password";
+
+ public static void main(String[] args) throws Exception {
+ OneKDC kdc = new OneKDC(null);
+ kdc.addPrincipal("foo", "bar".toCharArray());
+ kdc.writeKtab(OneKDC.KTAB); // rewrite to add foo
+
+ // All 4 works: keytab, shared state, callback, cache
+ login(null, "useKeyTab", "true", "principal", "dummy");
+ login(null, "tryFirstPass", "true", NAME, OneKDC.USER,
+ PWD, OneKDC.PASS);
+ System.setProperty("test.kdc.save.ccache", "krbcc");
+ login(new MyCallback(OneKDC.USER, OneKDC.PASS)); // save the cache
+ System.clearProperty("test.kdc.save.ccache");
+ login(null, "useTicketCache", "true", "ticketCache", "krbcc");
+
+ // Fallbacks
+ // 1. ccache -> keytab
+ login(null, "useTicketCache", "true", "ticketCache", "krbcc_non_exists",
+ "useKeyTab", "true", "principal", "dummy");
+ // 2. keytab -> shared
+ login(null, "useKeyTab", "true", "principal", "dummy",
+ "keyTab", "ktab_non_exist",
+ "tryFirstPass", "true", NAME, OneKDC.USER, PWD, OneKDC.PASS);
+ // 3. shared -> callback
+ // 3.1. useFirstPass, no callback
+ boolean failed = false;
+ try {
+ login(new MyCallback(OneKDC.USER, OneKDC.PASS),
+ "useFirstPass", "true",
+ NAME, OneKDC.USER, PWD, "haha".toCharArray());
+ } catch (Exception e) {
+ failed = true;
+ }
+ if (!failed) {
+ throw new Exception("useFirstPass should not fallback to callback");
+ }
+ // 3.2. tryFirstPass, has callback
+ login(new MyCallback(OneKDC.USER, OneKDC.PASS),
+ "tryFirstPass", "true",
+ NAME, OneKDC.USER, PWD, "haha".toCharArray());
+
+ // Preferences of type
+ // 1. ccache preferred to keytab
+ login(new MyCallback("foo", null),
+ "useTicketCache", "true", "ticketCache", "krbcc",
+ "useKeyTab", "true");
+ // 2. keytab preferred to shared. This test case is not exactly correct,
+ // because principal=dummy would shadow the PWD setting in the shared
+ // state. So by only looking at the final authentication user name
+ // (which is how this program does), there's no way to tell if keyTab
+ // is picked first, or shared is tried first but fallback to keytab.
+ login(null, "useKeyTab", "true", "principal", "dummy",
+ "tryFirstPass", "true", NAME, "foo", PWD, "bar".toCharArray());
+ // 3. shared preferred to callback
+ login(new MyCallback("foo", "bar".toCharArray()),
+ "tryFirstPass", "true", NAME, OneKDC.USER, PWD, OneKDC.PASS);
+
+ // Preferences of username
+ // 1. principal preferred to NAME (NAME can be wrong or missing)
+ login(null, "principal", OneKDC.USER,
+ "tryFirstPass", "true", NAME, "someone_else", PWD, OneKDC.PASS);
+ login(null, "principal", OneKDC.USER,
+ "tryFirstPass", "true", PWD, OneKDC.PASS);
+ // 2. NAME preferred to callback
+ login(new MyCallback("someone_else", OneKDC.PASS),
+ "principal", OneKDC.USER);
+ // 3. With tryFirstPass, NAME preferred to callback
+ login(new MyCallback("someone_else", null),
+ "tryFirstPass", "true", NAME, OneKDC.USER, PWD, OneKDC.PASS);
+ // 3.1. you must provide a NAME (when there's no principal)
+ failed = false;
+ try {
+ login(new MyCallback(OneKDC.USER, null),
+ "tryFirstPass", "true", PWD, OneKDC.PASS);
+ } catch (Exception e) {
+ failed = true;
+ }
+ if (!failed) {
+ throw new Exception("useFirstPass must provide a NAME");
+ }
+ // 3.2 Hybrid, you can use NAME as "", and provide it using callback.
+ // I don't think this is designed.
+ login(new MyCallback(OneKDC.USER, null),
+ "tryFirstPass", "true", NAME, "", PWD, OneKDC.PASS);
+
+ // Test for the bug fix: doNotPrompt can be true if tryFirstPass=true
+ login(null, "doNotPrompt", "true", "storeKey", "true",
+ "tryFirstPass", "true", NAME, OneKDC.USER, PWD, OneKDC.PASS);
+ }
+
+ static void login(CallbackHandler callback, Object... options)
+ throws Exception {
+ Krb5LoginModule krb5 = new Krb5LoginModule();
+ Subject subject = new Subject();
+ Map<String, String> map = new HashMap<String, String>();
+ Map<String, Object> shared = new HashMap<String, Object>();
+
+ int count = options.length / 2;
+ for (int i = 0; i < count; i++) {
+ String key = (String) options[2 * i];
+ Object value = options[2 * i + 1];
+ if (key.startsWith("javax")) {
+ shared.put(key, value);
+ } else {
+ map.put(key, (String) value);
+ }
+ }
+ krb5.initialize(subject, callback, shared, map);
+ krb5.login();
+ krb5.commit();
+ if (!subject.getPrincipals().iterator().next()
+ .getName().startsWith(OneKDC.USER)) {
+ throw new Exception("The authenticated is not " + OneKDC.USER);
+ }
+ }
+
+ static class MyCallback implements CallbackHandler {
+
+ private String name;
+ private char[] password;
+
+ public MyCallback(String name, char[] password) {
+ this.name = name;
+ this.password = password;
+ }
+
+ public void handle(Callback[] callbacks) {
+ for (Callback callback : callbacks) {
+ System.err.println(callback);
+ if (callback instanceof NameCallback) {
+ System.err.println("name is " + name);
+ ((NameCallback) callback).setName(name);
+ }
+ if (callback instanceof PasswordCallback) {
+ System.err.println("pass is " + new String(password));
+ ((PasswordCallback) callback).setPassword(password);
+ }
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/auto/NonMutualSpnego.java Thu Nov 13 11:15:01 2008 -0800
@@ -0,0 +1,58 @@
+/*
+ * 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 6733095
+ * @summary Failure when SPNEGO request non-Mutual
+ */
+
+import sun.security.jgss.GSSUtil;
+
+public class NonMutualSpnego {
+
+ public static void main(String[] args)
+ throws Exception {
+
+ // Create and start the KDC
+ new OneKDC(null).writeJAASConf();
+ new NonMutualSpnego().go();
+ }
+
+ void go() throws Exception {
+ Context c = Context.fromJAAS("client");
+ Context s = Context.fromJAAS("server");
+
+ c.startAsClient(OneKDC.SERVER, GSSUtil.GSS_SPNEGO_MECH_OID);
+ c.x().requestMutualAuth(false);
+ s.startAsServer(GSSUtil.GSS_SPNEGO_MECH_OID);
+
+ Context.handshake(c, s);
+
+ Context.transmit("i say high --", c, s);
+ Context.transmit(" you say low", s, c);
+
+ c.dispose();
+ s.dispose();
+ }
+}