提交 6b491fa1 编写于 作者: T tbell

Merge

...@@ -69,9 +69,9 @@ public class ServiceName { ...@@ -69,9 +69,9 @@ public class ServiceName {
/** /**
* The version of the JMX specification implemented by this product. * The version of the JMX specification implemented by this product.
* <BR> * <BR>
* The value is <CODE>1.4</CODE>. * The value is <CODE>2.0</CODE>.
*/ */
public static final String JMX_SPEC_VERSION = "1.4"; public static final String JMX_SPEC_VERSION = "2.0";
/** /**
* The vendor of the JMX specification implemented by this product. * The vendor of the JMX specification implemented by this product.
......
...@@ -41,7 +41,7 @@ public class EventParams { ...@@ -41,7 +41,7 @@ public class EventParams {
@SuppressWarnings("cast") // cast for jdk 1.5 @SuppressWarnings("cast") // cast for jdk 1.5
public static long getLeaseTimeout() { public static long getLeaseTimeout() {
long timeout = EventClient.DEFAULT_LEASE_TIMEOUT; long timeout = EventClient.DEFAULT_REQUESTED_LEASE_TIME;
try { try {
final GetPropertyAction act = final GetPropertyAction act =
new GetPropertyAction(DEFAULT_LEASE_TIMEOUT); new GetPropertyAction(DEFAULT_LEASE_TIMEOUT);
......
...@@ -29,6 +29,7 @@ import com.sun.jmx.remote.util.ClassLogger; ...@@ -29,6 +29,7 @@ import com.sun.jmx.remote.util.ClassLogger;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
...@@ -143,9 +144,10 @@ public class LeaseManager { ...@@ -143,9 +144,10 @@ public class LeaseManager {
private final Runnable callback; private final Runnable callback;
private ScheduledFuture<?> scheduled; // If null, the lease has expired. private ScheduledFuture<?> scheduled; // If null, the lease has expired.
private static final ThreadFactory threadFactory =
new DaemonThreadFactory("JMX LeaseManager %d");
private final ScheduledExecutorService executor private final ScheduledExecutorService executor
= Executors.newScheduledThreadPool(1, = Executors.newScheduledThreadPool(1, threadFactory);
new DaemonThreadFactory("JMX LeaseManager %d"));
private static final ClassLogger logger = private static final ClassLogger logger =
new ClassLogger("javax.management.event", "LeaseManager"); new ClassLogger("javax.management.event", "LeaseManager");
......
...@@ -55,9 +55,19 @@ import javax.management.namespace.JMXNamespaces; ...@@ -55,9 +55,19 @@ import javax.management.namespace.JMXNamespaces;
import javax.management.namespace.MBeanServerSupport; import javax.management.namespace.MBeanServerSupport;
import javax.management.remote.IdentityMBeanServerForwarder; import javax.management.remote.IdentityMBeanServerForwarder;
/**
* <p>An {@link MBeanServerForwarder} that simulates the existence of a
* given MBean. Requests for that MBean, call it X, are intercepted by the
* forwarder, and requests for any other MBean are forwarded to the next
* forwarder in the chain. Requests such as queryNames which can span both the
* X and other MBeans are handled by merging the results for X with the results
* from the next forwarder, unless the "visible" parameter is false, in which
* case X is invisible to such requests.</p>
*/
public class SingleMBeanForwarder extends IdentityMBeanServerForwarder { public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
private final ObjectName mbeanName; private final ObjectName mbeanName;
private final boolean visible;
private DynamicMBean mbean; private DynamicMBean mbean;
private MBeanServer mbeanMBS = new MBeanServerSupport() { private MBeanServer mbeanMBS = new MBeanServerSupport() {
...@@ -85,10 +95,20 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder { ...@@ -85,10 +95,20 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
return null; return null;
} }
// This will only be called if mbeanName has an empty domain.
// In that case a getAttribute (e.g.) of that name will have the
// domain replaced by MBeanServerSupport with the default domain,
// so we must be sure that the default domain is empty too.
@Override
public String getDefaultDomain() {
return mbeanName.getDomain();
}
}; };
public SingleMBeanForwarder(ObjectName mbeanName, DynamicMBean mbean) { public SingleMBeanForwarder(
ObjectName mbeanName, DynamicMBean mbean, boolean visible) {
this.mbeanName = mbeanName; this.mbeanName = mbeanName;
this.visible = visible;
setSingleMBean(mbean); setSingleMBean(mbean);
} }
...@@ -213,8 +233,10 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder { ...@@ -213,8 +233,10 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
@Override @Override
public String[] getDomains() { public String[] getDomains() {
TreeSet<String> domainSet = String[] domains = super.getDomains();
new TreeSet<String>(Arrays.asList(super.getDomains())); if (!visible)
return domains;
TreeSet<String> domainSet = new TreeSet<String>(Arrays.asList(domains));
domainSet.add(mbeanName.getDomain()); domainSet.add(mbeanName.getDomain());
return domainSet.toArray(new String[domainSet.size()]); return domainSet.toArray(new String[domainSet.size()]);
} }
...@@ -222,7 +244,7 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder { ...@@ -222,7 +244,7 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
@Override @Override
public Integer getMBeanCount() { public Integer getMBeanCount() {
Integer count = super.getMBeanCount(); Integer count = super.getMBeanCount();
if (!super.isRegistered(mbeanName)) if (visible && !super.isRegistered(mbeanName))
count++; count++;
return count; return count;
} }
...@@ -284,7 +306,7 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder { ...@@ -284,7 +306,7 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
*/ */
private boolean applies(ObjectName pattern) { private boolean applies(ObjectName pattern) {
// we know pattern is not null. // we know pattern is not null.
if (!pattern.apply(mbeanName)) if (!visible || !pattern.apply(mbeanName))
return false; return false;
final String dompat = pattern.getDomain(); final String dompat = pattern.getDomain();
...@@ -306,22 +328,26 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder { ...@@ -306,22 +328,26 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
@Override @Override
public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) { public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
Set<ObjectInstance> names = super.queryMBeans(name, query); Set<ObjectInstance> names = super.queryMBeans(name, query);
if (visible) {
if (name == null || applies(name) ) { if (name == null || applies(name) ) {
// Don't assume mbs.queryNames returns a writable set. // Don't assume mbs.queryNames returns a writable set.
names = Util.cloneSet(names); names = Util.cloneSet(names);
names.addAll(mbeanMBS.queryMBeans(name, query)); names.addAll(mbeanMBS.queryMBeans(name, query));
} }
}
return names; return names;
} }
@Override @Override
public Set<ObjectName> queryNames(ObjectName name, QueryExp query) { public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
Set<ObjectName> names = super.queryNames(name, query); Set<ObjectName> names = super.queryNames(name, query);
if (visible) {
if (name == null || applies(name)) { if (name == null || applies(name)) {
// Don't assume mbs.queryNames returns a writable set. // Don't assume mbs.queryNames returns a writable set.
names = Util.cloneSet(names); names = Util.cloneSet(names);
names.addAll(mbeanMBS.queryNames(name, query)); names.addAll(mbeanMBS.queryNames(name, query));
} }
}
return names; return names;
} }
......
...@@ -122,7 +122,7 @@ public final class JmxMBeanServer ...@@ -122,7 +122,7 @@ public final class JmxMBeanServer
* {@link javax.management.MBeanServerFactory#newMBeanServer(java.lang.String)} * {@link javax.management.MBeanServerFactory#newMBeanServer(java.lang.String)}
* instead. * instead.
* <p> * <p>
* By default, {@link MBeanServerInterceptor} are disabled. Use * By default, interceptors are disabled. Use
* {@link #JmxMBeanServer(java.lang.String,javax.management.MBeanServer,javax.management.MBeanServerDelegate,boolean)} to enable them. * {@link #JmxMBeanServer(java.lang.String,javax.management.MBeanServer,javax.management.MBeanServerDelegate,boolean)} to enable them.
* </ul> * </ul>
* @param domain The default domain name used by this MBeanServer. * @param domain The default domain name used by this MBeanServer.
...@@ -239,7 +239,7 @@ public final class JmxMBeanServer ...@@ -239,7 +239,7 @@ public final class JmxMBeanServer
this.mBeanServerDelegateObject = delegate; this.mBeanServerDelegateObject = delegate;
this.outerShell = outer; this.outerShell = outer;
final Repository repository = new Repository(domain,fairLock); final Repository repository = new Repository(domain);
this.mbsInterceptor = this.mbsInterceptor =
new NamespaceDispatchInterceptor(outer, delegate, instantiator, new NamespaceDispatchInterceptor(outer, delegate, instantiator,
repository); repository);
......
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.jmx.namespace;
import com.sun.jmx.defaults.JmxProperties;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanServerConnection;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.event.EventClient;
import javax.management.event.EventClientDelegateMBean;
import javax.management.namespace.JMXNamespace;
import javax.management.namespace.JMXNamespaces;
import javax.management.remote.JMXAddressable;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXServiceURL;
import javax.security.auth.Subject;
/**
* A collection of methods that provide JMXConnector wrappers for
* JMXRemoteNamepaces underlying connectors.
* <p><b>
* This API is a Sun internal API and is subject to changes without notice.
* </b></p>
* @since 1.7
*/
public final class JMXNamespaceUtils {
/**
* A logger for this class.
**/
private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
private static <K,V> Map<K,V> newWeakHashMap() {
return new WeakHashMap<K,V>();
}
/** There are no instances of this class */
private JMXNamespaceUtils() {
}
// returns un unmodifiable view of a map.
public static <K,V> Map<K,V> unmodifiableMap(Map<K,V> aMap) {
if (aMap == null || aMap.isEmpty())
return Collections.emptyMap();
return Collections.unmodifiableMap(aMap);
}
/**
* A base class that helps writing JMXConnectors that return
* MBeanServerConnection wrappers.
* This base class wraps an inner JMXConnector (the source), and preserve
* its caching policy. If a connection is cached in the source, its wrapper
* will be cached in this connector too.
* Author's note: rewriting this with java.lang.reflect.Proxy could be
* envisaged. It would avoid the combinatory sub-classing introduced by
* JMXAddressable.
* <p>
* Note: all the standard JMXConnector implementations are serializable.
* This implementation here is not. Should it be?
* I believe it must not be serializable unless it becomes
* part of a public API (either standard or officially exposed
* and supported in a documented com.sun package)
**/
static class JMXCachingConnector
implements JMXConnector {
// private static final long serialVersionUID = -2279076110599707875L;
final JMXConnector source;
// if this object is made serializable, then the variable below
// needs to become volatile transient and be lazyly-created...
private final
Map<MBeanServerConnection,MBeanServerConnection> connectionMap;
public JMXCachingConnector(JMXConnector source) {
this.source = checkNonNull(source, "source");
connectionMap = newWeakHashMap();
}
private MBeanServerConnection
getCached(MBeanServerConnection inner) {
return connectionMap.get(inner);
}
private MBeanServerConnection putCached(final MBeanServerConnection inner,
final MBeanServerConnection wrapper) {
if (inner == wrapper) return wrapper;
synchronized (this) {
final MBeanServerConnection concurrent =
connectionMap.get(inner);
if (concurrent != null) return concurrent;
connectionMap.put(inner,wrapper);
}
return wrapper;
}
public void addConnectionNotificationListener(NotificationListener
listener, NotificationFilter filter, Object handback) {
source.addConnectionNotificationListener(listener,filter,handback);
}
public void close() throws IOException {
source.close();
}
public void connect() throws IOException {
source.connect();
}
public void connect(Map<String,?> env) throws IOException {
source.connect(env);
}
public String getConnectionId() throws IOException {
return source.getConnectionId();
}
/**
* Preserve caching policy of the underlying connector.
**/
public MBeanServerConnection
getMBeanServerConnection() throws IOException {
final MBeanServerConnection inner =
source.getMBeanServerConnection();
final MBeanServerConnection cached = getCached(inner);
if (cached != null) return cached;
final MBeanServerConnection wrapper = wrap(inner);
return putCached(inner,wrapper);
}
public MBeanServerConnection
getMBeanServerConnection(Subject delegationSubject)
throws IOException {
final MBeanServerConnection wrapped =
source.getMBeanServerConnection(delegationSubject);
synchronized (this) {
final MBeanServerConnection cached = getCached(wrapped);
if (cached != null) return cached;
final MBeanServerConnection wrapper =
wrapWithSubject(wrapped,delegationSubject);
return putCached(wrapped,wrapper);
}
}
public void removeConnectionNotificationListener(
NotificationListener listener)
throws ListenerNotFoundException {
source.removeConnectionNotificationListener(listener);
}
public void removeConnectionNotificationListener(
NotificationListener l, NotificationFilter f,
Object handback) throws ListenerNotFoundException {
source.removeConnectionNotificationListener(l,f,handback);
}
/**
* This is the method that subclass will redefine. This method
* is called by {@code this.getMBeanServerConnection()}.
* {@code inner} is the connection returned by
* {@code source.getMBeanServerConnection()}.
**/
protected MBeanServerConnection wrap(MBeanServerConnection inner)
throws IOException {
return inner;
}
/**
* Subclass may also want to redefine this method.
* By default it calls wrap(inner). This method
* is called by {@code this.getMBeanServerConnection(Subject)}.
* {@code inner} is the connection returned by
* {@code source.getMBeanServerConnection(Subject)}.
**/
protected MBeanServerConnection wrapWithSubject(
MBeanServerConnection inner, Subject delegationSubject)
throws IOException {
return wrap(inner);
}
@Override
public String toString() {
if (source instanceof JMXAddressable) {
final JMXServiceURL address =
((JMXAddressable)source).getAddress();
if (address != null)
return address.toString();
}
return source.toString();
}
}
/**
* The name space connector can do 'cd'
**/
static class JMXNamespaceConnector extends JMXCachingConnector {
// private static final long serialVersionUID = -4813611540843020867L;
private final String toDir;
private final boolean closeable;
public JMXNamespaceConnector(JMXConnector source, String toDir,
boolean closeable) {
super(source);
this.toDir = toDir;
this.closeable = closeable;
}
@Override
public void close() throws IOException {
if (!closeable)
throw new UnsupportedOperationException("close");
else super.close();
}
@Override
protected MBeanServerConnection wrap(MBeanServerConnection wrapped)
throws IOException {
if (LOG.isLoggable(Level.FINER))
LOG.finer("Creating name space proxy connection for source: "+
"namespace="+toDir);
return JMXNamespaces.narrowToNamespace(wrapped,toDir);
}
@Override
public String toString() {
return "JMXNamespaces.narrowToNamespace("+
super.toString()+
", \""+toDir+"\")";
}
}
static class JMXEventConnector extends JMXCachingConnector {
// private static final long serialVersionUID = 4742659236340242785L;
JMXEventConnector(JMXConnector wrapped) {
super(wrapped);
}
@Override
protected MBeanServerConnection wrap(MBeanServerConnection inner)
throws IOException {
return EventClient.getEventClientConnection(inner);
}
@Override
public String toString() {
return "EventClient.withEventClient("+super.toString()+")";
}
}
static class JMXAddressableEventConnector extends JMXEventConnector
implements JMXAddressable {
// private static final long serialVersionUID = -9128520234812124712L;
JMXAddressableEventConnector(JMXConnector wrapped) {
super(wrapped);
}
public JMXServiceURL getAddress() {
return ((JMXAddressable)source).getAddress();
}
}
/**
* Creates a connector whose MBeamServerConnection will point to the
* given sub name space inside the source connector.
* @see JMXNamespace
**/
public static JMXConnector cd(final JMXConnector source,
final String toNamespace,
final boolean closeable)
throws IOException {
checkNonNull(source, "JMXConnector");
if (toNamespace == null || toNamespace.equals(""))
return source;
return new JMXNamespaceConnector(source,toNamespace,closeable);
}
/**
* Returns a JMX Connector that will use an {@link EventClient}
* to subscribe for notifications. If the server doesn't have
* an {@link EventClientDelegateMBean}, then the connector will
* use the legacy notification mechanism instead.
*
* @param source The underlying JMX Connector wrapped by the returned
* connector.
* @return A JMX Connector that will uses an {@link EventClient}, if
* available.
* @see EventClient#getEventClientConnection(MBeanServerConnection)
*/
public static JMXConnector withEventClient(final JMXConnector source) {
checkNonNull(source, "JMXConnector");
if (source instanceof JMXAddressable)
return new JMXAddressableEventConnector(source);
else
return new JMXEventConnector(source);
}
public static <T> T checkNonNull(T parameter, String name) {
if (parameter == null)
throw new IllegalArgumentException(name+" must not be null");
return parameter;
}
}
...@@ -49,11 +49,6 @@ public class ObjectNameRouter { ...@@ -49,11 +49,6 @@ public class ObjectNameRouter {
final int tlen; final int tlen;
final boolean identity; final boolean identity;
public ObjectNameRouter(String targetDirName) {
this(targetDirName,null);
}
/** Creates a new instance of ObjectNameRouter */ /** Creates a new instance of ObjectNameRouter */
public ObjectNameRouter(final String remove, final String add) { public ObjectNameRouter(final String remove, final String add) {
this.targetPrefix = (remove==null?"":remove); this.targetPrefix = (remove==null?"":remove);
...@@ -186,6 +181,4 @@ public class ObjectNameRouter { ...@@ -186,6 +181,4 @@ public class ObjectNameRouter {
b.append(NAMESPACE_SEPARATOR); b.append(NAMESPACE_SEPARATOR);
return b.toString(); return b.toString();
} }
} }
...@@ -31,7 +31,6 @@ import java.util.logging.Level; ...@@ -31,7 +31,6 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.management.MBeanServerConnection; import javax.management.MBeanServerConnection;
import javax.management.namespace.JMXNamespaces;
/** /**
...@@ -57,22 +56,14 @@ public class RoutingConnectionProxy ...@@ -57,22 +56,14 @@ public class RoutingConnectionProxy
private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER; private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
/**
* Creates a new instance of RoutingConnectionProxy
*/
public RoutingConnectionProxy(MBeanServerConnection source,
String sourceDir) {
this(source,sourceDir,"",false);
}
/** /**
* Creates a new instance of RoutingConnectionProxy * Creates a new instance of RoutingConnectionProxy
*/ */
public RoutingConnectionProxy(MBeanServerConnection source, public RoutingConnectionProxy(MBeanServerConnection source,
String sourceDir, String sourceDir,
String targetDir, String targetDir,
boolean forwardsContext) { boolean probe) {
super(source,sourceDir,targetDir,forwardsContext); super(source, sourceDir, targetDir, probe);
if (LOG.isLoggable(Level.FINER)) if (LOG.isLoggable(Level.FINER))
LOG.finer("RoutingConnectionProxy for " + getSourceNamespace() + LOG.finer("RoutingConnectionProxy for " + getSourceNamespace() +
...@@ -85,15 +76,13 @@ public class RoutingConnectionProxy ...@@ -85,15 +76,13 @@ public class RoutingConnectionProxy
final String sourceNs = getSourceNamespace(); final String sourceNs = getSourceNamespace();
String wrapped = String.valueOf(source()); String wrapped = String.valueOf(source());
if ("".equals(targetNs)) { if ("".equals(targetNs)) {
if (forwardsContext)
wrapped = "ClientContext.withDynamicContext("+wrapped+")";
return "JMXNamespaces.narrowToNamespace("+ return "JMXNamespaces.narrowToNamespace("+
wrapped+", \""+ wrapped+", \""+
sourceNs+"\")"; sourceNs+"\")";
} }
return this.getClass().getSimpleName()+"("+wrapped+", \""+ return this.getClass().getSimpleName()+"("+wrapped+", \""+
sourceNs+"\", \""+ sourceNs+"\", \""+
targetNs+"\", "+forwardsContext+")"; targetNs+"\")";
} }
static final RoutingProxyFactory static final RoutingProxyFactory
...@@ -102,22 +91,16 @@ public class RoutingConnectionProxy ...@@ -102,22 +91,16 @@ public class RoutingConnectionProxy
<MBeanServerConnection,RoutingConnectionProxy>() { <MBeanServerConnection,RoutingConnectionProxy>() {
public RoutingConnectionProxy newInstance(MBeanServerConnection source, public RoutingConnectionProxy newInstance(MBeanServerConnection source,
String sourcePath, String targetPath, String sourcePath, String targetPath, boolean probe) {
boolean forwardsContext) {
return new RoutingConnectionProxy(source,sourcePath, return new RoutingConnectionProxy(source,sourcePath,
targetPath,forwardsContext); targetPath, probe);
}
public RoutingConnectionProxy newInstance(
MBeanServerConnection source, String sourcePath) {
return new RoutingConnectionProxy(source,sourcePath);
} }
}; };
public static MBeanServerConnection cd(MBeanServerConnection source, public static MBeanServerConnection cd(
String sourcePath) { MBeanServerConnection source, String sourcePath, boolean probe) {
return RoutingProxy.cd(RoutingConnectionProxy.class, FACTORY, return RoutingProxy.cd(RoutingConnectionProxy.class, FACTORY,
source, sourcePath); source, sourcePath, probe);
} }
} }
...@@ -30,6 +30,7 @@ import java.io.IOException; ...@@ -30,6 +30,7 @@ import java.io.IOException;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException; import javax.management.MBeanException;
import javax.management.MBeanRegistrationException; import javax.management.MBeanRegistrationException;
...@@ -90,17 +91,9 @@ import javax.management.namespace.JMXNamespaces; ...@@ -90,17 +91,9 @@ import javax.management.namespace.JMXNamespaces;
// targetNs=<encoded-context> // context must be removed from object name // targetNs=<encoded-context> // context must be removed from object name
// sourceNs="" // nothing to add... // sourceNs="" // nothing to add...
// //
// RoutingProxies can also be used on the client side to implement
// "withClientContext" operations. In that case, the boolean parameter
// 'forwards context' is set to true, targetNs is "", and sourceNS may
// also be "". When forwardsContext is true, the RoutingProxy dynamically
// creates an ObjectNameRouter for each operation - in order to dynamically add
// the context attached to the thread to the routing ObjectName. This is
// performed in the getObjectNameRouter() method.
//
// Finally, in order to avoid too many layers of wrapping, // Finally, in order to avoid too many layers of wrapping,
// RoutingConnectionProxy and RoutingServerProxy can be created through a // RoutingConnectionProxy and RoutingServerProxy can be created through a
// factory method that can concatenate namespace pathes in order to // factory method that can concatenate namespace paths in order to
// return a single RoutingProxy - rather than wrapping a RoutingProxy inside // return a single RoutingProxy - rather than wrapping a RoutingProxy inside
// another RoutingProxy. See RoutingConnectionProxy.cd and // another RoutingProxy. See RoutingConnectionProxy.cd and
// RoutingServerProxy.cd // RoutingServerProxy.cd
...@@ -146,16 +139,18 @@ public abstract class RoutingProxy<T extends MBeanServerConnection> ...@@ -146,16 +139,18 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
private final T source; private final T source;
// The name space we're narrowing to (usually some name space in // The name space we're narrowing to (usually some name space in
// the source MBeanServerConnection // the source MBeanServerConnection), e.g. "a" for the namespace
// "a//". This is empty in the case of ClientContext described above.
private final String sourceNs; private final String sourceNs;
// The name space we pretend to be mounted in (usually "") // The name space we pretend to be mounted in. This is empty except
// in the case of ClientContext described above (where it will be
// something like "jmx.context//foo=bar".
private final String targetNs; private final String targetNs;
// The name of the JMXNamespace that handles the source name space // The name of the JMXNamespace that handles the source name space
private final ObjectName handlerName; private final ObjectName handlerName;
private final ObjectNameRouter router; private final ObjectNameRouter router;
final boolean forwardsContext;
private volatile String defaultDomain = null; private volatile String defaultDomain = null;
/** /**
...@@ -164,7 +159,7 @@ public abstract class RoutingProxy<T extends MBeanServerConnection> ...@@ -164,7 +159,7 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
protected RoutingProxy(T source, protected RoutingProxy(T source,
String sourceNs, String sourceNs,
String targetNs, String targetNs,
boolean forwardsContext) { boolean probe) {
if (source == null) throw new IllegalArgumentException("null"); if (source == null) throw new IllegalArgumentException("null");
this.sourceNs = JMXNamespaces.normalizeNamespaceName(sourceNs); this.sourceNs = JMXNamespaces.normalizeNamespaceName(sourceNs);
...@@ -177,21 +172,24 @@ public abstract class RoutingProxy<T extends MBeanServerConnection> ...@@ -177,21 +172,24 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
// System.err.println("sourceNs: "+sourceNs); // System.err.println("sourceNs: "+sourceNs);
this.handlerName = this.handlerName =
JMXNamespaces.getNamespaceObjectName(this.sourceNs); JMXNamespaces.getNamespaceObjectName(this.sourceNs);
if (probe) {
try { try {
// System.err.println("handlerName: "+handlerName); if (!source.isRegistered(handlerName)) {
if (!source.isRegistered(handlerName)) InstanceNotFoundException infe =
new InstanceNotFoundException(handlerName);
throw new IllegalArgumentException(sourceNs + throw new IllegalArgumentException(sourceNs +
": no such name space"); ": no such name space", infe);
}
} catch (IOException x) { } catch (IOException x) {
throw new IllegalArgumentException("source stale: "+x,x); throw new IllegalArgumentException("source stale: "+x,x);
} }
} }
}
this.source = source; this.source = source;
this.targetNs = (targetNs==null?"": this.targetNs = (targetNs==null?"":
JMXNamespaces.normalizeNamespaceName(targetNs)); JMXNamespaces.normalizeNamespaceName(targetNs));
this.router = this.router =
new ObjectNameRouter(this.targetNs,this.sourceNs); new ObjectNameRouter(this.targetNs,this.sourceNs);
this.forwardsContext = forwardsContext;
if (LOG.isLoggable(Level.FINER)) if (LOG.isLoggable(Level.FINER))
LOG.finer("RoutingProxy for " + this.sourceNs + " created"); LOG.finer("RoutingProxy for " + this.sourceNs + " created");
...@@ -200,14 +198,6 @@ public abstract class RoutingProxy<T extends MBeanServerConnection> ...@@ -200,14 +198,6 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
@Override @Override
public T source() { return source; } public T source() { return source; }
ObjectNameRouter getObjectNameRouter() {
// TODO: uncomment this when contexts are added
// if (forwardsContext)
// return ObjectNameRouter.wrapWithContext(router);
// else
return router;
}
@Override @Override
public ObjectName toSource(ObjectName targetName) public ObjectName toSource(ObjectName targetName)
throws MalformedObjectNameException { throws MalformedObjectNameException {
...@@ -222,8 +212,7 @@ public abstract class RoutingProxy<T extends MBeanServerConnection> ...@@ -222,8 +212,7 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
if (defaultDomain != null) if (defaultDomain != null)
targetName = targetName.withDomain(defaultDomain); targetName = targetName.withDomain(defaultDomain);
} }
final ObjectNameRouter r = getObjectNameRouter(); return router.toSourceContext(targetName,true);
return r.toSourceContext(targetName,true);
} }
@Override @Override
...@@ -243,8 +232,7 @@ public abstract class RoutingProxy<T extends MBeanServerConnection> ...@@ -243,8 +232,7 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
public ObjectName toTarget(ObjectName sourceName) public ObjectName toTarget(ObjectName sourceName)
throws MalformedObjectNameException { throws MalformedObjectNameException {
if (sourceName == null) return null; if (sourceName == null) return null;
final ObjectNameRouter r = getObjectNameRouter(); return router.toTargetContext(sourceName,false);
return r.toTargetContext(sourceName,false);
} }
private Object getAttributeFromHandler(String attributeName) private Object getAttributeFromHandler(String attributeName)
...@@ -357,11 +345,8 @@ public abstract class RoutingProxy<T extends MBeanServerConnection> ...@@ -357,11 +345,8 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
// instance. // instance.
static interface RoutingProxyFactory<T extends MBeanServerConnection, static interface RoutingProxyFactory<T extends MBeanServerConnection,
R extends RoutingProxy<T>> { R extends RoutingProxy<T>> {
R newInstance(T source, public R newInstance(
String sourcePath, String targetPath, T source, String sourcePath, String targetPath, boolean probe);
boolean forwardsContext);
R newInstance(T source,
String sourcePath);
} }
// Performs a narrowDownToNamespace operation. // Performs a narrowDownToNamespace operation.
...@@ -377,7 +362,7 @@ public abstract class RoutingProxy<T extends MBeanServerConnection> ...@@ -377,7 +362,7 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
static <T extends MBeanServerConnection, R extends RoutingProxy<T>> static <T extends MBeanServerConnection, R extends RoutingProxy<T>>
R cd(Class<R> routingProxyClass, R cd(Class<R> routingProxyClass,
RoutingProxyFactory<T,R> factory, RoutingProxyFactory<T,R> factory,
T source, String sourcePath) { T source, String sourcePath, boolean probe) {
if (source == null) throw new IllegalArgumentException("null"); if (source == null) throw new IllegalArgumentException("null");
if (source.getClass().equals(routingProxyClass)) { if (source.getClass().equals(routingProxyClass)) {
// cast is OK here, but findbugs complains unless we use class.cast // cast is OK here, but findbugs complains unless we use class.cast
...@@ -400,14 +385,13 @@ public abstract class RoutingProxy<T extends MBeanServerConnection> ...@@ -400,14 +385,13 @@ public abstract class RoutingProxy<T extends MBeanServerConnection>
final String path = final String path =
JMXNamespaces.concat(other.getSourceNamespace(), JMXNamespaces.concat(other.getSourceNamespace(),
sourcePath); sourcePath);
return factory.newInstance(other.source(),path,"", return factory.newInstance(other.source(), path, "", probe);
other.forwardsContext);
} }
// Note: we could do possibly something here - but it would involve // Note: we could do possibly something here - but it would involve
// removing part of targetDir, and possibly adding // removing part of targetDir, and possibly adding
// something to sourcePath. // something to sourcePath.
// Too complex to bother! => simply default to stacking... // Too complex to bother! => simply default to stacking...
} }
return factory.newInstance(source,sourcePath); return factory.newInstance(source, sourcePath, "", probe);
} }
} }
...@@ -54,7 +54,6 @@ import javax.management.OperationsException; ...@@ -54,7 +54,6 @@ import javax.management.OperationsException;
import javax.management.QueryExp; import javax.management.QueryExp;
import javax.management.ReflectionException; import javax.management.ReflectionException;
import javax.management.loading.ClassLoaderRepository; import javax.management.loading.ClassLoaderRepository;
import javax.management.namespace.JMXNamespaces;
/** /**
* A RoutingServerProxy is an MBeanServer proxy that proxies a * A RoutingServerProxy is an MBeanServer proxy that proxies a
...@@ -76,19 +75,11 @@ public class RoutingServerProxy ...@@ -76,19 +75,11 @@ public class RoutingServerProxy
extends RoutingProxy<MBeanServer> extends RoutingProxy<MBeanServer>
implements MBeanServer { implements MBeanServer {
/**
* Creates a new instance of RoutingServerProxy
*/
public RoutingServerProxy(MBeanServer source,
String sourceNs) {
this(source,sourceNs,"",false);
}
public RoutingServerProxy(MBeanServer source, public RoutingServerProxy(MBeanServer source,
String sourceNs, String sourceNs,
String targetNs, String targetNs,
boolean forwardsContext) { boolean probe) {
super(source,sourceNs,targetNs,forwardsContext); super(source, sourceNs, targetNs, probe);
} }
/** /**
...@@ -571,20 +562,15 @@ public class RoutingServerProxy ...@@ -571,20 +562,15 @@ public class RoutingServerProxy
FACTORY = new RoutingProxyFactory<MBeanServer,RoutingServerProxy>() { FACTORY = new RoutingProxyFactory<MBeanServer,RoutingServerProxy>() {
public RoutingServerProxy newInstance(MBeanServer source, public RoutingServerProxy newInstance(MBeanServer source,
String sourcePath, String targetPath, String sourcePath, String targetPath, boolean probe) {
boolean forwardsContext) { return new RoutingServerProxy(
return new RoutingServerProxy(source,sourcePath, source, sourcePath, targetPath, probe);
targetPath,forwardsContext);
}
public RoutingServerProxy newInstance(
MBeanServer source, String sourcePath) {
return new RoutingServerProxy(source,sourcePath);
} }
}; };
public static MBeanServer cd(MBeanServer source, String sourcePath) { public static MBeanServer cd(
MBeanServer source, String sourcePath, boolean probe) {
return RoutingProxy.cd(RoutingServerProxy.class, FACTORY, return RoutingProxy.cd(RoutingServerProxy.class, FACTORY,
source, sourcePath); source, sourcePath, probe);
} }
} }
...@@ -430,13 +430,11 @@ public class EventClientConnection implements InvocationHandler, ...@@ -430,13 +430,11 @@ public class EventClientConnection implements InvocationHandler,
* The {@code EventClient} is created lazily, when it is needed * The {@code EventClient} is created lazily, when it is needed
* for the first time. If null, a default factory will be used * for the first time. If null, a default factory will be used
* (see {@link #createEventClient}). * (see {@link #createEventClient}).
* @return the * @return the MBeanServerConnection.
**/ **/
public static MBeanServerConnection getEventConnectionFor( public static MBeanServerConnection getEventConnectionFor(
MBeanServerConnection connection, MBeanServerConnection connection,
Callable<EventClient> eventClientFactory) { Callable<EventClient> eventClientFactory) {
// if c already uses an EventClient no need to create a new one.
//
if (connection instanceof EventClientFactory if (connection instanceof EventClientFactory
&& eventClientFactory != null) && eventClientFactory != null)
throw new IllegalArgumentException("connection already uses EventClient"); throw new IllegalArgumentException("connection already uses EventClient");
......
...@@ -497,6 +497,10 @@ public class HttpsURLConnectionOldImpl ...@@ -497,6 +497,10 @@ public class HttpsURLConnectionOldImpl
delegate.setFixedLengthStreamingMode(contentLength); delegate.setFixedLengthStreamingMode(contentLength);
} }
public void setFixedLengthStreamingMode(long contentLength) {
delegate.setFixedLengthStreamingMode(contentLength);
}
public void setChunkedStreamingMode (int chunklen) { public void setChunkedStreamingMode (int chunklen) {
delegate.setChunkedStreamingMode(chunklen); delegate.setChunkedStreamingMode(chunklen);
} }
......
...@@ -86,6 +86,8 @@ import sun.misc.HexDumpEncoder; ...@@ -86,6 +86,8 @@ import sun.misc.HexDumpEncoder;
* the principal name from the configuration is used. In the * the principal name from the configuration is used. In the
* case where the principal property is not set and the principal * case where the principal property is not set and the principal
* entry also does not exist, the user is prompted for the name. * 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 * <p> The following is a list of configuration options supported
* for <code>Krb5LoginModule</code>: * for <code>Krb5LoginModule</code>:
...@@ -101,18 +103,19 @@ import sun.misc.HexDumpEncoder; ...@@ -101,18 +103,19 @@ import sun.misc.HexDumpEncoder;
* to false if you do not want this module to use the ticket cache. * to false if you do not want this module to use the ticket cache.
* (Default is False). * (Default is False).
* This module will * This module will
* search for the tickect * search for the ticket
* cache in the following locations: * cache in the following locations:
* For Windows 2000, it will use Local Security Authority (LSA) API * On Solaris and Linux
* to get the TGT. On Solaris and Linux
* it will look for the ticket cache in /tmp/krb5cc_<code>uid</code> * it will look for the ticket cache in /tmp/krb5cc_<code>uid</code>
* where the uid is numeric user * where the uid is numeric user
* identifier. If the ticket cache is * identifier. If the ticket cache is
* not available in either of the above locations, or if we are on a * not available in the above location, or if we are on a
* different Windows platform, it will look for the cache as * Windows platform, it will look for the cache as
* {user.home}{file.separator}krb5cc_{user.name}. * {user.home}{file.separator}krb5cc_{user.name}.
* You can override the ticket cache location by using * 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> * <P>
* <dt><b><code>ticketCache</code></b>:</dt> * <dt><b><code>ticketCache</code></b>:</dt>
* <dd>Set this to the name of the ticket * <dd>Set this to the name of the ticket
...@@ -129,20 +132,20 @@ import sun.misc.HexDumpEncoder; ...@@ -129,20 +132,20 @@ import sun.misc.HexDumpEncoder;
* <dt><b><code>doNotPrompt</code></b>:</dt> * <dt><b><code>doNotPrompt</code></b>:</dt>
* <dd>Set this to true if you do not want to be * <dd>Set this to true if you do not want to be
* prompted for the password * prompted for the password
* if credentials can * if credentials can not be obtained from the cache, the keytab,
* not be obtained from the cache or keytab.(Default is false) * or through shared state.(Default is false)
* If set to true authentication will fail if credentials can * If set to true, credential must be obtained through cache, keytab,
* not be obtained from the cache or keytab.</dd> * or shared state. Otherwise, authentication will fail.</dd>
* <P> * <P>
* <dt><b><code>useKeyTab</code></b>:</dt> * <dt><b><code>useKeyTab</code></b>:</dt>
* <dd>Set this to true if you * <dd>Set this to true if you
* want the module to get the principal's key from the * want the module to get the principal's key from the
* the keytab.(default value is False) * the keytab.(default value is False)
* If <code>keyatb</code> * If <code>keytab</code>
* is not set then * is not set then
* the module will locate the keytab from the * the module will locate the keytab from the
* Kerberos configuration file.</dd> * Kerberos configuration file.
* If it is not specifed in the Kerberos configuration file * If it is not specified in the Kerberos configuration file
* then it will look for the file * then it will look for the file
* <code>{user.home}{file.separator}</code>krb5.keytab.</dd> * <code>{user.home}{file.separator}</code>krb5.keytab.</dd>
* <P> * <P>
...@@ -210,20 +213,34 @@ import sun.misc.HexDumpEncoder; ...@@ -210,20 +213,34 @@ import sun.misc.HexDumpEncoder;
* exist for the username and password in the shared * exist for the username and password in the shared
* state, or if authentication fails. * state, or if authentication fails.
* *
* clearPass if, true, this <code>LoginModule</code> clears the * clearPass if, true, this LoginModule clears the
* username and password stored in the module's shared * username and password stored in the module's shared
* state after both phases of authentication * state after both phases of authentication
* (login and commit) have completed. * (login and commit) have completed.
* </pre> * </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 * <p>Examples of some configuration values for Krb5LoginModule in
* JAAS config file and the results are: * JAAS config file and the results are:
* <ul> * <ul>
* <p> <code>doNotPrompt</code>=true; * <p> <code>doNotPrompt</code>=true;
* </ul> * </ul>
* <p> This is an illegal combination since <code>useTicketCache</code> * <p> This is an illegal combination since none of <code>useTicketCache</code>,
* is not set and the user can not be prompted for the password. * <code>useKeyTab</code>, <code>useFirstPass</code> and <code>tryFirstPass</code>
* is set and the user can not be prompted for the password.
*<ul> *<ul>
* <p> <code>ticketCache</code> = < filename >; * <p> <code>ticketCache</code> = &lt;filename&gt;;
*</ul> *</ul>
* <p> This is an illegal combination since <code>useTicketCache</code> * <p> This is an illegal combination since <code>useTicketCache</code>
* is not set to true and the ticketCache is set. A configuration error * is not set to true and the ticketCache is set. A configuration error
...@@ -240,9 +257,9 @@ import sun.misc.HexDumpEncoder; ...@@ -240,9 +257,9 @@ import sun.misc.HexDumpEncoder;
*</ul> *</ul>
* <p> This is an illegal combination since <code>storeKey</code> is set to * <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 * 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> * <ul>
* <p> <code>keyTab</code> = < filename > <code>doNotPrompt</code>=true ; * <p> <code>keyTab</code> = &lt;filename&gt; <code>doNotPrompt</code>=true ;
* </ul> * </ul>
* <p>This is an illegal combination since useKeyTab is not set to true and * <p>This is an illegal combination since useKeyTab is not set to true and
* the keyTab is set. A configuration error will occur. * the keyTab is set. A configuration error will occur.
...@@ -260,7 +277,7 @@ import sun.misc.HexDumpEncoder; ...@@ -260,7 +277,7 @@ import sun.misc.HexDumpEncoder;
* with the principal and TGT. If the TGT is not available, * with the principal and TGT. If the TGT is not available,
* do not prompt the user, instead fail the authentication. * do not prompt the user, instead fail the authentication.
* <ul> * <ul>
* <p><code>principal</code>=< name ><code>useTicketCache</code> = true * <p><code>principal</code>=&lt;name&gt;<code>useTicketCache</code> = true
* <code>doNotPrompt</code>=true; * <code>doNotPrompt</code>=true;
*</ul> *</ul>
* <p> Get the TGT from the default cache for the principal and populate the * <p> Get the TGT from the default cache for the principal and populate the
...@@ -269,9 +286,9 @@ import sun.misc.HexDumpEncoder; ...@@ -269,9 +286,9 @@ import sun.misc.HexDumpEncoder;
* authentication will fail. * authentication will fail.
* <ul> * <ul>
* <p> <code>useTicketCache</code> = true * <p> <code>useTicketCache</code> = true
* <code>ticketCache</code>=< file name ><code>useKeyTab</code> = true * <code>ticketCache</code>=&lt;file name&gt;<code>useKeyTab</code> = true
* <code> keyTab</code>=< keytab filename > * <code> keyTab</code>=&lt;keytab filename&gt;
* <code>principal</code> = < principal name > * <code>principal</code> = &lt;principal name&gt;
* <code>doNotPrompt</code>=true; * <code>doNotPrompt</code>=true;
*</ul> *</ul>
* <p> Search the cache for the principal's TGT. If it is not available * <p> Search the cache for the principal's TGT. If it is not available
...@@ -281,7 +298,7 @@ import sun.misc.HexDumpEncoder; ...@@ -281,7 +298,7 @@ import sun.misc.HexDumpEncoder;
* If the key is not available or valid then authentication will fail. * If the key is not available or valid then authentication will fail.
* <ul> * <ul>
* <p><code>useTicketCache</code> = true * <p><code>useTicketCache</code> = true
* <code>ticketCache</code>=< file name > * <code>ticketCache</code>=&lt;file name&gt;
*</ul> *</ul>
* <p> The TGT will be obtained from the cache specified. * <p> The TGT will be obtained from the cache specified.
* The Kerberos principal name used will be the principal name in * The Kerberos principal name used will be the principal name in
...@@ -292,8 +309,8 @@ import sun.misc.HexDumpEncoder; ...@@ -292,8 +309,8 @@ import sun.misc.HexDumpEncoder;
* The Subject will be populated with the TGT. * The Subject will be populated with the TGT.
*<ul> *<ul>
* <p> <code>useKeyTab</code> = true * <p> <code>useKeyTab</code> = true
* <code>keyTab</code>=< keytab filename > * <code>keyTab</code>=&lt;keytab filename&gt;
* <code>principal</code>= < principal name > * <code>principal</code>= &lt;principal name&gt;
* <code>storeKey</code>=true; * <code>storeKey</code>=true;
*</ul> *</ul>
* <p> The key for the principal will be retrieved from the keytab. * <p> The key for the principal will be retrieved from the keytab.
...@@ -303,7 +320,7 @@ import sun.misc.HexDumpEncoder; ...@@ -303,7 +320,7 @@ import sun.misc.HexDumpEncoder;
* password entered. * password entered.
* <ul> * <ul>
* <p> <code>useKeyTab</code> = true * <p> <code>useKeyTab</code> = true
* <code>keyTab</code>=< keytabname > * <code>keyTab</code>=&lt;keytabname&gt;
* <code>storeKey</code>=true * <code>storeKey</code>=true
* <code>doNotPrompt</code>=true; * <code>doNotPrompt</code>=true;
*</ul> *</ul>
...@@ -316,21 +333,23 @@ import sun.misc.HexDumpEncoder; ...@@ -316,21 +333,23 @@ import sun.misc.HexDumpEncoder;
* Subject's private credentials set. Otherwise the authentication will * Subject's private credentials set. Otherwise the authentication will
* fail. * fail.
*<ul> *<ul>
* <p><code>useKeyTab</code> = true * <p>
* <code>keyTab</code>=< file name > <code>storeKey</code>=true
* <code>principal</code>= < principal name >
* <code>useTicketCache</code>=true * <code>useTicketCache</code>=true
* <code>ticketCache</code>=< file name >; * <code>ticketCache</code>=&lt;file name&gt;;
* <code>useKeyTab</code> = true
* <code>keyTab</code>=&lt;file name&gt; <code>storeKey</code>=true
* <code>principal</code>= &lt;principal name&gt;
*</ul> *</ul>
* <p>The principal's key will be retrieved from the keytab and added * <p>
* to the <code>Subject</code>'s private credentials. If the key * The client's TGT will be retrieved from the ticket cache and added to the
* 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
* <code>Subject</code>'s private credentials. If the TGT is not available * <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. * 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> * <ul>
* <p><code>isInitiator</code> = false * <p><code>isInitiator</code> = false
*</ul> *</ul>
...@@ -856,11 +875,13 @@ public class Krb5LoginModule implements LoginModule { ...@@ -856,11 +875,13 @@ public class Krb5LoginModule implements LoginModule {
} }
private void validateConfiguration() throws LoginException { private void validateConfiguration() throws LoginException {
if (doNotPrompt && !useTicketCache && !useKeyTab) if (doNotPrompt && !useTicketCache && !useKeyTab
&& !tryFirstPass && !useFirstPass)
throw new LoginException throw new LoginException
("Configuration Error" ("Configuration Error"
+ " - either doNotPrompt should be " + " - either doNotPrompt should be "
+ " false or useTicketCache/useKeyTab " + " false or at least one of useTicketCache, "
+ " useKeyTab, tryFirstPass and useFirstPass"
+ " should be true"); + " should be true");
if (ticketCacheName != null && !useTicketCache) if (ticketCacheName != null && !useTicketCache)
throw new LoginException throw new LoginException
...@@ -872,11 +893,12 @@ public class Krb5LoginModule implements LoginModule { ...@@ -872,11 +893,12 @@ public class Krb5LoginModule implements LoginModule {
throw new LoginException throw new LoginException
("Configuration Error - useKeyTab should be set to true " ("Configuration Error - useKeyTab should be set to true "
+ "to use the keytab" + keyTabName); + "to use the keytab" + keyTabName);
if (storeKey && doNotPrompt && !useKeyTab) if (storeKey && doNotPrompt && !useKeyTab
&& !tryFirstPass && !useFirstPass)
throw new LoginException throw new LoginException
("Configuration Error - either doNotPrompt " ("Configuration Error - either doNotPrompt should be set to "
+ "should be set to false or " + " false or at least one of tryFirstPass, useFirstPass "
+ "useKeyTab must be set to true for storeKey option"); + "or useKeyTab must be set to true for storeKey option");
if (renewTGT && !useTicketCache) if (renewTGT && !useTicketCache)
throw new LoginException throw new LoginException
("Configuration Error" ("Configuration Error"
......
...@@ -73,10 +73,23 @@ abstract public class HttpURLConnection extends URLConnection { ...@@ -73,10 +73,23 @@ abstract public class HttpURLConnection extends URLConnection {
* The fixed content-length when using fixed-length streaming mode. * The fixed content-length when using fixed-length streaming mode.
* A value of <code>-1</code> means fixed-length streaming mode is disabled * A value of <code>-1</code> means fixed-length streaming mode is disabled
* for output. * 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 * @since 1.5
*/ */
protected int fixedContentLength = -1; 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. * Returns the key for the <code>n</code><sup>th</sup> header field.
* Some implementations may treat the <code>0</code><sup>th</sup> * Some implementations may treat the <code>0</code><sup>th</sup>
...@@ -109,6 +122,9 @@ abstract public class HttpURLConnection extends URLConnection { ...@@ -109,6 +122,9 @@ abstract public class HttpURLConnection extends URLConnection {
* This exception can be queried for the details of the error. * This exception can be queried for the details of the error.
* <p> * <p>
* This method must be called before the URLConnection is connected. * 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 * @param contentLength The number of bytes which will be written
* to the OutputStream. * to the OutputStream.
...@@ -135,6 +151,52 @@ abstract public class HttpURLConnection extends URLConnection { ...@@ -135,6 +151,52 @@ abstract public class HttpURLConnection extends URLConnection {
fixedContentLength = contentLength; 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; /* Default chunk size (including chunk header) if not specified;
* we want to keep this in sync with the one defined in * we want to keep this in sync with the one defined in
* sun.net.www.http.ChunkedOutputStream * sun.net.www.http.ChunkedOutputStream
...@@ -170,7 +232,7 @@ abstract public class HttpURLConnection extends URLConnection { ...@@ -170,7 +232,7 @@ abstract public class HttpURLConnection extends URLConnection {
if (connected) { if (connected) {
throw new IllegalStateException ("Can't set streaming mode: already 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"); throw new IllegalStateException ("Fixed length streaming mode set");
} }
chunkLength = chunklen <=0? DEFAULT_CHUNK_SIZE : chunklen; chunkLength = chunklen <=0? DEFAULT_CHUNK_SIZE : chunklen;
......
...@@ -113,7 +113,7 @@ public class CertPathValidatorException extends GeneralSecurityException { ...@@ -113,7 +113,7 @@ public class CertPathValidatorException extends GeneralSecurityException {
* permitted, and indicates that the cause is nonexistent or unknown.) * permitted, and indicates that the cause is nonexistent or unknown.)
*/ */
public CertPathValidatorException(Throwable cause) { public CertPathValidatorException(Throwable cause) {
this(null, cause); this((cause == null ? null : cause.toString()), cause);
} }
/** /**
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -27,17 +27,23 @@ package javax.management; ...@@ -27,17 +27,23 @@ package javax.management;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* Represents a list of values for attributes of an MBean. The methods * <p>Represents a list of values for attributes of an MBean. See the
* used for the insertion of {@link javax.management.Attribute * {@link MBeanServerConnection#getAttributes getAttributes} and
* Attribute} objects in the <CODE>AttributeList</CODE> overrides the * {@link MBeanServerConnection#setAttributes setAttributes} methods of
* corresponding methods in the superclass * {@link MBeanServer} and {@link MBeanServerConnection}.</p>
* <CODE>ArrayList</CODE>. This is needed in order to insure that the *
* objects contained in the <CODE>AttributeList</CODE> are only * <p id="type-safe">For compatibility reasons, it is possible, though
* <CODE>Attribute</CODE> objects. This avoids getting an exception * highly discouraged, to add objects to an {@code AttributeList} that are
* when retrieving elements from the <CODE>AttributeList</CODE>. * 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 * @since 1.5
*/ */
...@@ -58,8 +64,8 @@ import java.util.List; ...@@ -58,8 +64,8 @@ import java.util.List;
*/ */
public class AttributeList extends ArrayList<Object> { public class AttributeList extends ArrayList<Object> {
private transient boolean typeSafe; private transient volatile boolean typeSafe;
private transient boolean tainted; private transient volatile boolean tainted;
/* Serial version */ /* Serial version */
private static final long serialVersionUID = -4077085769279709076L; private static final long serialVersionUID = -4077085769279709076L;
...@@ -124,13 +130,63 @@ public class AttributeList extends ArrayList<Object> { ...@@ -124,13 +130,63 @@ public class AttributeList extends ArrayList<Object> {
// Check for non-Attribute objects // Check for non-Attribute objects
// //
checkTypeSafe(list); adding(list);
// Build the List<Attribute> // Build the List<Attribute>
// //
super.addAll(list); super.addAll(list);
} }
/**
* <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>}. * Return a view of this list as a {@code List<Attribute>}.
* Changes to the returned value are reflected by changes * Changes to the returned value are reflected by changes
...@@ -154,11 +210,9 @@ public class AttributeList extends ArrayList<Object> { ...@@ -154,11 +210,9 @@ public class AttributeList extends ArrayList<Object> {
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public List<Attribute> asList() { 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; return (List<Attribute>) (List<?>) this;
} }
...@@ -175,7 +229,7 @@ public class AttributeList extends ArrayList<Object> { ...@@ -175,7 +229,7 @@ public class AttributeList extends ArrayList<Object> {
* Inserts the attribute specified as an element at the position specified. * Inserts the attribute specified as an element at the position specified.
* Elements with an index greater than or equal to the current position are * 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 > * 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. * java.lang.IndexOutOfBoundsException thrown.
* *
* @param object The <CODE>Attribute</CODE> object to be inserted. * @param object The <CODE>Attribute</CODE> object to be inserted.
...@@ -245,8 +299,7 @@ public class AttributeList extends ArrayList<Object> { ...@@ -245,8 +299,7 @@ public class AttributeList extends ArrayList<Object> {
public boolean addAll(int index, AttributeList list) { public boolean addAll(int index, AttributeList list) {
try { try {
return super.addAll(index, list); return super.addAll(index, list);
} } catch (IndexOutOfBoundsException e) {
catch (IndexOutOfBoundsException e) {
throw new RuntimeOperationsException(e, throw new RuntimeOperationsException(e,
"The specified index is out of range"); "The specified index is out of range");
} }
...@@ -258,96 +311,77 @@ public class AttributeList extends ArrayList<Object> { ...@@ -258,96 +311,77 @@ public class AttributeList extends ArrayList<Object> {
* been called on this instance. * 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 @Override
public boolean add(Object o) { public boolean add(Object element) {
if (!tainted) adding(element);
tainted = isTainted(o); return super.add(element);
if (typeSafe)
checkTypeSafe(o);
return super.add(o);
} }
/**
* {@inheritDoc}
* @throws IllegalArgumentException if this {@code AttributeList} is
* <a href="#type-safe">type-safe</a> and {@code element} is not an
* {@code Attribute}.
*/
@Override @Override
public void add(int index, Object element) { public void add(int index, Object element) {
if (!tainted) adding(element);
tainted = isTainted(element);
if (typeSafe)
checkTypeSafe(element);
super.add(index, 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 @Override
public boolean addAll(Collection<?> c) { public boolean addAll(Collection<?> c) {
if (!tainted) adding(c);
tainted = isTainted(c);
if (typeSafe)
checkTypeSafe(c);
return super.addAll(c); return super.addAll(c);
} }
/**
* {@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 @Override
public boolean addAll(int index, Collection<?> c) { public boolean addAll(int index, Collection<?> c) {
if (!tainted) adding(c);
tainted = isTainted(c);
if (typeSafe)
checkTypeSafe(c);
return super.addAll(index, c); return super.addAll(index, c);
} }
/**
* {@inheritDoc}
* @throws IllegalArgumentException if this {@code AttributeList} is
* <a href="#type-safe">type-safe</a> and {@code element} is not an
* {@code Attribute}.
*/
@Override @Override
public Object set(int index, Object element) { public Object set(int index, Object element) {
if (!tainted) adding(element);
tainted = isTainted(element);
if (typeSafe)
checkTypeSafe(element);
return super.set(index, element); return super.set(index, element);
} }
/** private void adding(Object x) {
* IllegalArgumentException if o is a non-Attribute object. if (x == null || x instanceof Attribute)
*/ return;
private static void checkTypeSafe(Object o) { if (typeSafe)
try { throw new IllegalArgumentException("Not an Attribute: " + x);
o = (Attribute) o; else
} catch (ClassCastException e) { tainted = true;
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);
}
}
/**
* Returns true if o is a non-Attribute object.
*/
private static boolean isTainted(Object o) {
try {
checkTypeSafe(o);
} catch (IllegalArgumentException e) {
return true;
}
return false;
} }
/** private void adding(Collection<?> c) {
* Returns true if c contains any non-Attribute objects. for (Object x : c)
*/ adding(x);
private static boolean isTainted(Collection<?> c) {
try {
checkTypeSafe(c);
} catch (IllegalArgumentException e) {
return true;
}
return false;
} }
} }
此差异已折叠。
...@@ -35,8 +35,8 @@ import java.io.Serializable; ...@@ -35,8 +35,8 @@ import java.io.Serializable;
// Javadoc imports: // Javadoc imports:
import java.lang.management.MemoryUsage; import java.lang.management.MemoryUsage;
import java.util.Arrays; import java.util.Arrays;
import java.util.Locale;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import javax.management.openmbean.CompositeData; import javax.management.openmbean.CompositeData;
import javax.management.openmbean.MXBeanMappingFactory; import javax.management.openmbean.MXBeanMappingFactory;
import javax.management.openmbean.OpenMBeanAttributeInfoSupport; import javax.management.openmbean.OpenMBeanAttributeInfoSupport;
...@@ -118,19 +118,22 @@ import javax.management.openmbean.OpenType; ...@@ -118,19 +118,22 @@ import javax.management.openmbean.OpenType;
* deprecation, for example {@code "1.3 Replaced by the Capacity * deprecation, for example {@code "1.3 Replaced by the Capacity
* attribute"}.</td> * attribute"}.</td>
* *
* <tr id="descriptionResourceBundleBaseName"> * <tr><td id="descriptionResourceBundleBaseName"><i>descriptionResource<br>
* <td>descriptionResource<br>BundleBaseName</td><td>String</td><td>Any</td> * BundleBaseName</i></td><td>String</td><td>Any</td>
* *
* <td>The base name for the {@link ResourceBundle} in which the key given in * <td>The base name for the {@link ResourceBundle} in which the key given in
* the {@code descriptionResourceKey} field can be found, for example * the {@code descriptionResourceKey} field can be found, for example
* {@code "com.example.myapp.MBeanResources"}.</td> * {@code "com.example.myapp.MBeanResources"}. See
* {@link MBeanInfo#localizeDescriptions MBeanInfo.localizeDescriptions}.</td>
* *
* <tr id="descriptionResourceKey"> * <tr><td id="descriptionResourceKey"><i>descriptionResourceKey</i></td>
* <td>descriptionResourceKey</td><td>String</td><td>Any</td> * <td>String</td><td>Any</td>
* *
* <td>A resource key for the description of this element. In * <td>A resource key for the description of this element. In
* conjunction with the {@code descriptionResourceBundleBaseName}, * conjunction with the {@code descriptionResourceBundleBaseName},
* this can be used to find a localized version of the description.</td> * this can be used to find a localized version of the description.
* See {@link MBeanInfo#localizeDescriptions MBeanInfo.localizeDescriptions}.
* </td>
* *
* <tr><td>enabled</td><td>String</td> * <tr><td>enabled</td><td>String</td>
* <td>MBeanAttributeInfo<br>MBeanNotificationInfo<br>MBeanOperationInfo</td> * <td>MBeanAttributeInfo<br>MBeanNotificationInfo<br>MBeanOperationInfo</td>
...@@ -157,11 +160,11 @@ import javax.management.openmbean.OpenType; ...@@ -157,11 +160,11 @@ import javax.management.openmbean.OpenType;
* href="MBeanInfo.html#info-changed">{@code "jmx.mbean.info.changed"}</a> * href="MBeanInfo.html#info-changed">{@code "jmx.mbean.info.changed"}</a>
* notification.</td> * notification.</td>
* *
* <tr><td>infoTimeout</td><td>String<br>Long</td><td>MBeanInfo</td> * <tr id="infoTimeout"><td>infoTimeout</td><td>String<br>Long</td><td>MBeanInfo</td>
* *
* <td id="infoTimeout">The time in milli-seconds that the MBeanInfo can * <td>The time in milli-seconds that the MBeanInfo can reasonably be
* reasonably be expected to be unchanged. The value can be a {@code Long} * expected to be unchanged. The value can be a {@code Long} or a
* or a decimal string. This provides a hint from a DynamicMBean or any * decimal string. This provides a hint from a DynamicMBean or any
* MBean that does not define {@code immutableInfo} as {@code true} * MBean that does not define {@code immutableInfo} as {@code true}
* that the MBeanInfo is not likely to change within this period and * that the MBeanInfo is not likely to change within this period and
* therefore can be cached. When this field is missing or has the * therefore can be cached. When this field is missing or has the
...@@ -185,6 +188,13 @@ import javax.management.openmbean.OpenType; ...@@ -185,6 +188,13 @@ import javax.management.openmbean.OpenType;
* <td>Legal values for an attribute or parameter. See * <td>Legal values for an attribute or parameter. See
* {@link javax.management.openmbean}.</td> * {@link javax.management.openmbean}.</td>
* *
* <tr id="locale"><td><i>locale</i></td>
* <td>String</td><td>Any</td>
*
* <td>The {@linkplain Locale locale} of the description in this
* {@code MBeanInfo}, {@code MBeanAttributeInfo}, etc, as returned
* by {@link Locale#toString()}.</td>
*
* <tr id="maxValue"><td><i>maxValue</i><td>Object</td> * <tr id="maxValue"><td><i>maxValue</i><td>Object</td>
* <td>MBeanAttributeInfo<br>MBeanParameterInfo</td> * <td>MBeanAttributeInfo<br>MBeanParameterInfo</td>
* *
......
...@@ -30,6 +30,7 @@ import com.sun.jmx.mbeanserver.MBeanInjector; ...@@ -30,6 +30,7 @@ import com.sun.jmx.mbeanserver.MBeanInjector;
import com.sun.jmx.remote.util.ClassLogger; import com.sun.jmx.remote.util.ClassLogger;
import java.beans.BeanInfo; import java.beans.BeanInfo;
import java.beans.PropertyDescriptor; import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
...@@ -37,6 +38,7 @@ import java.lang.reflect.Method; ...@@ -37,6 +38,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
import javax.management.namespace.JMXNamespaces;
import javax.management.openmbean.MXBeanMappingFactory; import javax.management.openmbean.MXBeanMappingFactory;
/** /**
...@@ -60,6 +62,21 @@ public class JMX { ...@@ -60,6 +62,21 @@ public class JMX {
*/ */
public static final String DEFAULT_VALUE_FIELD = "defaultValue"; public static final String DEFAULT_VALUE_FIELD = "defaultValue";
/**
* The name of the <a
* href="Descriptor.html#descriptionResourceBundleBaseName">{@code
* descriptionResourceBundleBaseName}</a> field.
*/
public static final String DESCRIPTION_RESOURCE_BUNDLE_BASE_NAME_FIELD =
"descriptionResourceBundleBaseName";
/**
* The name of the <a href="Descriptor.html#descriptionResourceKey">{@code
* descriptionResourceKey}</a> field.
*/
public static final String DESCRIPTION_RESOURCE_KEY_FIELD =
"descriptionResourceKey";
/** /**
* The name of the <a href="Descriptor.html#immutableInfo">{@code * The name of the <a href="Descriptor.html#immutableInfo">{@code
* immutableInfo}</a> field. * immutableInfo}</a> field.
...@@ -78,6 +95,12 @@ public class JMX { ...@@ -78,6 +95,12 @@ public class JMX {
*/ */
public static final String LEGAL_VALUES_FIELD = "legalValues"; public static final String LEGAL_VALUES_FIELD = "legalValues";
/**
* The name of the <a href="Descriptor.html#locale">{@code locale}</a>
* field.
*/
public static final String LOCALE_FIELD = "locale";
/** /**
* The name of the <a href="Descriptor.html#maxValue">{@code * The name of the <a href="Descriptor.html#maxValue">{@code
* maxValue}</a> field. * maxValue}</a> field.
...@@ -120,13 +143,12 @@ public class JMX { ...@@ -120,13 +143,12 @@ public class JMX {
* <p>Options to apply to an MBean proxy or to an instance of {@link * <p>Options to apply to an MBean proxy or to an instance of {@link
* StandardMBean}.</p> * StandardMBean}.</p>
* *
* <p>For example, to specify a custom {@link MXBeanMappingFactory} * <p>For example, to specify the "wrapped object visible" option for a
* for a {@code StandardMBean}, you might write this:</p> * {@code StandardMBean}, you might write this:</p>
* *
* <pre> * <pre>
* MXBeanMappingFactory factory = new MyMXBeanMappingFactory(); * StandardMBean.Options opts = new StandardMBean.Options();
* JMX.MBeanOptions opts = new JMX.MBeanOptions(); * opts.setWrappedObjectVisible(true);
* opts.setMXBeanMappingFactory(factory);
* StandardMBean mbean = new StandardMBean(impl, intf, opts); * StandardMBean mbean = new StandardMBean(impl, intf, opts);
* </pre> * </pre>
* *
...@@ -808,4 +830,80 @@ public class JMX { ...@@ -808,4 +830,80 @@ public class JMX {
((DynamicWrapperMBean) mbean).getWrappedObject() : mbean; ((DynamicWrapperMBean) mbean).getWrappedObject() : mbean;
return (MBeanInjector.injectsSendNotification(resource)); 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);
}
}
} }
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
package javax.management; package javax.management;
import com.sun.jmx.mbeanserver.Util;
import java.io.IOException; import java.io.IOException;
import java.io.StreamCorruptedException; import java.io.StreamCorruptedException;
import java.io.Serializable; import java.io.Serializable;
...@@ -37,6 +38,12 @@ import java.util.WeakHashMap; ...@@ -37,6 +38,12 @@ import java.util.WeakHashMap;
import java.security.AccessController; import java.security.AccessController;
import java.security.PrivilegedAction; import java.security.PrivilegedAction;
import java.util.HashMap;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import static javax.management.ImmutableDescriptor.nonNullDescriptor; import static javax.management.ImmutableDescriptor.nonNullDescriptor;
/** /**
...@@ -290,6 +297,7 @@ public class MBeanInfo implements Cloneable, Serializable, DescriptorRead { ...@@ -290,6 +297,7 @@ public class MBeanInfo implements Cloneable, Serializable, DescriptorRead {
* <p>Since this class is immutable, the clone method is chiefly of * <p>Since this class is immutable, the clone method is chiefly of
* interest to subclasses.</p> * interest to subclasses.</p>
*/ */
@Override
public Object clone () { public Object clone () {
try { try {
return super.clone() ; return super.clone() ;
...@@ -474,6 +482,7 @@ public class MBeanInfo implements Cloneable, Serializable, DescriptorRead { ...@@ -474,6 +482,7 @@ public class MBeanInfo implements Cloneable, Serializable, DescriptorRead {
return (Descriptor) nonNullDescriptor(descriptor).clone(); return (Descriptor) nonNullDescriptor(descriptor).clone();
} }
@Override
public String toString() { public String toString() {
return return
getClass().getName() + "[" + getClass().getName() + "[" +
...@@ -505,6 +514,7 @@ public class MBeanInfo implements Cloneable, Serializable, DescriptorRead { ...@@ -505,6 +514,7 @@ public class MBeanInfo implements Cloneable, Serializable, DescriptorRead {
* @return true if and only if <code>o</code> is an MBeanInfo that is equal * @return true if and only if <code>o</code> is an MBeanInfo that is equal
* to this one according to the rules above. * to this one according to the rules above.
*/ */
@Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (o == this) if (o == this)
return true; return true;
...@@ -524,6 +534,7 @@ public class MBeanInfo implements Cloneable, Serializable, DescriptorRead { ...@@ -524,6 +534,7 @@ public class MBeanInfo implements Cloneable, Serializable, DescriptorRead {
Arrays.equals(p.fastGetNotifications(), fastGetNotifications())); Arrays.equals(p.fastGetNotifications(), fastGetNotifications()));
} }
@Override
public int hashCode() { public int hashCode() {
/* Since computing the hashCode is quite expensive, we cache it. /* Since computing the hashCode is quite expensive, we cache it.
If by some terrible misfortune the computed value is 0, the If by some terrible misfortune the computed value is 0, the
...@@ -747,4 +758,377 @@ public class MBeanInfo implements Cloneable, Serializable, DescriptorRead { ...@@ -747,4 +758,377 @@ public class MBeanInfo implements Cloneable, Serializable, DescriptorRead {
throw new StreamCorruptedException("Got unexpected byte."); throw new StreamCorruptedException("Got unexpected byte.");
} }
} }
/**
* <p>Return an {@code MBeanInfo} object that is the same as this one
* except that its descriptions are localized in the given locale.
* This means the text returned by {@link MBeanInfo#getDescription}
* (the description of the MBean itself), and the text returned by the
* {@link MBeanFeatureInfo#getDescription getDescription()} method
* for every {@linkplain MBeanAttributeInfo attribute}, {@linkplain
* MBeanOperationInfo operation}, {@linkplain MBeanConstructorInfo
* constructor}, and {@linkplain MBeanNotificationInfo notification}
* contained in the {@code MBeanInfo}.</p>
*
* <p>Here is how the description {@code this.getDescription()} is
* localized.</p>
*
* <p>First, if the {@linkplain #getDescriptor() descriptor}
* of this {@code MBeanInfo} contains a field <code><a
* href="Descriptor.html#locale">"locale"</a></code>, and the value of
* the field is the same as {@code locale.toString()}, then this {@code
* MBeanInfo} is returned. Otherwise, localization proceeds as follows,
* and the {@code "locale"} field in the returned {@code MBeanInfo} will
* be {@code locale.toString()}.
*
* <p>A <em>{@code className}</em> is determined. If this
* {@code MBeanInfo} contains a descriptor with the field
* <a href="Descriptor.html#interfaceClassName">{@code
* "interfaceClassName"}</a>, then the value of that field is the
* {@code className}. Otherwise, it is {@link #getClassName()}.
* Everything before the last period (.) in the {@code className} is
* the <em>{@code package}</em>, and everything after is the <em>{@code
* simpleClassName}</em>. (If there is no period, then the {@code package}
* is empty and the {@code simpleClassName} is the same as the {@code
* className}.)</p>
*
* <p>A <em>{@code resourceKey}</em> is determined. If this {@code
* MBeanInfo} contains a {@linkplain MBeanInfo#getDescriptor() descriptor}
* with a field {@link JMX#DESCRIPTION_RESOURCE_KEY_FIELD
* "descriptionResourceKey"}, the value of the field is
* the {@code resourceKey}. Otherwise, the {@code resourceKey} is {@code
* simpleClassName + ".mbean"}.</p>
*
* <p>A <em>{@code resourceBundleBaseName}</em> is determined. If
* this {@code MBeanInfo} contains a descriptor with a field {@link
* JMX#DESCRIPTION_RESOURCE_BUNDLE_BASE_NAME_FIELD
* "descriptionResourceBundleBaseName"}, the value of the field
* is the {@code resourceBundleBaseName}. Otherwise, the {@code
* resourceBundleBaseName} is {@code package + ".MBeanDescriptions"}.
*
* <p>Then, a {@link java.util.ResourceBundle ResourceBundle} is
* determined, using<br> {@link java.util.ResourceBundle#getBundle(String,
* Locale, ClassLoader) ResourceBundle.getBundle(resourceBundleBaseName,
* locale, loader)}. If this succeeds, and if {@link
* java.util.ResourceBundle#getString(String) getString(resourceKey)}
* returns a string, then that string is the localized description.
* Otherwise, the original description is unchanged.</p>
*
* <p>A localized description for an {@code MBeanAttributeInfo} is
* obtained similarly. The default {@code resourceBundleBaseName}
* is the same as above. The default description and the
* descriptor fields {@code "descriptionResourceKey"} and {@code
* "descriptionResourceBundleBaseName"} come from the {@code
* MBeanAttributeInfo} rather than the {@code MBeanInfo}. If the
* attribute's {@linkplain MBeanFeatureInfo#getName() name} is {@code
* Foo} then its default {@code resourceKey} is {@code simpleClassName +
* ".attribute.Foo"}.</p>
*
* <p>Similar rules apply for operations, constructors, and notifications.
* If the name of the operation, constructor, or notification is {@code
* Foo} then the default {@code resourceKey} is respectively {@code
* simpleClassName + ".operation.Foo"}, {@code simpleClassName +
* ".constructor.Foo"}, or {@code simpleClassName + ".notification.Foo"}.
* If two operations or constructors have the same name (overloading) then
* they have the same default {@code resourceKey}; if different localized
* descriptions are needed then a non-default key must be supplied using
* {@code "descriptionResourceKey"}.</p>
*
* <p>Similar rules also apply for descriptions of parameters ({@link
* MBeanParameterInfo}). The default {@code resourceKey} for a parameter
* whose {@linkplain MBeanFeatureInfo#getName() name} is {@code
* Bar} in an operation or constructor called {@code Foo} is {@code
* simpleClassName + ".operation.Foo.Bar"} or {@code simpleClassName +
* ".constructor.Foo.Bar"} respectively.</p>
*
* <h4>Example</h4>
*
* <p>Suppose you have an MBean defined by these two Java source files:</p>
*
* <pre>
* // ConfigurationMBean.java
* package com.example;
* public interface ConfigurationMBean {
* public String getName();
* public void save(String fileName);
* }
*
* // Configuration.java
* package com.example;
* public class Configuration implements ConfigurationMBean {
* public Configuration(String defaultName) {
* ...
* }
* ...
* }
* </pre>
*
* <p>Then you could define the default descriptions for the MBean, by
* including a resource bundle called {@code com/example/MBeanDescriptions}
* with the compiled classes. Most often this is done by creating a file
* {@code MBeanDescriptions.properties} in the same directory as {@code
* ConfigurationMBean.java}. Make sure that this file is copied into the
* same place as the compiled classes; in typical build environments that
* will be true by default.</p>
*
* <p>The file {@code com/example/MBeanDescriptions.properties} might
* look like this:</p>
*
* <pre>
* # Description of the MBean
* ConfigurationMBean.mbean = Configuration manager
*
* # Description of the Name attribute
* ConfigurationMBean.attribute.Name = The name of the configuration
*
* # Description of the save operation
* ConfigurationMBean.operation.save = Save the configuration to a file
*
* # Description of the parameter to the save operation.
* # Parameter names from the original Java source are not available,
* # so the default names are p1, p2, etc. If the names were available,
* # this would be ConfigurationMBean.operation.save.fileName
* ConfigurationMBean.operation.save.p1 = The name of the file
*
* # Description of the constructor. The default name of a constructor is
* # its fully-qualified class name.
* ConfigurationMBean.constructor.com.example.Configuration = <!--
* -->Constructor with name of default file
* # Description of the constructor parameter.
* ConfigurationMBean.constructor.com.example.Configuration.p1 = <!--
* -->Name of the default file
* </pre>
*
* <p>Starting with this file, you could create descriptions for the French
* locale by creating {@code com/example/MBeanDescriptions_fr.properties}.
* The keys in this file are the same as before but the text has been
* translated:
*
* <pre>
* ConfigurationMBean.mbean = Gestionnaire de configuration
*
* ConfigurationMBean.attribute.Name = Le nom de la configuration
*
* ConfigurationMBean.operation.save = Sauvegarder la configuration <!--
* -->dans un fichier
*
* ConfigurationMBean.operation.save.p1 = Le nom du fichier
*
* ConfigurationMBean.constructor.com.example.Configuration = <!--
* -->Constructeur avec nom du fichier par d&eacute;faut
* ConfigurationMBean.constructor.com.example.Configuration.p1 = <!--
* -->Nom du fichier par d&eacute;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 &#64;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");
}
}
} }
...@@ -532,8 +532,30 @@ public interface MBeanServerConnection extends NotificationManager { ...@@ -532,8 +532,30 @@ public interface MBeanServerConnection extends NotificationManager {
/** /**
* Enables the values of several attributes of a named MBean. The MBean * <p>Retrieves the values of several attributes of a named MBean. The MBean
* is identified by its object name. * 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 * @param name The object name of the MBean from which the
* attributes are retrieved. * attributes are retrieved.
...@@ -557,6 +579,7 @@ public interface MBeanServerConnection extends NotificationManager { ...@@ -557,6 +579,7 @@ public interface MBeanServerConnection extends NotificationManager {
throws InstanceNotFoundException, ReflectionException, throws InstanceNotFoundException, ReflectionException,
IOException; IOException;
/** /**
* Sets the value of a specific attribute of a named MBean. The MBean * Sets the value of a specific attribute of a named MBean. The MBean
* is identified by its object name. * is identified by its object name.
...@@ -592,10 +615,36 @@ public interface MBeanServerConnection extends NotificationManager { ...@@ -592,10 +615,36 @@ public interface MBeanServerConnection extends NotificationManager {
ReflectionException, IOException; ReflectionException, IOException;
/** /**
* Sets the values of several attributes of a named MBean. The MBean is * <p>Sets the values of several attributes of a named MBean. The MBean is
* identified by its object name. * 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 * @param name The object name of the MBean within which the
* attributes are to be set. * attributes are to be set.
...@@ -622,7 +671,39 @@ public interface MBeanServerConnection extends NotificationManager { ...@@ -622,7 +671,39 @@ public interface MBeanServerConnection extends NotificationManager {
throws InstanceNotFoundException, ReflectionException, IOException; 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 * @param name The object name of the MBean on which the method is
* to be invoked. * to be invoked.
...@@ -630,7 +711,8 @@ public interface MBeanServerConnection extends NotificationManager { ...@@ -630,7 +711,8 @@ public interface MBeanServerConnection extends NotificationManager {
* @param params An array containing the parameters to be set when * @param params An array containing the parameters to be set when
* the operation is invoked * the operation is invoked
* @param signature An array containing the signature of the * @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 * class loader as the one used for loading the MBean on which the
* operation was invoked. * operation was invoked.
* *
......
...@@ -27,15 +27,70 @@ package javax.management; ...@@ -27,15 +27,70 @@ package javax.management;
/** /**
* Represents a notification emitted by the MBean server through the MBeanServerDelegate MBean. * Represents a notification emitted by the MBean Server through the MBeanServerDelegate MBean.
* The MBean Server emits the following types of notifications: MBean registration, MBean * The MBean Server emits the following types of notifications: MBean registration, MBean
* de-registration. * unregistration.
* <P> * <P>
* To receive to MBeanServerNotifications, you need to be declared as listener to * To receive MBeanServerNotifications, you need to register a listener with
* the {@link javax.management.MBeanServerDelegate javax.management.MBeanServerDelegate} MBean * the {@link MBeanServerDelegate MBeanServerDelegate} MBean
* that represents the MBeanServer. The ObjectName of the MBeanServerDelegate is: * that represents the MBeanServer. The ObjectName of the MBeanServerDelegate is
* {@link MBeanServerDelegate#DELEGATE_NAME}, which is
* <CODE>JMImplementation:type=MBeanServerDelegate</CODE>. * <CODE>JMImplementation:type=MBeanServerDelegate</CODE>.
* *
* <p>The following code prints a message every time an MBean is registered
* or unregistered in the MBean Server {@code mbeanServer}:</p>
*
* <pre>
* private static final NotificationListener printListener = new NotificationListener() {
* public void handleNotification(Notification n, Object handback) {
* if (!(n instanceof MBeanServerNotification)) {
* System.out.println("Ignored notification of class " + n.getClass().getName());
* return;
* }
* MBeanServerNotification mbsn = (MBeanServerNotification) n;
* String what;
* if (n.getType().equals(MBeanServerNotification.REGISTRATION_NOTIFICATION))
* what = "MBean registered";
* else if (n.getType().equals(MBeanServerNotification.UNREGISTRATION_NOTIFICATION))
* what = "MBean unregistered";
* else
* what = "Unknown type " + n.getType();
* System.out.println("Received MBean Server notification: " + what + ": " +
* mbsn.getMBeanName());
* };
*
* ...
* mbeanServer.addNotificationListener(
* MBeanServerDelegate.DELEGATE_NAME, printListener, null, null);
* </pre>
*
* <p>The following code prints a message every time an MBean is registered
* or unregistered in the MBean Server {@code mbeanServer}:</p>
*
* <pre>
* private static final NotificationListener printListener = new NotificationListener() {
* public void handleNotification(Notification n, Object handback) {
* if (!(n instanceof MBeanServerNotification)) {
* System.out.println("Ignored notification of class " + n.getClass().getName());
* return;
* }
* MBeanServerNotification mbsn = (MBeanServerNotification) n;
* String what;
* if (n.getType().equals(MBeanServerNotification.REGISTRATION_NOTIFICATION))
* what = "MBean registered";
* else if (n.getType().equals(MBeanServerNotification.UNREGISTRATION_NOTIFICATION))
* what = "MBean unregistered";
* else
* what = "Unknown type " + n.getType();
* System.out.println("Received MBean Server notification: " + what + ": " +
* mbsn.getMBeanName());
* };
*
* ...
* mbeanServer.addNotificationListener(
* MBeanServerDelegate.DELEGATE_NAME, printListener, null, null);
* </pre>
*
* @since 1.5 * @since 1.5
*/ */
public class MBeanServerNotification extends Notification { public class MBeanServerNotification extends Notification {
......
...@@ -54,7 +54,7 @@ import com.sun.jmx.mbeanserver.GetPropertyAction; ...@@ -54,7 +54,7 @@ import com.sun.jmx.mbeanserver.GetPropertyAction;
* @since 1.5 * @since 1.5
*/ */
@SuppressWarnings("serial") // serialVersionUID is not constant @SuppressWarnings("serial") // serialVersionUID is not constant
public class Notification extends EventObject { public class Notification extends EventObject implements Cloneable {
// Serialization compatibility stuff: // Serialization compatibility stuff:
// Two serial forms are supported in this class. The selected form depends // Two serial forms are supported in this class. The selected form depends
...@@ -243,6 +243,26 @@ public class Notification extends EventObject { ...@@ -243,6 +243,26 @@ public class Notification extends EventObject {
this.message = message ; this.message = message ;
} }
/**
* <p>Creates and returns a copy of this object. The copy is created as
* described for {@link Object#clone()}. This means, first, that the
* class of the object will be the same as the class of this object, and,
* second, that the copy is a "shallow copy". Fields of this notification
* are not themselves copied. In particular, the {@linkplain
* #getUserData user data} of the copy is the same object as the
* original.</p>
*
* @return a copy of this object.
*/
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(e);
}
}
/** /**
* Sets the source. * Sets the source.
* *
...@@ -285,8 +305,10 @@ public class Notification extends EventObject { ...@@ -285,8 +305,10 @@ public class Notification extends EventObject {
/** /**
* Get the notification type. * Get the notification type.
* *
* @return The notification type. It's a string expressed in a dot notation similar * @return The notification type. It's a string expressed in a dot notation
* to Java properties. An example of a notification type is network.alarm.router . * similar to Java properties. It is recommended that the notification type
* should follow the reverse-domain-name convention used by Java package
* names. An example of a notification type is com.example.alarm.router.
*/ */
public String getType() { public String getType() {
return type ; return type ;
...@@ -317,14 +339,25 @@ public class Notification extends EventObject { ...@@ -317,14 +339,25 @@ public class Notification extends EventObject {
/** /**
* Get the notification message. * Get the notification message.
* *
* @return The message string of this notification object. It contains in a string, * @return The message string of this notification object.
* which could be the explanation of the notification for displaying to a user
* *
* @see #setMessage
*/ */
public String getMessage() { public String getMessage() {
return message ; return message ;
} }
/**
* Set the notification message.
*
* @param message the new notification message.
*
* @see #getMessage
*/
public void setMessage(String message) {
this.message = message;
}
/** /**
* Get the user data. * Get the user data.
* *
...@@ -355,6 +388,7 @@ public class Notification extends EventObject { ...@@ -355,6 +388,7 @@ public class Notification extends EventObject {
* *
* @return A String representation of this notification. * @return A String representation of this notification.
*/ */
@Override
public String toString() { public String toString() {
return super.toString()+"[type="+type+"][message="+message+"]"; return super.toString()+"[type="+type+"][message="+message+"]";
} }
......
...@@ -26,7 +26,6 @@ ...@@ -26,7 +26,6 @@
package javax.management; package javax.management;
import com.sun.jmx.mbeanserver.NotificationMBeanSupport; import com.sun.jmx.mbeanserver.NotificationMBeanSupport;
import com.sun.jmx.mbeanserver.Util;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
...@@ -43,6 +42,11 @@ import java.util.Set; ...@@ -43,6 +42,11 @@ import java.util.Set;
* on both the client and the server in the remote case, so using this class * on both the client and the server in the remote case, so using this class
* instead is recommended where possible.</p> * 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 * <p>This class uses the {@linkplain Query Query API} to specify the
* filtering logic. For example, to select only notifications where the * filtering logic. For example, to select only notifications where the
* {@linkplain Notification#getType() type} is {@code "com.example.mytype"}, * {@linkplain Notification#getType() type} is {@code "com.example.mytype"},
......
...@@ -29,7 +29,6 @@ import com.sun.jmx.event.DaemonThreadFactory; ...@@ -29,7 +29,6 @@ import com.sun.jmx.event.DaemonThreadFactory;
import com.sun.jmx.event.LeaseRenewer; import com.sun.jmx.event.LeaseRenewer;
import com.sun.jmx.event.ReceiverBuffer; import com.sun.jmx.event.ReceiverBuffer;
import com.sun.jmx.event.RepeatedSingletonJob; import com.sun.jmx.event.RepeatedSingletonJob;
import com.sun.jmx.namespace.JMXNamespaceUtils;
import com.sun.jmx.mbeanserver.PerThreadGroupPool; import com.sun.jmx.mbeanserver.PerThreadGroupPool;
import com.sun.jmx.remote.util.ClassLogger; import com.sun.jmx.remote.util.ClassLogger;
...@@ -58,7 +57,6 @@ import javax.management.NotificationBroadcasterSupport; ...@@ -58,7 +57,6 @@ import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationFilter; import javax.management.NotificationFilter;
import javax.management.NotificationListener; import javax.management.NotificationListener;
import javax.management.ObjectName; import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.NotificationResult; import javax.management.remote.NotificationResult;
import javax.management.remote.TargetedNotification; import javax.management.remote.TargetedNotification;
...@@ -129,11 +127,12 @@ public class EventClient implements EventConsumer, NotificationManager { ...@@ -129,11 +127,12 @@ public class EventClient implements EventConsumer, NotificationManager {
public static final String NOTIFS_LOST = "jmx.event.service.notifs.lost"; public static final String NOTIFS_LOST = "jmx.event.service.notifs.lost";
/** /**
* The default lease time, {@value}, in milliseconds. * The default lease time that EventClient instances will request, in
* milliseconds. This value is {@value}.
* *
* @see EventClientDelegateMBean#lease * @see EventClientDelegateMBean#lease
*/ */
public static final long DEFAULT_LEASE_TIMEOUT = 300000; public static final long DEFAULT_REQUESTED_LEASE_TIME = 300000;
/** /**
* <p>Constructs a default {@code EventClient} object.</p> * <p>Constructs a default {@code EventClient} object.</p>
...@@ -173,7 +172,7 @@ public class EventClient implements EventConsumer, NotificationManager { ...@@ -173,7 +172,7 @@ public class EventClient implements EventConsumer, NotificationManager {
*/ */
public EventClient(EventClientDelegateMBean delegate) public EventClient(EventClientDelegateMBean delegate)
throws IOException { throws IOException {
this(delegate, null, null, null, DEFAULT_LEASE_TIMEOUT); this(delegate, null, null, null, DEFAULT_REQUESTED_LEASE_TIME);
} }
/** /**
...@@ -196,7 +195,7 @@ public class EventClient implements EventConsumer, NotificationManager { ...@@ -196,7 +195,7 @@ public class EventClient implements EventConsumer, NotificationManager {
* If {@code null}, a default scheduler will be used. * If {@code null}, a default scheduler will be used.
* @param requestedLeaseTime The lease time used to keep this client alive * @param requestedLeaseTime The lease time used to keep this client alive
* in the {@link EventClientDelegateMBean}. A value of zero is equivalent * in the {@link EventClientDelegateMBean}. A value of zero is equivalent
* to the {@linkplain #DEFAULT_LEASE_TIMEOUT default value}. * to the {@linkplain #DEFAULT_REQUESTED_LEASE_TIME default value}.
* *
* @throws IllegalArgumentException If {@code delegate} is null. * @throws IllegalArgumentException If {@code delegate} is null.
* @throws IOException If an I/O error occurs when communicating with the * @throws IOException If an I/O error occurs when communicating with the
...@@ -213,7 +212,7 @@ public class EventClient implements EventConsumer, NotificationManager { ...@@ -213,7 +212,7 @@ public class EventClient implements EventConsumer, NotificationManager {
} }
if (requestedLeaseTime == 0) if (requestedLeaseTime == 0)
requestedLeaseTime = DEFAULT_LEASE_TIMEOUT; requestedLeaseTime = DEFAULT_REQUESTED_LEASE_TIME;
else if (requestedLeaseTime < 0) { else if (requestedLeaseTime < 0) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Negative lease time: " + requestedLeaseTime); "Negative lease time: " + requestedLeaseTime);
...@@ -269,7 +268,13 @@ public class EventClient implements EventConsumer, NotificationManager { ...@@ -269,7 +268,13 @@ public class EventClient implements EventConsumer, NotificationManager {
new ScheduledThreadPoolExecutor(20, daemonThreadFactory); new ScheduledThreadPoolExecutor(20, daemonThreadFactory);
executor.setKeepAliveTime(1, TimeUnit.SECONDS); executor.setKeepAliveTime(1, TimeUnit.SECONDS);
executor.allowCoreThreadTimeOut(true); executor.allowCoreThreadTimeOut(true);
executor.setRemoveOnCancelPolicy(true); if (setRemoveOnCancelPolicy != null) {
try {
setRemoveOnCancelPolicy.invoke(executor, true);
} catch (Exception e) {
logger.trace("setRemoveOnCancelPolicy", e);
}
}
// By default, a ScheduledThreadPoolExecutor will keep jobs // By default, a ScheduledThreadPoolExecutor will keep jobs
// in its queue even after they have been cancelled. They // in its queue even after they have been cancelled. They
// will only be removed when their scheduled time arrives. // will only be removed when their scheduled time arrives.
...@@ -277,12 +282,25 @@ public class EventClient implements EventConsumer, NotificationManager { ...@@ -277,12 +282,25 @@ public class EventClient implements EventConsumer, NotificationManager {
// this EventClient, this can lead to a moderately large number // this EventClient, this can lead to a moderately large number
// of objects remaining referenced until the renewal time // of objects remaining referenced until the renewal time
// arrives. Hence the above call, which removes the job from // arrives. Hence the above call, which removes the job from
// the queue as soon as it is cancelled. // the queue as soon as it is cancelled. Since the call is
// new with JDK 7, we invoke it via reflection to make it
// easier to use this code on JDK 6.
return executor; return executor;
} }
}; };
return leaseRenewerThreadPool.getThreadPoolExecutor(create); return leaseRenewerThreadPool.getThreadPoolExecutor(create);
}
private static final Method setRemoveOnCancelPolicy;
static {
Method m;
try {
m = ScheduledThreadPoolExecutor.class.getMethod(
"setRemoveOnCancelPolicy", boolean.class);
} catch (Exception e) {
m = null;
}
setRemoveOnCancelPolicy = m;
} }
/** /**
...@@ -1042,7 +1060,7 @@ public class EventClient implements EventConsumer, NotificationManager { ...@@ -1042,7 +1060,7 @@ public class EventClient implements EventConsumer, NotificationManager {
final public EventClient call() throws Exception { final public EventClient call() throws Exception {
EventClientDelegateMBean ecd = EventClientDelegate.getProxy(conn); EventClientDelegateMBean ecd = EventClientDelegate.getProxy(conn);
return new EventClient(ecd, eventRelay, null, null, return new EventClient(ecd, eventRelay, null, null,
DEFAULT_LEASE_TIMEOUT); DEFAULT_REQUESTED_LEASE_TIME);
} }
}; };
...@@ -1080,24 +1098,6 @@ public class EventClient implements EventConsumer, NotificationManager { ...@@ -1080,24 +1098,6 @@ public class EventClient implements EventConsumer, NotificationManager {
return clientId; return clientId;
} }
/**
* Returns a JMX Connector that will use an {@link EventClient}
* to subscribe for notifications. If the server doesn't have
* an {@link EventClientDelegateMBean}, then the connector will
* use the legacy notification mechanism instead.
*
* @param wrapped The underlying JMX Connector wrapped by the returned
* connector.
*
* @return A JMX Connector that will uses an {@link EventClient}, if
* available.
*
* @see EventClient#getEventClientConnection(MBeanServerConnection)
*/
public static JMXConnector withEventClient(final JMXConnector wrapped) {
return JMXNamespaceUtils.withEventClient(wrapped);
}
private static final PerThreadGroupPool<ScheduledThreadPoolExecutor> private static final PerThreadGroupPool<ScheduledThreadPoolExecutor>
leaseRenewerThreadPool = PerThreadGroupPool.make(); leaseRenewerThreadPool = PerThreadGroupPool.make();
} }
...@@ -149,6 +149,7 @@ public class EventClientDelegate implements EventClientDelegateMBean { ...@@ -149,6 +149,7 @@ public class EventClientDelegate implements EventClientDelegateMBean {
// of a setMBeanServer on some other forwarder later in the chain. // of a setMBeanServer on some other forwarder later in the chain.
private static class Forwarder extends SingleMBeanForwarder { private static class Forwarder extends SingleMBeanForwarder {
private MBeanServer loopMBS;
private static class UnsupportedInvocationHandler private static class UnsupportedInvocationHandler
implements InvocationHandler { implements InvocationHandler {
...@@ -173,7 +174,11 @@ public class EventClientDelegate implements EventClientDelegateMBean { ...@@ -173,7 +174,11 @@ public class EventClientDelegate implements EventClientDelegateMBean {
private volatile boolean madeECD; private volatile boolean madeECD;
Forwarder() { Forwarder() {
super(OBJECT_NAME, makeUnsupportedECD()); super(OBJECT_NAME, makeUnsupportedECD(), true);
}
synchronized void setLoopMBS(MBeanServer loopMBS) {
this.loopMBS = loopMBS;
} }
@Override @Override
...@@ -186,7 +191,7 @@ public class EventClientDelegate implements EventClientDelegateMBean { ...@@ -186,7 +191,7 @@ public class EventClientDelegate implements EventClientDelegateMBean {
AccessController.doPrivileged( AccessController.doPrivileged(
new PrivilegedAction<EventClientDelegate>() { new PrivilegedAction<EventClientDelegate>() {
public EventClientDelegate run() { public EventClientDelegate run() {
return getEventClientDelegate(Forwarder.this); return getEventClientDelegate(loopMBS);
} }
}); });
DynamicMBean mbean = new StandardMBean( DynamicMBean mbean = new StandardMBean(
...@@ -208,11 +213,46 @@ public class EventClientDelegate implements EventClientDelegateMBean { ...@@ -208,11 +213,46 @@ public class EventClientDelegate implements EventClientDelegateMBean {
* that are targeted for that MBean and handles them itself. All other * that are targeted for that MBean and handles them itself. All other
* requests are forwarded to the next element in the forwarder chain.</p> * requests are forwarded to the next element in the forwarder chain.</p>
* *
* @param nextMBS the next {@code MBeanServer} in the chain of forwarders,
* which might be another {@code MBeanServerForwarder} or a plain {@code
* MBeanServer}. This is the object to which {@code MBeanServer} requests
* that do not concern the {@code EventClientDelegateMBean} are sent.
* It will be the value of {@link MBeanServerForwarder#getMBeanServer()
* getMBeanServer()} on the returned object, and can be changed with {@link
* MBeanServerForwarder#setMBeanServer setMBeanServer}. It can be null but
* must be set to a non-null value before any {@code MBeanServer} requests
* arrive.
*
* @param loopMBS the {@code MBeanServer} to which requests from the
* {@code EventClientDelegateMBean} should be sent. For example,
* when you invoke the {@link EventClientDelegateMBean#addListener
* addListener} operation on the {@code EventClientDelegateMBean}, it will
* result in a call to {@link
* MBeanServer#addNotificationListener(ObjectName, NotificationListener,
* NotificationFilter, Object) addNotificationListener} on this object.
* If this parameter is null, then these requests will be sent to the
* newly-created {@code MBeanServerForwarder}. Usually the parameter will
* either be null or will be the result of {@link
* javax.management.remote.JMXConnectorServer#getSystemMBeanServerForwarder()
* getSystemMBeanServerForwarder()} for the connector server in which
* this forwarder will be installed.
*
* @return a new {@code MBeanServerForwarder} that simulates the existence * @return a new {@code MBeanServerForwarder} that simulates the existence
* of an {@code EventClientDelegateMBean}. * of an {@code EventClientDelegateMBean}.
*
* @see javax.management.remote.JMXConnectorServer#installStandardForwarders
*/ */
public static MBeanServerForwarder newForwarder() { public static MBeanServerForwarder newForwarder(
return new Forwarder(); MBeanServer nextMBS, MBeanServer loopMBS) {
Forwarder mbsf = new Forwarder();
// We must setLoopMBS before setMBeanServer, because when we
// setMBeanServer that will call getEventClientDelegate(loopMBS).
if (loopMBS == null)
loopMBS = mbsf;
mbsf.setLoopMBS(loopMBS);
if (nextMBS != null)
mbsf.setMBeanServer(nextMBS);
return mbsf;
} }
/** /**
...@@ -437,10 +477,9 @@ public class EventClientDelegate implements EventClientDelegateMBean { ...@@ -437,10 +477,9 @@ public class EventClientDelegate implements EventClientDelegateMBean {
// private classes // private classes
// ------------------------------------ // ------------------------------------
private class ClientInfo { private class ClientInfo {
String clientId; final String clientId;
EventBuffer buffer; final NotificationListener clientListener;
NotificationListener clientListener; final Map<Integer, AddedListener> listenerInfoMap =
Map<Integer, AddedListener> listenerInfoMap =
new HashMap<Integer, AddedListener>(); new HashMap<Integer, AddedListener>();
ClientInfo(String clientId, EventForwarder forwarder) { ClientInfo(String clientId, EventForwarder forwarder) {
...@@ -703,7 +742,8 @@ public class EventClientDelegate implements EventClientDelegateMBean { ...@@ -703,7 +742,8 @@ public class EventClientDelegate implements EventClientDelegateMBean {
clientInfo = clientInfoMap.get(clientId); clientInfo = clientInfoMap.get(clientId);
if (clientInfo == null) { if (clientInfo == null) {
throw new EventClientNotFoundException("The client is not found."); throw new EventClientNotFoundException(
"Client not found (id " + clientId + ")");
} }
return clientInfo; return clientInfo;
......
...@@ -51,7 +51,8 @@ import javax.management.remote.NotificationResult; ...@@ -51,7 +51,8 @@ import javax.management.remote.NotificationResult;
* and the MBean Server, that will intercept accesses to the Event Client * and the MBean Server, that will intercept accesses to the Event Client
* Delegate MBean and treat them as the real MBean would. This forwarder is * Delegate MBean and treat them as the real MBean would. This forwarder is
* inserted by default with the standard RMI Connector Server, and can also * inserted by default with the standard RMI Connector Server, and can also
* be created explicitly using {@link EventClientDelegate#newForwarder()}. * be created explicitly using {@link EventClientDelegate#newForwarder
* EventClientDelegate.newForwarder}.
* *
* <li><p>A variant on the above is to replace the MBean Server that is * <li><p>A variant on the above is to replace the MBean Server that is
* used locally with a forwarder as described above. Since * used locally with a forwarder as described above. Since
...@@ -61,9 +62,7 @@ import javax.management.remote.NotificationResult; ...@@ -61,9 +62,7 @@ import javax.management.remote.NotificationResult;
* *
* <pre> * <pre>
* MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); // or whatever * MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); // or whatever
* MBeanServerForwarder mbsf = EventClientDelegate.newForwarder(); * mbs = EventClientDelegate.newForwarder(mbs, null);
* mbsf.setMBeanServer(mbs);
* mbs = mbsf;
* // now use mbs just as you did before, but it will have an EventClientDelegate * // now use mbs just as you did before, but it will have an EventClientDelegate
* </pre> * </pre>
* *
......
...@@ -27,7 +27,6 @@ package javax.management.event; ...@@ -27,7 +27,6 @@ package javax.management.event;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.Executors; // for javadoc import java.util.concurrent.Executors; // for javadoc
import java.util.concurrent.ScheduledFuture;
/** /**
* This interface is used to specify a way to receive * This interface is used to specify a way to receive
......
...@@ -83,8 +83,8 @@ ...@@ -83,8 +83,8 @@
* javax.management.event.EventClientDelegateMBean EventClientDelegateMBean} * javax.management.event.EventClientDelegateMBean EventClientDelegateMBean}
* must be registered in the MBean Server, or the connector server must * must be registered in the MBean Server, or the connector server must
* be configured to simulate the existence of this MBean, for example * be configured to simulate the existence of this MBean, for example
* using {@link javax.management.event.EventClientDelegate#newForwarder() * using {@link javax.management.event.EventClientDelegate#newForwarder
* EventClientDelegate.newForwarder()}. The standard RMI connector is so * EventClientDelegate.newForwarder}. The standard RMI connector is so
* configured by default. The {@code EventClientDelegateMBean} documentation * configured by default. The {@code EventClientDelegateMBean} documentation
* has further details.</p> * has further details.</p>
* *
......
...@@ -26,21 +26,19 @@ ...@@ -26,21 +26,19 @@
package javax.management.namespace; package javax.management.namespace;
import com.sun.jmx.defaults.JmxProperties; import com.sun.jmx.defaults.JmxProperties;
import com.sun.jmx.namespace.JMXNamespaceUtils;
import com.sun.jmx.namespace.ObjectNameRouter; import com.sun.jmx.namespace.ObjectNameRouter;
import com.sun.jmx.namespace.serial.RewritingProcessor; import com.sun.jmx.namespace.serial.RewritingProcessor;
import com.sun.jmx.namespace.RoutingConnectionProxy; import com.sun.jmx.namespace.RoutingConnectionProxy;
import com.sun.jmx.namespace.RoutingServerProxy; import com.sun.jmx.namespace.RoutingServerProxy;
import java.io.IOException;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanServer; import javax.management.MBeanServer;
import javax.management.MBeanServerConnection; import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException; import javax.management.MalformedObjectNameException;
import javax.management.ObjectName; import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
/** /**
* Static constants and utility methods to help work with * Static constants and utility methods to help work with
...@@ -68,23 +66,6 @@ public class JMXNamespaces { ...@@ -68,23 +66,6 @@ public class JMXNamespaces {
NAMESPACE_SEPARATOR.length(); NAMESPACE_SEPARATOR.length();
/**
* Returns a connector connected to a sub name space exposed through
* the parent connector.
* @param parent the parent connector.
* @param namespace the {@linkplain javax.management.namespace name space}
* to which the returned connector is
* connected.
* @return A connector connected to a sub name space exposed through
* the parent connector.
**/
public static JMXConnector narrowToNamespace(final JMXConnector parent,
final String namespace)
throws IOException {
return JMXNamespaceUtils.cd(parent,namespace,true);
}
/** /**
* Creates a new {@code MBeanServerConnection} proxy on a * Creates a new {@code MBeanServerConnection} proxy on a
* {@linkplain javax.management.namespace sub name space} * {@linkplain javax.management.namespace sub name space}
...@@ -96,15 +77,18 @@ public class JMXNamespaces { ...@@ -96,15 +77,18 @@ public class JMXNamespaces {
* name space} in which to narrow. * name space} in which to narrow.
* @return A new {@code MBeanServerConnection} proxy that shows the content * @return A new {@code MBeanServerConnection} proxy that shows the content
* of that name space. * of that name space.
* @throws IllegalArgumentException if the name space does not exist, or * @throws IllegalArgumentException if either argument is null,
* if a proxy for that name space cannot be created. * or the name space does not exist, or if a proxy for that name space
* cannot be created. The {@linkplain Throwable#getCause() cause} of
* this exception will be an {@link InstanceNotFoundException} if and only
* if the name space is found not to exist.
*/ */
public static MBeanServerConnection narrowToNamespace( public static MBeanServerConnection narrowToNamespace(
MBeanServerConnection parent, MBeanServerConnection parent,
String namespace) { String namespace) {
if (LOG.isLoggable(Level.FINER)) if (LOG.isLoggable(Level.FINER))
LOG.finer("Making MBeanServerConnection for: " +namespace); LOG.finer("Making MBeanServerConnection for: " +namespace);
return RoutingConnectionProxy.cd(parent,namespace); return RoutingConnectionProxy.cd(parent, namespace, true);
} }
/** /**
...@@ -120,13 +104,15 @@ public class JMXNamespaces { ...@@ -120,13 +104,15 @@ public class JMXNamespaces {
* of that name space. * of that name space.
* @throws IllegalArgumentException if either argument is null, * @throws IllegalArgumentException if either argument is null,
* or the name space does not exist, or if a proxy for that name space * or the name space does not exist, or if a proxy for that name space
* cannot be created. * cannot be created. The {@linkplain Throwable#getCause() cause} of
* this exception will be an {@link InstanceNotFoundException} if and only
* if the name space is found not to exist.
*/ */
public static MBeanServer narrowToNamespace(MBeanServer parent, public static MBeanServer narrowToNamespace(MBeanServer parent,
String namespace) { String namespace) {
if (LOG.isLoggable(Level.FINER)) if (LOG.isLoggable(Level.FINER))
LOG.finer("Making NamespaceServerProxy for: " +namespace); LOG.finer("Making MBeanServer for: " +namespace);
return RoutingServerProxy.cd(parent,namespace); return RoutingServerProxy.cd(parent, namespace, true);
} }
/** /**
...@@ -266,7 +252,7 @@ public class JMXNamespaces { ...@@ -266,7 +252,7 @@ public class JMXNamespaces {
ObjectNameRouter.normalizeNamespacePath(namespace,false, ObjectNameRouter.normalizeNamespacePath(namespace,false,
true,false); true,false);
try { try {
// We could use Util.newObjectName here - but throwing an // We could use ObjectName.valueOf here - but throwing an
// IllegalArgumentException that contains just the supplied // IllegalArgumentException that contains just the supplied
// namespace instead of the whole ObjectName seems preferable. // namespace instead of the whole ObjectName seems preferable.
return ObjectName.getInstance(sourcePath+ return ObjectName.getInstance(sourcePath+
......
...@@ -27,10 +27,10 @@ package javax.management.namespace; ...@@ -27,10 +27,10 @@ package javax.management.namespace;
import com.sun.jmx.defaults.JmxProperties; import com.sun.jmx.defaults.JmxProperties;
import com.sun.jmx.mbeanserver.Util; import com.sun.jmx.mbeanserver.Util;
import com.sun.jmx.namespace.JMXNamespaceUtils;
import com.sun.jmx.remote.util.EnvHelp; import com.sun.jmx.remote.util.EnvHelp;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
...@@ -39,6 +39,7 @@ import java.util.logging.Logger; ...@@ -39,6 +39,7 @@ import java.util.logging.Logger;
import javax.management.AttributeChangeNotification; import javax.management.AttributeChangeNotification;
import javax.management.ClientContext;
import javax.management.InstanceNotFoundException; import javax.management.InstanceNotFoundException;
import javax.management.ListenerNotFoundException; import javax.management.ListenerNotFoundException;
import javax.management.MBeanNotificationInfo; import javax.management.MBeanNotificationInfo;
...@@ -220,17 +221,26 @@ public class JMXRemoteNamespace ...@@ -220,17 +221,26 @@ public class JMXRemoteNamespace
initParentOnce(this); initParentOnce(this);
// URL must not be null. // URL must not be null.
this.jmxURL = JMXNamespaceUtils.checkNonNull(sourceURL,"url"); if (sourceURL == null)
throw new IllegalArgumentException("Null URL");
this.jmxURL = sourceURL;
this.broadcaster = this.broadcaster =
new NotificationBroadcasterSupport(connectNotification); new NotificationBroadcasterSupport(connectNotification);
// handles options // handles options
this.optionsMap = JMXNamespaceUtils.unmodifiableMap(optionsMap); this.optionsMap = unmodifiableMap(optionsMap);
// handles (dis)connection events // handles (dis)connection events
this.listener = new ConnectionListener(); this.listener = new ConnectionListener();
} }
// returns un unmodifiable view of a map.
private static <K,V> Map<K,V> unmodifiableMap(Map<K,V> aMap) {
if (aMap == null || aMap.isEmpty())
return Collections.emptyMap();
return Collections.unmodifiableMap(aMap);
}
/** /**
* Returns the {@code JMXServiceURL} that is (or will be) used to * Returns the {@code JMXServiceURL} that is (or will be) used to
* connect to the remote name space. <p> * connect to the remote name space. <p>
...@@ -483,106 +493,171 @@ public class JMXRemoteNamespace ...@@ -483,106 +493,171 @@ public class JMXRemoteNamespace
} }
} }
JMXConnector connect(JMXServiceURL url, Map<String,?> env) private JMXConnector connect(JMXServiceURL url, Map<String,?> env)
throws IOException { throws IOException {
final JMXConnector c = newJMXConnector(jmxURL, env); final JMXConnector c = newJMXConnector(url, env);
c.connect(env); c.connect(env);
return c; return c;
} }
/** /**
* Creates a new JMXConnector with the specified {@code url} and * <p>Creates a new JMXConnector with the specified {@code url} and
* {@code env} options map. * {@code env} options map. The default implementation of this method
* <p> * returns {@link JMXConnectorFactory#newJMXConnector
* This method first calls {@link JMXConnectorFactory#newJMXConnector * JMXConnectorFactory.newJMXConnector(jmxURL, env)}. Subclasses can
* JMXConnectorFactory.newJMXConnector(jmxURL, env)} to obtain a new * override this method to customize behavior.</p>
* JMX connector, and returns that. *
* </p> * @param url The JMXServiceURL of the remote server.
* <p> * @param optionsMap An options map that will be passed to the
* A subclass of {@link JMXRemoteNamespace} can provide an implementation * {@link JMXConnectorFactory} when {@linkplain
* that connects to a sub namespace of the remote server by subclassing * JMXConnectorFactory#newJMXConnector creating} the
* this class in the following way: * {@link JMXConnector} that can connect to the remote source
* MBean Server.
* @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 {
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> * <pre>
* class JMXRemoteSubNamespace extends JMXRemoteNamespace { * class JMXRemoteSubNamespace extends JMXRemoteNamespace {
* private final String subnamespace; * private final String subnamespace;
* JMXRemoteSubNamespace(JMXServiceURL url, *
* Map{@code <String,?>} env, String subnamespace) { * JMXRemoteSubNamespace(
* super(url,options); * JMXServiceURL url, Map{@code <String, ?>} env, String subnamespace) {
* super(url, env);
* this.subnamespace = subnamespace; * this.subnamespace = subnamespace;
* } * }
* protected JMXConnector newJMXConnector(JMXServiceURL url, *
* Map<String,?> env) throws IOException { * {@code @Override}
* final JMXConnector inner = super.newJMXConnector(url,env); * protected MBeanServerConnection getMBeanServerConnection(
* return {@link JMXNamespaces#narrowToNamespace(JMXConnector,String) * JMXConnector jmxc) throws IOException {
* JMXNamespaces.narrowToNamespace(inner,subnamespace)}; * MBeanServerConnection mbsc = super.getMBeanServerConnection(jmxc);
* return {@link JMXNamespaces#narrowToNamespace(MBeanServerConnection,String)
* JMXNamespaces.narrowToNamespace(mbsc, subnamespace)};
* } * }
* } * }
* </pre> * </pre>
* </p> *
* <p> * <h4>Example: using the Event Service for notifications</h4>
* 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 * <p>Some connectors may have been designed to work with an earlier
* new {@linkplain javax.management.event Event Service} defined in this * version of the JMX API, and may not have been upgraded to use
* version of the JMX API. * the {@linkplain javax.management.event Event Service} defined in
* <p> * this version of the JMX API. In that case, and if the remote
* In that case, and if the remote server to which this JMXRemoteNamespace * server to which this JMXRemoteNamespace connects also contains
* connects also contains namespaces, it may be necessary to configure * namespaces, it may be necessary to configure explicitly an {@linkplain
* explicitly an {@linkplain * javax.management.event.EventClientDelegate#newForwarder Event Client
* javax.management.event.EventClientDelegate#newForwarder() * Forwarder} on the remote server side, and to force the use of an {@link
* Event Client Forwarder} on the remote server side, and to force the use * EventClient} on this client side.</p>
* of an {@link EventClient} on this client side. *
* <br> * <p>A subclass of {@link JMXRemoteNamespace} can provide an
* A subclass of {@link JMXRemoteNamespace} can provide an implementation * implementation of {@code getMBeanServerConnection} that will force
* of {@code newJMXConnector} that will force notification subscriptions * notification subscriptions to flow through an {@link EventClient} over
* to flow through an {@link EventClient} over a legacy protocol by * a legacy protocol. It can do so by overriding this method in the
* overriding this method in the following way: * following way:</p>
* </p> *
* <pre> * <pre>
* class JMXRemoteEventClientNamespace extends JMXRemoteNamespace { * class JMXRemoteEventClientNamespace extends JMXRemoteNamespace {
* JMXRemoteSubNamespaceConnector(JMXServiceURL url, * JMXRemoteEventClientNamespace(JMXServiceURL url, {@code Map<String,?>} env) {
* Map<String,?> env) { * super(url, env);
* super(url,options);
* } * }
* protected JMXConnector newJMXConnector(JMXServiceURL url, *
* Map<String,?> env) throws IOException { * {@code @Override}
* final JMXConnector inner = super.newJMXConnector(url,env); * protected MBeanServerConnection getMBeanServerConnection(JMXConnector jmxc)
* return {@link EventClient#withEventClient( * throws IOException {
* JMXConnector) EventClient.withEventClient(inner)}; * MBeanServerConnection mbsc = super.getMBeanServerConnection(jmxc);
* return EventClient.getEventClientConnection(mbsc);
* } * }
* } * }
* </pre> * </pre>
*
* <p> * <p>
* Note that the remote server also needs to provide an {@link * Note that the remote server also needs to provide an {@link
* javax.management.event.EventClientDelegateMBean}: only configuring * javax.management.event.EventClientDelegateMBean}: configuring only
* the client side (this object) is not enough.<br> * the client side (this object) is not enough.</p>
* In summary, this technique should be used if the remote server *
* <p>In summary, this technique should be used if the remote server
* supports JMX namespaces, but uses a JMX Connector Server whose * supports JMX namespaces, but uses a JMX Connector Server whose
* implementation does not transparently use the new Event Service * implementation does not transparently use the new Event Service
* (as would be the case with the JMXMPConnectorServer implementation * (as would be the case with the JMXMPConnectorServer implementation
* from the reference implementation of the JMX Remote API 1.0 * from the reference implementation of the JMX Remote API 1.0
* specification). * specification).</p>
* </p> *
* @param url The JMXServiceURL of the remote server. * @param jmxc the newly-created {@code JMXConnector}.
* @param optionsMap An unmodifiable options map that will be passed to the *
* {@link JMXConnectorFactory} when {@linkplain * @return an {@code MBeanServerConnection} connected to the remote
* JMXConnectorFactory#newJMXConnector creating} the * MBeanServer.
* {@link JMXConnector} that can connect to the remote source *
* MBean Server. * @throws IOException if the connection cannot be made. If this method
* @return An unconnected JMXConnector to use to connect to the remote * throws {@code IOException} then the calling {@link #connect()} method
* server * will also fail with an {@code IOException}.
* @throws java.io.IOException if the connector could not be created. *
* @see JMXConnectorFactory#newJMXConnector(javax.management.remote.JMXServiceURL, java.util.Map) * @see #connect
* @see #JMXRemoteNamespace
*/ */
protected JMXConnector newJMXConnector(JMXServiceURL url, protected MBeanServerConnection getMBeanServerConnection(JMXConnector jmxc)
Map<String,?> optionsMap) throws IOException { throws IOException {
final JMXConnector c = final MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
JMXConnectorFactory.newJMXConnector(jmxURL, optionsMap); try {
// TODO: uncomment this when contexts are added return ClientContext.withDynamicContext(mbsc);
// return ClientContext.withDynamicContext(c); } catch (IllegalArgumentException e) {
return c; LOG.log(Level.FINER, "ClientContext.withDynamicContext", e);
return mbsc;
}
} }
/**
* {@inheritDoc}
*
* <p>The sequence of events when this method is called includes,
* effectively, the following code:</p>
*
* <pre>
* JMXServiceURL url = {@link #getJMXServiceURL getJMXServiceURL}();
* JMXConnector jmxc = {@link #newJMXConnector newJMXConnector}(url, env);
* jmxc.connect();
* MBeanServerConnection mbsc = {@link #getMBeanServerConnection(JMXConnector)
* getMBeanServerConnection}(jmxc);
* </pre>
*
* <p>Here, {@code env} is a {@code Map} containing the entries from the
* {@code optionsMap} that was passed to the {@linkplain #JMXRemoteNamespace
* constructor} or to the {@link #newJMXRemoteNamespace newJMXRemoteNamespace}
* factory method.</p>
*
* <p>Subclasses can customize connection behavior by overriding the
* {@code getJMXServiceURL}, {@code newJMXConnector}, or
* {@code getMBeanServerConnection} methods.</p>
*/
public void connect() throws IOException { public void connect() throws IOException {
LOG.fine("connecting..."); LOG.fine("connecting...");
final Map<String,Object> env = final Map<String,Object> env =
...@@ -590,7 +665,7 @@ public class JMXRemoteNamespace ...@@ -590,7 +665,7 @@ public class JMXRemoteNamespace
try { try {
// XXX: We should probably document this... // XXX: We should probably document this...
// This allows to specify a loader name - which will be // This allows to specify a loader name - which will be
// retrieved from the paret MBeanServer. // retrieved from the parent MBeanServer.
defaultClassLoader = defaultClassLoader =
EnvHelp.resolveServerClassLoader(env,getMBeanServer()); EnvHelp.resolveServerClassLoader(env,getMBeanServer());
} catch (InstanceNotFoundException x) { } catch (InstanceNotFoundException x) {
...@@ -604,7 +679,7 @@ public class JMXRemoteNamespace ...@@ -604,7 +679,7 @@ public class JMXRemoteNamespace
final JMXConnector aconn = connect(url,env); final JMXConnector aconn = connect(url,env);
final MBeanServerConnection msc; final MBeanServerConnection msc;
try { try {
msc = aconn.getMBeanServerConnection(); msc = getMBeanServerConnection(aconn);
aconn.addConnectionNotificationListener(listener,null,aconn); aconn.addConnectionNotificationListener(listener,null,aconn);
} catch (IOException io) { } catch (IOException io) {
close(aconn); close(aconn);
......
...@@ -33,12 +33,14 @@ import java.io.Serializable; ...@@ -33,12 +33,14 @@ import java.io.Serializable;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.SortedMap; import java.util.SortedMap;
import java.util.TreeMap; import java.util.TreeMap;
// jmx import // jmx import
import java.util.TreeSet;
// //
...@@ -60,16 +62,15 @@ public class CompositeDataSupport ...@@ -60,16 +62,15 @@ public class CompositeDataSupport
* respective values. * respective values.
* A {@link SortedMap} is used for faster retrieval of elements. * 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. * @serial The <i>composite type </i> of this <i>composite data</i> instance.
*/ */
private CompositeType compositeType; private final CompositeType compositeType;
/** /**
* <p> * <p>Constructs a <tt>CompositeDataSupport</tt> instance with the specified
* Constructs a <tt>CompositeDataSupport</tt> instance with the specified
* <tt>compositeType</tt>, whose item values * <tt>compositeType</tt>, whose item values
* are specified by <tt>itemValues[]</tt>, in the same order as in * are specified by <tt>itemValues[]</tt>, in the same order as in
* <tt>itemNames[]</tt>. * <tt>itemNames[]</tt>.
...@@ -79,103 +80,67 @@ public class CompositeDataSupport ...@@ -79,103 +80,67 @@ public class CompositeDataSupport
* The items contained in this <tt>CompositeDataSupport</tt> instance are * The items contained in this <tt>CompositeDataSupport</tt> instance are
* internally stored in a <tt>TreeMap</tt>, * internally stored in a <tt>TreeMap</tt>,
* thus sorted in ascending lexicographic order of their names, for faster * thus sorted in ascending lexicographic order of their names, for faster
* retrieval of individual item values. * retrieval of individual item values.</p>
* <p> *
* The constructor checks that all the constraints listed below for each * <p>The constructor checks that all the constraints listed below for each
* parameter are satisfied, * parameter are satisfied,
* and throws the appropriate exception if they are not. * and throws the appropriate exception if they are not.</p>
* <p> *
* @param compositeType the <i>composite type </i> of this <i>composite * @param compositeType the <i>composite type </i> of this <i>composite
* data</i> instance; * data</i> instance; must not be null.
* must not be null. *
* <p>
* @param itemNames <tt>itemNames</tt> must list, in any order, all the * @param itemNames <tt>itemNames</tt> must list, in any order, all the
* item names defined in <tt>compositeType</tt>; * item names defined in <tt>compositeType</tt>; the order in which the
* the order in which the names are listed, is used to * names are listed, is used to match values in <tt>itemValues[]</tt>; must
* match values in <tt>itemValues[]</tt>; * not be null.
* must not be null or empty. *
* <p>
* @param itemValues the values of the items, listed in the same order as * @param itemValues the values of the items, listed in the same order as
* their respective names in <tt>itemNames</tt>; * their respective names in <tt>itemNames</tt>; each item value can be
* each item value can be null, but if it is non-null it must be * null, but if it is non-null it must be a valid value for the open type
* a valid value for the open type defined in <tt>compositeType</tt> for the corresponding item; * defined in <tt>compositeType</tt> for the corresponding item; must be of
* must be of the same size as <tt>itemNames</tt>; must not be null or empty. * the same size as <tt>itemNames</tt>; must not be null.
* <p> *
* @throws IllegalArgumentException <tt>compositeType</tt> is null, or <tt>itemNames[]</tt> or <tt>itemValues[]</tt> is null or empty, * @throws IllegalArgumentException <tt>compositeType</tt> is null, or
* or one of the elements in <tt>itemNames[]</tt> is a null or empty string, * <tt>itemNames[]</tt> or <tt>itemValues[]</tt> is null or empty, or one
* or <tt>itemNames[]</tt> and <tt>itemValues[]</tt> are not of the same size. * of the elements in <tt>itemNames[]</tt> is a null or empty string, or
* <p> * <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>, * @throws OpenDataException <tt>itemNames[]</tt> or
* or one of the elements in <tt>itemNames[]</tt> does not exist as an item name defined in <tt>compositeType</tt>, * <tt>itemValues[]</tt>'s size differs from the number of items defined in
* or one of the elements in <tt>itemValues[]</tt> is not a valid value for the corresponding item * <tt>compositeType</tt>, or one of the elements in <tt>itemNames[]</tt>
* as defined in <tt>compositeType</tt>. * does not exist as an item name defined in <tt>compositeType</tt>, or one
* <p> * 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) public CompositeDataSupport(
CompositeType compositeType, String[] itemNames, Object[] itemValues)
throws OpenDataException { throws OpenDataException {
this(makeMap(itemNames, itemValues), compositeType);
// Check compositeType is not null
//
if (compositeType == null) {
throw new IllegalArgumentException("Argument compositeType cannot be null.");
} }
// item names defined in compositeType: private static SortedMap<String, Object> makeMap(
Set<String> namesSet = compositeType.keySet(); String[] itemNames, Object[] itemValues)
throws OpenDataException {
// 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");
// 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 == null || itemValues == null)
// throw new IllegalArgumentException("Null itemNames or itemValues");
if (itemNames.length != itemValues.length) { if (itemNames.length != itemValues.length) {
throw new IllegalArgumentException("Array arguments itemNames[] and itemValues[] "+ throw new IllegalArgumentException(
"should be of same length (got "+ itemNames.length + "Different lengths: itemNames[" + itemNames.length +
" and "+ itemValues.length +")."); "], itemValues[" + itemValues.length + "]");
}
// 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 SortedMap<String, Object> map = new TreeMap<String, Object>();
// for (int i = 0; i < itemNames.length; i++) {
OpenType<?> itemType; String name = itemNames[i];
for (int i=0; i<itemValues.length; i++) { if (name == null || name.equals(""))
itemType = compositeType.getType(itemNames[i]); throw new IllegalArgumentException("Null or empty item name");
if ( (itemValues[i] != null) && (! itemType.isValue(itemValues[i])) ) { if (map.containsKey(name))
throw new OpenDataException("Argument's element itemValues["+ i +"]=\""+ itemValues[i] +"\" is not a valid value for"+ throw new OpenDataException("Duplicate item name " + name);
" this item (itemName="+ itemNames[i] +",itemType="+ itemType +")."); map.put(itemNames[i], itemValues[i]);
}
} }
// Initialize internal fields: compositeType and contents return map;
//
this.compositeType = compositeType;
for (int i=0; i<itemNames.length; i++) {
this.contents.put(itemNames[i], itemValues[i]);
}
} }
/** /**
...@@ -184,66 +149,101 @@ public class CompositeDataSupport ...@@ -184,66 +149,101 @@ public class CompositeDataSupport
* are given by the mappings in the map <tt>items</tt>. * 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 * 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>. * <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; * @param compositeType the <i>composite type </i> of this <i>composite data</i> instance;
* must not be null. * must not be null.
* <p>
* @param items the mappings of all the item names to their values; * @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>; * <tt>items</tt> must contain all the item names defined in <tt>compositeType</tt>;
* must not be null or empty. * must not be null.
* <p> *
* @throws IllegalArgumentException <tt>compositeType</tt> is null, or <tt>items</tt> is null or empty, * @throws IllegalArgumentException <tt>compositeType</tt> is null, or
* or one of the keys in <tt>items</tt> is a null or empty string, * <tt>items</tt> is null, or one of the keys in <tt>items</tt> is a null
* or one of the values in <tt>items</tt> is null. * or empty string.
* <p> * @throws OpenDataException <tt>items</tt>' size differs from the
* @throws OpenDataException <tt>items</tt>' size differs from the number of items defined in <tt>compositeType</tt>, * number of items defined in <tt>compositeType</tt>, or one of the
* or one of the keys in <tt>items</tt> does not exist as an item name defined in <tt>compositeType</tt>, * keys in <tt>items</tt> does not exist as an item name defined in
* or one of the values in <tt>items</tt> is not a valid value for the corresponding item * <tt>compositeType</tt>, or one of the values in <tt>items</tt>
* as defined in <tt>compositeType</tt>. * is not a valid value for the corresponding item as defined in
* <p> * <tt>compositeType</tt>.
* @throws ArrayStoreException one or more keys in <tt>items</tt> is not of the class <tt>java.lang.String</tt>. * @throws ArrayStoreException one or more keys in <tt>items</tt> is not of
* <p> * the class <tt>java.lang.String</tt>.
*
* @see #toMap
*/ */
public CompositeDataSupport(CompositeType compositeType, public CompositeDataSupport(CompositeType compositeType,
Map<String,?> items) Map<String,?> items)
throws OpenDataException { 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");
// Let the other constructor do the job, as the call to another constructor must be the first call SortedMap<String, Object> map = new TreeMap<String, Object>();
// for (Object key : items.keySet()) {
this( compositeType, if (!(key instanceof String)) {
(items==null ? null : items.keySet().toArray(new String[items.size()])), // may raise an ArrayStoreException throw new ArrayStoreException("Item name is not string: " + key);
(items==null ? null : items.values().toArray()) ); // 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));
/**
*
*/
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++) { return map;
if (arg[i] == null) {
throw new IllegalArgumentException(
"Argument's element "+ argName +"["+ i +"] cannot be null.");
} }
private CompositeDataSupport(
SortedMap<String, Object> items, CompositeType compositeType)
throws OpenDataException {
// Check compositeType is not null
//
if (compositeType == null) {
throw new IllegalArgumentException("Argument compositeType cannot be null.");
}
// item names defined in compositeType:
Set<String> namesFromType = compositeType.keySet();
Set<String> namesFromItems = items.keySet();
// 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 each value, if not null, is of the open type defined for the
* // corresponding item
*/ for (String name : namesFromType) {
private static void checkForEmptyString(String[] arg, String argName) { Object value = items.get(name);
for (int i=0; i<arg.length; i++) { if (value != null) {
if (arg[i].trim().equals("")) { OpenType<?> itemType = compositeType.getType(name);
throw new IllegalArgumentException( if (!itemType.isValue(value)) {
"Argument's element "+ argName +"["+ i +"] cannot be an empty string."); throw new OpenDataException(
"Argument value of wrong type for item " + name +
": value " + value + ", type " + itemType);
} }
} }
} }
// Initialize internal fields: compositeType and contents
//
this.compositeType = compositeType;
this.contents = items;
}
/** /**
* Returns the <i>composite type </i> of this <i>composite data</i> instance. * Returns the <i>composite type </i> of this <i>composite data</i> instance.
*/ */
...@@ -328,6 +328,54 @@ public class CompositeDataSupport ...@@ -328,6 +328,54 @@ public class CompositeDataSupport
return Collections.unmodifiableCollection(contents.values()); return Collections.unmodifiableCollection(contents.values());
} }
/**
* <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 * Compares the specified <var>obj</var> parameter with this
* <code>CompositeDataSupport</code> instance for equality. * <code>CompositeDataSupport</code> instance for equality.
......
<html> <html>
<head> <head>
<title>javax.management package</title> <title>javax.management package</title>
<!-- <!--
Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
...@@ -24,9 +24,9 @@ Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ...@@ -24,9 +24,9 @@ Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
CA 95054 USA or visit www.sun.com if you need additional information or CA 95054 USA or visit www.sun.com if you need additional information or
have any questions. have any questions.
--> -->
</head> </head>
<body bgcolor="white"> <body bgcolor="white">
<p>Provides the core classes for the Java Management Extensions.</p> <p>Provides the core classes for the Java Management Extensions.</p>
<p>The Java Management Extensions <p>The Java Management Extensions
...@@ -407,6 +407,92 @@ have any questions. ...@@ -407,6 +407,92 @@ have any questions.
So for example an SNMP GET operation might result in a So for example an SNMP GET operation might result in a
<code>getAttribute</code> on the MBean Server.</p> <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"> <p id="spec">
@see <a href="{@docRoot}/../technotes/guides/jmx/index.html"> @see <a href="{@docRoot}/../technotes/guides/jmx/index.html">
Java SE 6 Platform documentation on JMX technology</a> Java SE 6 Platform documentation on JMX technology</a>
......
...@@ -322,10 +322,12 @@ public class JMXConnectorFactory { ...@@ -322,10 +322,12 @@ public class JMXConnectorFactory {
JMXConnectorProvider.class; JMXConnectorProvider.class;
final String protocol = serviceURL.getProtocol(); final String protocol = serviceURL.getProtocol();
final String providerClassName = "ClientProvider"; final String providerClassName = "ClientProvider";
final JMXServiceURL providerURL = serviceURL;
JMXConnectorProvider provider = JMXConnectorProvider provider = getProvider(providerURL, envcopy,
getProvider(serviceURL, envcopy, providerClassName, providerClassName,
targetInterface, loader); targetInterface,
loader);
IOException exception = null; IOException exception = null;
if (provider == null) { if (provider == null) {
...@@ -336,7 +338,7 @@ public class JMXConnectorFactory { ...@@ -336,7 +338,7 @@ public class JMXConnectorFactory {
if (loader != null) { if (loader != null) {
try { try {
JMXConnector connection = JMXConnector connection =
getConnectorAsService(loader, serviceURL, envcopy); getConnectorAsService(loader, providerURL, envcopy);
if (connection != null) if (connection != null)
return connection; return connection;
} catch (JMXProviderException e) { } catch (JMXProviderException e) {
...@@ -345,8 +347,7 @@ public class JMXConnectorFactory { ...@@ -345,8 +347,7 @@ public class JMXConnectorFactory {
exception = e; exception = e;
} }
} }
provider = provider = getProvider(protocol, PROTOCOL_PROVIDER_DEFAULT_PACKAGE,
getProvider(protocol, PROTOCOL_PROVIDER_DEFAULT_PACKAGE,
JMXConnectorFactory.class.getClassLoader(), JMXConnectorFactory.class.getClassLoader(),
providerClassName, targetInterface); providerClassName, targetInterface);
} }
...@@ -448,9 +449,10 @@ public class JMXConnectorFactory { ...@@ -448,9 +449,10 @@ public class JMXConnectorFactory {
getProviderIterator(JMXConnectorProvider.class, loader); getProviderIterator(JMXConnectorProvider.class, loader);
JMXConnector connection; JMXConnector connection;
IOException exception = null; IOException exception = null;
while(providers.hasNext()) { while (providers.hasNext()) {
JMXConnectorProvider provider = providers.next();
try { try {
connection = providers.next().newJMXConnector(url, map); connection = provider.newJMXConnector(url, map);
return connection; return connection;
} catch (JMXProviderException e) { } catch (JMXProviderException e) {
throw e; throw e;
...@@ -553,4 +555,5 @@ public class JMXConnectorFactory { ...@@ -553,4 +555,5 @@ public class JMXConnectorFactory {
private static String protocol2package(String protocol) { private static String protocol2package(String protocol) {
return protocol.replace('+', '.').replace('-', '_'); return protocol.replace('+', '.').replace('-', '_');
} }
} }
...@@ -132,7 +132,7 @@ public interface JMXConnectorServerMBean { ...@@ -132,7 +132,7 @@ public interface JMXConnectorServerMBean {
* *
* <p>A connector server may support two chains of forwarders, * <p>A connector server may support two chains of forwarders,
* a system chain and a user chain. See {@link * a system chain and a user chain. See {@link
* JMXConnectorServer#setSystemMBeanServerForwarder} for details.</p> * JMXConnectorServer#getSystemMBeanServerForwarder} for details.</p>
* *
* @param mbsf the new <code>MBeanServerForwarder</code>. * @param mbsf the new <code>MBeanServerForwarder</code>.
* *
...@@ -141,7 +141,7 @@ public interface JMXConnectorServerMBean { ...@@ -141,7 +141,7 @@ public interface JMXConnectorServerMBean {
* with <code>IllegalArgumentException</code>. This includes the * with <code>IllegalArgumentException</code>. This includes the
* case where <code>mbsf</code> is null. * case where <code>mbsf</code> is null.
* *
* @see JMXConnectorServer#setSystemMBeanServerForwarder * @see JMXConnectorServer#getSystemMBeanServerForwarder
*/ */
public void setMBeanServerForwarder(MBeanServerForwarder mbsf); public void setMBeanServerForwarder(MBeanServerForwarder mbsf);
......
...@@ -405,7 +405,7 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable ...@@ -405,7 +405,7 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
rmbsc, EventClientDelegateMBean.OBJECT_NAME, rmbsc, EventClientDelegateMBean.OBJECT_NAME,
EventClientDelegateMBean.class); EventClientDelegateMBean.class);
EventClient ec = new EventClient(ecd, null, defaultExecutor(), null, EventClient ec = new EventClient(ecd, null, defaultExecutor(), null,
EventClient.DEFAULT_LEASE_TIMEOUT); EventClient.DEFAULT_REQUESTED_LEASE_TIME);
rmbsc = EventConnection.Factory.make(rmbsc, ec); rmbsc = EventConnection.Factory.make(rmbsc, ec);
ec.addEventClientListener( ec.addEventClientListener(
......
...@@ -383,7 +383,7 @@ public class RMIConnectorServer extends JMXConnectorServer { ...@@ -383,7 +383,7 @@ public class RMIConnectorServer extends JMXConnectorServer {
try { try {
if (tracing) logger.trace("start", "setting default class loader"); if (tracing) logger.trace("start", "setting default class loader");
defaultClassLoader = EnvHelp.resolveServerClassLoader( defaultClassLoader = EnvHelp.resolveServerClassLoader(
attributes, getSystemMBeanServer()); attributes, getSystemMBeanServerForwarder());
} catch (InstanceNotFoundException infc) { } catch (InstanceNotFoundException infc) {
IllegalArgumentException x = new IllegalArgumentException x = new
IllegalArgumentException("ClassLoader not found: "+infc); IllegalArgumentException("ClassLoader not found: "+infc);
...@@ -398,7 +398,7 @@ public class RMIConnectorServer extends JMXConnectorServer { ...@@ -398,7 +398,7 @@ public class RMIConnectorServer extends JMXConnectorServer {
else else
rmiServer = newServer(); rmiServer = newServer();
rmiServer.setMBeanServer(getSystemMBeanServer()); rmiServer.setMBeanServer(getSystemMBeanServerForwarder());
rmiServer.setDefaultClassLoader(defaultClassLoader); rmiServer.setDefaultClassLoader(defaultClassLoader);
rmiServer.setRMIConnectorServer(this); rmiServer.setRMIConnectorServer(this);
rmiServer.export(); rmiServer.export();
...@@ -592,31 +592,6 @@ public class RMIConnectorServer extends JMXConnectorServer { ...@@ -592,31 +592,6 @@ public class RMIConnectorServer extends JMXConnectorServer {
return Collections.unmodifiableMap(map); return Collections.unmodifiableMap(map);
} }
@Override
public synchronized void setMBeanServerForwarder(MBeanServerForwarder mbsf) {
MBeanServer oldSMBS = getSystemMBeanServer();
super.setMBeanServerForwarder(mbsf);
if (oldSMBS != getSystemMBeanServer())
updateMBeanServer();
// If the system chain of MBeanServerForwarders is not empty, then
// there is no need to call rmiServerImpl.setMBeanServer, because
// it is pointing to the head of the system chain and that has not
// changed. (The *end* of the system chain will have been changed
// to point to mbsf.)
}
private void updateMBeanServer() {
if (rmiServerImpl != null)
rmiServerImpl.setMBeanServer(getSystemMBeanServer());
}
@Override
public synchronized void setSystemMBeanServerForwarder(
MBeanServerForwarder mbsf) {
super.setSystemMBeanServerForwarder(mbsf);
updateMBeanServer();
}
/** /**
* {@inheritDoc} * {@inheritDoc}
* @return true, since this connector server does support a system chain * @return true, since this connector server does support a system chain
...@@ -631,16 +606,19 @@ public class RMIConnectorServer extends JMXConnectorServer { ...@@ -631,16 +606,19 @@ public class RMIConnectorServer extends JMXConnectorServer {
here so that they are accessible to other classes in this package here so that they are accessible to other classes in this package
even though they have protected access. */ even though they have protected access. */
@Override
protected void connectionOpened(String connectionId, String message, protected void connectionOpened(String connectionId, String message,
Object userData) { Object userData) {
super.connectionOpened(connectionId, message, userData); super.connectionOpened(connectionId, message, userData);
} }
@Override
protected void connectionClosed(String connectionId, String message, protected void connectionClosed(String connectionId, String message,
Object userData) { Object userData) {
super.connectionClosed(connectionId, message, userData); super.connectionClosed(connectionId, message, userData);
} }
@Override
protected void connectionFailed(String connectionId, String message, protected void connectionFailed(String connectionId, String message,
Object userData) { Object userData) {
super.connectionFailed(connectionId, message, userData); super.connectionFailed(connectionId, message, userData);
......
...@@ -435,8 +435,14 @@ public class HttpURLConnection extends java.net.HttpURLConnection { ...@@ -435,8 +435,14 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
if (streaming()) { if (streaming()) {
if (chunkLength != -1) { if (chunkLength != -1) {
requests.set ("Transfer-Encoding", "chunked"); requests.set ("Transfer-Encoding", "chunked");
} else { } else { /* fixed content length */
requests.set ("Content-Length", String.valueOf(fixedContentLength)); 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) { } else if (poster != null) {
/* add Content-Length & POST/PUT data */ /* add Content-Length & POST/PUT data */
...@@ -871,11 +877,17 @@ public class HttpURLConnection extends java.net.HttpURLConnection { ...@@ -871,11 +877,17 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
ps = (PrintStream)http.getOutputStream(); ps = (PrintStream)http.getOutputStream();
if (streaming()) { if (streaming()) {
if (strOutputStream == null) { if (strOutputStream == null) {
if (fixedContentLength != -1) { if (chunkLength != -1) { /* chunked */
strOutputStream = new StreamingOutputStream (ps, fixedContentLength); strOutputStream = new StreamingOutputStream(
} else if (chunkLength != -1) { new ChunkedOutputStream(ps, chunkLength), -1L);
strOutputStream = } else { /* must be fixed content length */
new StreamingOutputStream (new ChunkedOutputStream (ps, chunkLength), -1); long length = 0L;
if (fixedContentLengthLong != -1) {
length = fixedContentLengthLong;
} else if (fixedContentLength != -1) {
length = fixedContentLength;
}
strOutputStream = new StreamingOutputStream(ps, length);
} }
} }
return strOutputStream; return strOutputStream;
...@@ -895,7 +907,8 @@ public class HttpURLConnection extends java.net.HttpURLConnection { ...@@ -895,7 +907,8 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
} }
private boolean streaming () { private boolean streaming () {
return (fixedContentLength != -1) || (chunkLength != -1); return (fixedContentLength != -1) || (fixedContentLengthLong != -1) ||
(chunkLength != -1);
} }
/* /*
...@@ -2619,8 +2632,8 @@ public class HttpURLConnection extends java.net.HttpURLConnection { ...@@ -2619,8 +2632,8 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
class StreamingOutputStream extends FilterOutputStream { class StreamingOutputStream extends FilterOutputStream {
int expected; long expected;
int written; long written;
boolean closed; boolean closed;
boolean error; boolean error;
IOException errorExcp; IOException errorExcp;
...@@ -2631,10 +2644,10 @@ public class HttpURLConnection extends java.net.HttpURLConnection { ...@@ -2631,10 +2644,10 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
* In the 2nd case, we make sure the expected number of * In the 2nd case, we make sure the expected number of
* of bytes are actually written * of bytes are actually written
*/ */
StreamingOutputStream (OutputStream os, int expectedLength) { StreamingOutputStream (OutputStream os, long expectedLength) {
super (os); super (os);
expected = expectedLength; expected = expectedLength;
written = 0; written = 0L;
closed = false; closed = false;
error = false; error = false;
} }
...@@ -2643,7 +2656,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection { ...@@ -2643,7 +2656,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
public void write (int b) throws IOException { public void write (int b) throws IOException {
checkError(); checkError();
written ++; written ++;
if (expected != -1 && written > expected) { if (expected != -1L && written > expected) {
throw new IOException ("too many bytes written"); throw new IOException ("too many bytes written");
} }
out.write (b); out.write (b);
...@@ -2658,7 +2671,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection { ...@@ -2658,7 +2671,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
public void write (byte[] b, int off, int len) throws IOException { public void write (byte[] b, int off, int len) throws IOException {
checkError(); checkError();
written += len; written += len;
if (expected != -1 && written > expected) { if (expected != -1L && written > expected) {
out.close (); out.close ();
throw new IOException ("too many bytes written"); throw new IOException ("too many bytes written");
} }
...@@ -2691,7 +2704,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection { ...@@ -2691,7 +2704,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
return; return;
} }
closed = true; closed = true;
if (expected != -1) { if (expected != -1L) {
/* not chunked */ /* not chunked */
if (written != expected) { if (written != expected) {
error = true; error = true;
......
...@@ -527,6 +527,10 @@ public class HttpsURLConnectionImpl ...@@ -527,6 +527,10 @@ public class HttpsURLConnectionImpl
delegate.setFixedLengthStreamingMode(contentLength); delegate.setFixedLengthStreamingMode(contentLength);
} }
public void setFixedLengthStreamingMode(long contentLength) {
delegate.setFixedLengthStreamingMode(contentLength);
}
public void setChunkedStreamingMode (int chunklen) { public void setChunkedStreamingMode (int chunklen) {
delegate.setChunkedStreamingMode(chunklen); delegate.setChunkedStreamingMode(chunklen);
} }
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -284,7 +284,8 @@ class GSSContextImpl implements GSSContext { ...@@ -284,7 +284,8 @@ class GSSContextImpl implements GSSContext {
ByteArrayOutputStream bos = new ByteArrayOutputStream(100); ByteArrayOutputStream bos = new ByteArrayOutputStream(100);
acceptSecContext(new ByteArrayInputStream(inTok, offset, len), acceptSecContext(new ByteArrayInputStream(inTok, offset, len),
bos); bos);
return bos.toByteArray(); byte[] out = bos.toByteArray();
return (out.length == 0) ? null : out;
} }
public void acceptSecContext(InputStream inStream, public void acceptSecContext(InputStream inStream,
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -413,13 +413,14 @@ public class SpNegoContext implements GSSContextSpi { ...@@ -413,13 +413,14 @@ public class SpNegoContext implements GSSContextSpi {
// pull out the mechanism token // pull out the mechanism token
byte[] accept_token = targToken.getResponseToken(); byte[] accept_token = targToken.getResponseToken();
if (accept_token == null) { if (accept_token == null) {
// return wth failure if (!isMechContextEstablished()) {
// return with failure
throw new GSSException(errorCode, -1, throw new GSSException(errorCode, -1,
"mechansim token from server is null"); "mechanism token from server is null");
} }
} else {
mechToken = GSS_initSecContext(accept_token); mechToken = GSS_initSecContext(accept_token);
}
// verify MIC // verify MIC
if (!GSSUtil.useMSInterop()) { if (!GSSUtil.useMSInterop()) {
byte[] micToken = targToken.getMechListMIC(); byte[] micToken = targToken.getMechListMIC();
...@@ -428,7 +429,6 @@ public class SpNegoContext implements GSSContextSpi { ...@@ -428,7 +429,6 @@ public class SpNegoContext implements GSSContextSpi {
"verification of MIC on MechList Failed!"); "verification of MIC on MechList Failed!");
} }
} }
if (isMechContextEstablished()) { if (isMechContextEstablished()) {
state = STATE_DONE; state = STATE_DONE;
retVal = mechToken; retVal = mechToken;
...@@ -556,9 +556,6 @@ public class SpNegoContext implements GSSContextSpi { ...@@ -556,9 +556,6 @@ public class SpNegoContext implements GSSContextSpi {
// get the token for mechanism // get the token for mechanism
byte[] accept_token = GSS_acceptSecContext(mechToken); byte[] accept_token = GSS_acceptSecContext(mechToken);
if (accept_token == null) {
valid = false;
}
// verify MIC // verify MIC
if (!GSSUtil.useMSInterop() && valid) { if (!GSSUtil.useMSInterop() && valid) {
......
...@@ -151,6 +151,10 @@ class OCSPResponse { ...@@ -151,6 +151,10 @@ class OCSPResponse {
private SingleResponse singleResponse; 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) // an array of all of the CRLReasons (used in SingleResponse)
private static CRLReason[] values = CRLReason.values(); private static CRLReason[] values = CRLReason.values();
...@@ -583,7 +587,9 @@ class OCSPResponse { ...@@ -583,7 +587,9 @@ class OCSPResponse {
} }
} }
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) { if (DEBUG != null) {
String until = ""; String until = "";
if (nextUpdate != null) { if (nextUpdate != null) {
...@@ -593,8 +599,8 @@ class OCSPResponse { ...@@ -593,8 +599,8 @@ class OCSPResponse {
thisUpdate + until); thisUpdate + until);
} }
// Check that the test date is within the validity interval // Check that the test date is within the validity interval
if ((thisUpdate != null && now.before(thisUpdate)) || if ((thisUpdate != null && nowPlusSkew.before(thisUpdate)) ||
(nextUpdate != null && now.after(nextUpdate))) { (nextUpdate != null && nowMinusSkew.after(nextUpdate))) {
if (DEBUG != null) { if (DEBUG != null) {
DEBUG.println("Response is unreliable: its validity " + DEBUG.println("Response is unreliable: its validity " +
......
...@@ -273,8 +273,8 @@ static const jlong END_MAXLEN = 0xFFFF + ENDHDR; ...@@ -273,8 +273,8 @@ static const jlong END_MAXLEN = 0xFFFF + ENDHDR;
/* /*
* Searches for end of central directory (END) header. The contents of * Searches for end of central directory (END) header. The contents of
* the END header will be read and placed in endbuf. Returns the file * 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 * position of the END header, otherwise returns -1 if the END header
* was not found or -1 if an error occurred. * was not found or an error occurred.
*/ */
static jlong static jlong
findEND(jzfile *zip, void *endbuf) findEND(jzfile *zip, void *endbuf)
...@@ -314,7 +314,7 @@ findEND(jzfile *zip, void *endbuf) ...@@ -314,7 +314,7 @@ findEND(jzfile *zip, void *endbuf)
} }
} }
} }
return 0; /* END header not found */ return -1; /* END header not found */
} }
/* /*
...@@ -460,9 +460,8 @@ if (1) { zip->msg = message; goto Catch; } else ((void)0) ...@@ -460,9 +460,8 @@ if (1) { zip->msg = message; goto Catch; } else ((void)0)
/* /*
* Reads zip file central directory. Returns the file position of first * Reads zip file central directory. Returns the file position of first
* CEN header, otherwise returns 0 if central directory not found or -1 * CEN header, otherwise returns -1 if an error occured. If zip->msg != NULL
* if an error occurred. If zip->msg != NULL then the error was a zip * then the error was a zip format error and zip->msg has the error text.
* format error and zip->msg has the error text.
* Always pass in -1 for knownTotal; it's used for a recursive call. * Always pass in -1 for knownTotal; it's used for a recursive call.
*/ */
static jlong static jlong
...@@ -488,9 +487,9 @@ readCEN(jzfile *zip, jint knownTotal) ...@@ -488,9 +487,9 @@ readCEN(jzfile *zip, jint knownTotal)
/* Get position of END header */ /* Get position of END header */
if ((endpos = findEND(zip, endbuf)) == -1) 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); freeCEN(zip);
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
# 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.
此差异已折叠。
...@@ -200,8 +200,7 @@ public class CustomForwarderTest { ...@@ -200,8 +200,7 @@ public class CustomForwarderTest {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
MBeanServerForwarder mbsf = EventClientDelegate.newForwarder(); MBeanServerForwarder mbsf = EventClientDelegate.newForwarder(mbs, null);
mbsf.setMBeanServer(mbs);
mbs = mbsf; mbs = mbsf;
// for 1.5 // for 1.5
......
...@@ -65,8 +65,7 @@ public class EventClientExecutorTest { ...@@ -65,8 +65,7 @@ public class EventClientExecutorTest {
new NamedThreadFactory("LEASE")); new NamedThreadFactory("LEASE"));
MBeanServer mbs = MBeanServerFactory.newMBeanServer(); MBeanServer mbs = MBeanServerFactory.newMBeanServer();
MBeanServerForwarder mbsf = EventClientDelegate.newForwarder(); MBeanServerForwarder mbsf = EventClientDelegate.newForwarder(mbs, null);
mbsf.setMBeanServer(mbs);
mbs = mbsf; mbs = mbsf;
EventClientDelegateMBean ecd = EventClientDelegate.getProxy(mbs); EventClientDelegateMBean ecd = EventClientDelegate.getProxy(mbs);
......
...@@ -98,7 +98,7 @@ public class EventManagerTest { ...@@ -98,7 +98,7 @@ public class EventManagerTest {
succeed &= test(new EventClient(ecd, succeed &= test(new EventClient(ecd,
new RMIPushEventRelay(ecd), new RMIPushEventRelay(ecd),
null, null, null, null,
EventClient.DEFAULT_LEASE_TIMEOUT)); EventClient.DEFAULT_REQUESTED_LEASE_TIME));
conn.close(); conn.close();
server.stop(); server.stop();
......
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册