提交 ba8c47f6 编写于 作者: D dfuchs

5072476: RFE: support cascaded (federated) MBean Servers

6299231: Add support for named MBean Servers
Summary: New javax.management.namespace package.
Reviewed-by: emcmanus
上级 e1a8df2f
......@@ -158,6 +158,7 @@ CORE_PKGS = \
javax.management.event \
javax.management.loading \
javax.management.monitor \
javax.management.namespace \
javax.management.relation \
javax.management.openmbean \
javax.management.timer \
......
......@@ -176,6 +176,18 @@ public class JmxProperties {
public static final String RELATION_LOGGER_NAME =
"javax.management.relation";
/**
* Logger name for Namespaces.
*/
public static final String NAMESPACE_LOGGER_NAME =
"javax.management.namespace";
/**
* Logger name for Namespaces.
*/
public static final Logger NAMESPACE_LOGGER =
Logger.getLogger(NAMESPACE_LOGGER_NAME);
/**
* Logger for Relation Service.
*/
......
/*
* 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.interceptor;
import com.sun.jmx.defaults.JmxProperties;
import com.sun.jmx.mbeanserver.MBeanInstantiator;
import com.sun.jmx.mbeanserver.Repository;
import com.sun.jmx.mbeanserver.Util;
import com.sun.jmx.namespace.DomainInterceptor;
import java.util.Queue;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.MBeanServer;
import javax.management.MBeanServerDelegate;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.QueryExp;
import javax.management.namespace.JMXDomain;
import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR;
/**
* A dispatcher that dispatch incoming MBeanServer requests to
* DomainInterceptors.
* <p><b>
* This API is a Sun internal API and is subject to changes without notice.
* </b></p>
* @since 1.7
*/
//
// See comments in DispatchInterceptor.
//
class DomainDispatchInterceptor
extends DispatchInterceptor<DomainInterceptor, JMXDomain> {
/**
* A logger for this class.
**/
private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
private static final ObjectName ALL_DOMAINS =
JMXDomain.getDomainObjectName("*");
/**
* A QueryInterceptor that perform & aggregates queries spanning several
* domains.
*/
final static class AggregatingQueryInterceptor extends QueryInterceptor {
private final DomainDispatchInterceptor parent;
AggregatingQueryInterceptor(DomainDispatchInterceptor dispatcher) {
super(dispatcher.localNamespace);
parent = dispatcher;
}
/**
* Perform queryNames or queryMBeans, depending on which QueryInvoker
* is passed as argument. This is closures without closures.
**/
@Override
<T> Set<T> query(ObjectName pattern, QueryExp query,
QueryInvoker<T> invoker, MBeanServer localNamespace) {
final Set<T> local = invoker.query(localNamespace, pattern, query);
// Add all matching MBeans from local namespace.
final Set<T> res = Util.cloneSet(local);
final boolean all = (pattern == null ||
pattern.getDomain().equals("*"));
if (pattern == null) pattern = ObjectName.WILDCARD;
final String domain = pattern.getDomain();
// If there's no domain pattern, just include the pattern's domain.
// Otherwiae, loop over all virtual domains (parent.getKeys()).
final String[] keys =
(pattern.isDomainPattern() ?
parent.getKeys() : new String[]{domain});
// Add all matching MBeans from each virtual domain
//
for (String key : keys) {
// Only invoke those virtual domain which are selected
// by the domain pattern
//
if (!all && !Util.isDomainSelected(key, domain))
continue;
try {
final MBeanServer mbs = parent.getInterceptor(key);
// mbs can be null if the interceptor was removed
// concurrently...
// See handlerMap and getKeys() in DispatchInterceptor
//
if (mbs == null) continue;
// If the domain is selected, we can replace the pattern
// by the actual domain. This is safer if we want to avoid
// a domain (which could be backed up by an MBeanServer) to
// return names from outside the domain.
// So instead of asking the domain handler for "foo" to
// return all names which match "?o*:type=Bla,*" we're
// going to ask it to return all names which match
// "foo:type=Bla,*"
//
final ObjectName subPattern = pattern.withDomain(key);
res.addAll(invoker.query(mbs, subPattern, query));
} catch (Exception x) {
LOG.finest("Ignoring exception " +
"when attempting to query namespace "+key+": "+x);
continue;
}
}
return res;
}
}
private final DefaultMBeanServerInterceptor localNamespace;
private final String mbeanServerName;
private final MBeanServerDelegate delegate;
/**
* Creates a DomainDispatchInterceptor with the specified
* repository instance.
*
* @param outer A pointer to the MBeanServer object that must be
* passed to the MBeans when invoking their
* {@link javax.management.MBeanRegistration} interface.
* @param delegate A pointer to the MBeanServerDelegate associated
* with the new MBeanServer. The new MBeanServer must register
* this MBean in its MBean repository.
* @param instantiator The MBeanInstantiator that will be used to
* instantiate MBeans and take care of class loading issues.
* @param repository The repository to use for this MBeanServer
*/
public DomainDispatchInterceptor(MBeanServer outer,
MBeanServerDelegate delegate,
MBeanInstantiator instantiator,
Repository repository,
NamespaceDispatchInterceptor namespaces) {
localNamespace = new DefaultMBeanServerInterceptor(outer,
delegate, instantiator,repository,namespaces);
mbeanServerName = Util.getMBeanServerSecurityName(delegate);
this.delegate = delegate;
}
final boolean isLocalHandlerNameFor(String domain,
ObjectName handlerName) {
if (domain == null) return true;
return handlerName.getDomain().equals(domain) &&
JMXDomain.TYPE_ASSIGNMENT.equals(
handlerName.getKeyPropertyListString());
}
@Override
void validateHandlerNameFor(String key, ObjectName name) {
super.validateHandlerNameFor(key,name);
final String[] domains = localNamespace.getDomains();
for (int i=0;i<domains.length;i++) {
if (domains[i].equals(key))
throw new IllegalArgumentException("domain "+key+
" is not empty");
}
}
@Override
final MBeanServer getInterceptorOrNullFor(ObjectName name) {
if (name == null) return localNamespace;
final String domain = name.getDomain();
if (domain.endsWith(NAMESPACE_SEPARATOR)) return localNamespace;
if (domain.contains(NAMESPACE_SEPARATOR)) return null;
final String localDomain = domain;
if (isLocalHandlerNameFor(localDomain,name)) {
LOG.finer("dispatching to local namespace");
return localNamespace;
}
final DomainInterceptor ns = getInterceptor(localDomain);
if (ns == null) {
if (LOG.isLoggable(Level.FINER)) {
LOG.finer("dispatching to local namespace: " + localDomain);
}
return getNextInterceptor();
}
if (LOG.isLoggable(Level.FINER)) {
LOG.finer("dispatching to domain: " + localDomain);
}
return ns;
}
private boolean multipleQuery(ObjectName pattern) {
if (pattern == null) return true;
if (pattern.isDomainPattern()) return true;
try {
// This is a bit of a hack. If there's any chance that a JMXDomain
// MBean name is selected by the given pattern then we must include
// the local namespace in our search.
// Returning true will have this effect.
if (pattern.apply(ALL_DOMAINS.withDomain(pattern.getDomain())))
return true;
} catch (MalformedObjectNameException x) {
// should not happen
throw new IllegalArgumentException(String.valueOf(pattern), x);
}
return false;
}
@Override
final QueryInterceptor getInterceptorForQuery(ObjectName pattern) {
// Check if we need to aggregate.
if (multipleQuery(pattern))
return new AggregatingQueryInterceptor(this);
// We don't need to aggregate: do the "simple" thing...
final String domain = pattern.getDomain();
// Do we have a virtual domain?
final DomainInterceptor ns = getInterceptor(domain);
if (ns != null) {
if (LOG.isLoggable(Level.FINER))
LOG.finer("dispatching to domain: " + domain);
return new QueryInterceptor(ns);
}
// We don't have a virtual domain. Send to local domains.
if (LOG.isLoggable(Level.FINER))
LOG.finer("dispatching to local namespace: " + domain);
return new QueryInterceptor(localNamespace);
}
@Override
final ObjectName getHandlerNameFor(String key)
throws MalformedObjectNameException {
return JMXDomain.getDomainObjectName(key);
}
@Override
final public String getHandlerKey(ObjectName name) {
return name.getDomain();
}
@Override
final DomainInterceptor createInterceptorFor(String key,
ObjectName name, JMXDomain handler,
Queue<Runnable> postRegisterQueue) {
final DomainInterceptor ns =
new DomainInterceptor(mbeanServerName,handler,key);
ns.addPostRegisterTask(postRegisterQueue, delegate);
if (LOG.isLoggable(Level.FINER)) {
LOG.finer("DomainInterceptor created: "+ns);
}
return ns;
}
@Override
final void interceptorReleased(DomainInterceptor interceptor,
Queue<Runnable> postDeregisterQueue) {
interceptor.addPostDeregisterTask(postDeregisterQueue, delegate);
}
@Override
final DefaultMBeanServerInterceptor getNextInterceptor() {
return localNamespace;
}
/**
* Returns the list of domains in which any MBean is currently
* registered.
*/
@Override
public String[] getDomains() {
// A JMXDomain is registered in its own domain.
// Therefore, localNamespace.getDomains() contains all domains.
// In addition, localNamespace will perform the necessary
// MBeanPermission checks for getDomains().
//
return localNamespace.getDomains();
}
/**
* Returns the number of MBeans registered in the MBean server.
*/
@Override
public Integer getMBeanCount() {
int count = getNextInterceptor().getMBeanCount().intValue();
final String[] keys = getKeys();
for (String key:keys) {
final MBeanServer mbs = getInterceptor(key);
if (mbs == null) continue;
count += mbs.getMBeanCount().intValue();
}
return Integer.valueOf(count);
}
}
/*
* 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.interceptor;
import java.io.ObjectInputStream;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.ObjectName;
import javax.management.OperationsException;
import javax.management.ReflectionException;
import javax.management.loading.ClassLoaderRepository;
/**
* An abstract class for MBeanServerInterceptorSupport.
* Some methods in MBeanServerInterceptor should never be called.
* This base class provides an implementation of these methods that simply
* throw an {@link UnsupportedOperationException}.
* <p><b>
* This API is a Sun internal API and is subject to changes without notice.
* </b></p>
* @since 1.7
*/
public abstract class MBeanServerInterceptorSupport
implements MBeanServerInterceptor {
/**
* This method should never be called.
* Throws UnsupportedOperationException.
*/
public Object instantiate(String className)
throws ReflectionException, MBeanException {
throw new UnsupportedOperationException("Not applicable.");
}
/**
* This method should never be called.
* Throws UnsupportedOperationException.
*/
public Object instantiate(String className, ObjectName loaderName)
throws ReflectionException, MBeanException,
InstanceNotFoundException {
throw new UnsupportedOperationException("Not applicable.");
}
/**
* This method should never be called.
* Throws UnsupportedOperationException.
*/
public Object instantiate(String className, Object[] params,
String[] signature) throws ReflectionException, MBeanException {
throw new UnsupportedOperationException("Not applicable.");
}
/**
* This method should never be called.
* Throws UnsupportedOperationException.
*/
public Object instantiate(String className, ObjectName loaderName,
Object[] params, String[] signature)
throws ReflectionException, MBeanException,
InstanceNotFoundException {
throw new UnsupportedOperationException("Not applicable.");
}
/**
* This method should never be called.
* Throws UnsupportedOperationException.
*/
@Deprecated
public ObjectInputStream deserialize(ObjectName name, byte[] data)
throws InstanceNotFoundException, OperationsException {
throw new UnsupportedOperationException("Not applicable.");
}
/**
* This method should never be called.
* Throws UnsupportedOperationException.
*/
@Deprecated
public ObjectInputStream deserialize(String className, byte[] data)
throws OperationsException, ReflectionException {
throw new UnsupportedOperationException("Not applicable.");
}
/**
* This method should never be called.
* Throws UnsupportedOperationException.
*/
@Deprecated
public ObjectInputStream deserialize(String className,
ObjectName loaderName, byte[] data)
throws InstanceNotFoundException, OperationsException,
ReflectionException {
throw new UnsupportedOperationException("Not applicable.");
}
/**
* This method should never be called.
* Throws UnsupportedOperationException.
*/
public ClassLoaderRepository getClassLoaderRepository() {
throw new UnsupportedOperationException("Not applicable.");
}
}
/*
* 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.interceptor;
import com.sun.jmx.defaults.JmxProperties;
import com.sun.jmx.mbeanserver.MBeanInstantiator;
import com.sun.jmx.mbeanserver.Repository;
import com.sun.jmx.mbeanserver.Util;
import com.sun.jmx.namespace.NamespaceInterceptor;
import java.util.Queue;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.MBeanServer;
import javax.management.MBeanServerDelegate;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.namespace.JMXDomain;
import javax.management.namespace.JMXNamespace;
import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR;
/**
* A dispatcher that dispatches to NamespaceInterceptors.
* <p><b>
* This API is a Sun internal API and is subject to changes without notice.
* </b></p>
* @since 1.7
*/
public class NamespaceDispatchInterceptor
extends DispatchInterceptor<NamespaceInterceptor, JMXNamespace> {
/**
* A logger for this class.
**/
private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
private static final int NAMESPACE_SEPARATOR_LENGTH =
NAMESPACE_SEPARATOR.length();
private final DomainDispatchInterceptor localNamespace;
private final String serverName;
/**
* Creates a NamespaceDispatchInterceptor with the specified
* repository instance.
* <p>Do not forget to call <code>initialize(outer,delegate)</code>
* before using this object.
*
* @param outer A pointer to the MBeanServer object that must be
* passed to the MBeans when invoking their
* {@link javax.management.MBeanRegistration} interface.
* @param delegate A pointer to the MBeanServerDelegate associated
* with the new MBeanServer. The new MBeanServer must register
* this MBean in its MBean repository.
* @param instantiator The MBeanInstantiator that will be used to
* instantiate MBeans and take care of class loading issues.
* @param repository The repository to use for this MBeanServer
*/
public NamespaceDispatchInterceptor(MBeanServer outer,
MBeanServerDelegate delegate,
MBeanInstantiator instantiator,
Repository repository) {
localNamespace = new DomainDispatchInterceptor(outer,delegate,
instantiator,repository,this);
serverName = Util.getMBeanServerSecurityName(delegate);
}
// TODO: Should move that to JMXNamespace? or to ObjectName?
/**
* Get first name space in ObjectName path. Ignore leading namespace
* separators.
**/
public static String getFirstNamespace(ObjectName name) {
if (name == null) return "";
final String domain = name.getDomain();
if (domain.equals("")) return "";
int first = 0;
int end = domain.indexOf(NAMESPACE_SEPARATOR,first);
while (end == first) {
first = end+NAMESPACE_SEPARATOR_LENGTH;
end = domain.indexOf(NAMESPACE_SEPARATOR,first);
if (end == -1) break;
}
if (end == -1) return "";
final String namespace = domain.substring(first,end);
return namespace;
}
/**
* Called by the DefaultMBeanServerInterceptor, just before adding an
* MBean to the repository.
*
* @param resource the MBean to be registered.
* @param logicalName the name of the MBean to be registered.
*/
final void checkLocallyRegistrable(Object resource,
ObjectName logicalName) {
if (!(resource instanceof JMXNamespace) &&
logicalName.getDomain().contains(NAMESPACE_SEPARATOR))
throw new IllegalArgumentException(String.valueOf(logicalName)+
": Invalid ObjectName for an instance of " +
resource.getClass().getName());
}
final boolean isLocalHandlerNameFor(String namespace,
ObjectName handlerName) {
return handlerName.getDomain().equals(namespace+NAMESPACE_SEPARATOR) &&
JMXNamespace.TYPE_ASSIGNMENT.equals(
handlerName.getKeyPropertyListString());
}
@Override
final MBeanServer getInterceptorOrNullFor(ObjectName name) {
final String namespace = getFirstNamespace(name);
if (namespace.equals("") || isLocalHandlerNameFor(namespace,name) ||
name.getDomain().equals(namespace+NAMESPACE_SEPARATOR)) {
LOG.finer("dispatching to local name space");
return localNamespace;
}
final NamespaceInterceptor ns = getInterceptor(namespace);
if (LOG.isLoggable(Level.FINER)) {
if (ns != null) {
LOG.finer("dispatching to name space: " + namespace);
} else {
LOG.finer("no handler for: " + namespace);
}
}
return ns;
}
@Override
final QueryInterceptor getInterceptorForQuery(ObjectName pattern) {
final String namespace = getFirstNamespace(pattern);
if (namespace.equals("") || isLocalHandlerNameFor(namespace,pattern) ||
pattern.getDomain().equals(namespace+NAMESPACE_SEPARATOR)) {
LOG.finer("dispatching to local name space");
return new QueryInterceptor(localNamespace);
}
final NamespaceInterceptor ns = getInterceptor(namespace);
if (LOG.isLoggable(Level.FINER)) {
if (ns != null) {
LOG.finer("dispatching to name space: " + namespace);
} else {
LOG.finer("no handler for: " + namespace);
}
}
if (ns == null) return null;
return new QueryInterceptor(ns);
}
@Override
final ObjectName getHandlerNameFor(String key)
throws MalformedObjectNameException {
return ObjectName.getInstance(key+NAMESPACE_SEPARATOR,
"type", JMXNamespace.TYPE);
}
@Override
final public String getHandlerKey(ObjectName name) {
return getFirstNamespace(name);
}
@Override
final NamespaceInterceptor createInterceptorFor(String key,
ObjectName name, JMXNamespace handler,
Queue<Runnable> postRegisterQueue) {
final NamespaceInterceptor ns =
new NamespaceInterceptor(serverName,handler,key);
if (LOG.isLoggable(Level.FINER)) {
LOG.finer("NamespaceInterceptor created: "+ns);
}
return ns;
}
@Override
final DomainDispatchInterceptor getNextInterceptor() {
return localNamespace;
}
/**
* Returns the list of domains in which any MBean is currently
* registered.
*/
@Override
public String[] getDomains() {
return localNamespace.getDomains();
}
@Override
public void addNamespace(ObjectName name, JMXNamespace handler,
Queue<Runnable> postRegisterQueue) {
if (handler instanceof JMXDomain)
localNamespace.addNamespace(name,
(JMXDomain)handler,postRegisterQueue);
else super.addNamespace(name,handler,postRegisterQueue);
}
@Override
public void removeNamespace(ObjectName name, JMXNamespace handler,
Queue<Runnable> postDeregisterQueue) {
if (handler instanceof JMXDomain)
localNamespace.removeNamespace(name,(JMXDomain)handler,
postDeregisterQueue);
else super.removeNamespace(name,handler,postDeregisterQueue);
}
}
......@@ -51,6 +51,8 @@ import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.QueryExp;
import javax.management.ReflectionException;
import javax.management.namespace.JMXNamespaces;
import javax.management.namespace.MBeanServerSupport;
import javax.management.remote.IdentityMBeanServerForwarder;
public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
......@@ -285,14 +287,14 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
if (!pattern.apply(mbeanName))
return false;
// final String dompat = pattern.getDomain();
// if (!dompat.contains(JMXNamespaces.NAMESPACE_SEPARATOR))
// return true; // We already checked that patterns apply.
//
// if (mbeanName.getDomain().endsWith(JMXNamespaces.NAMESPACE_SEPARATOR)) {
// // only matches if pattern ends with //
// return dompat.endsWith(JMXNamespaces.NAMESPACE_SEPARATOR);
// }
final String dompat = pattern.getDomain();
if (!dompat.contains(JMXNamespaces.NAMESPACE_SEPARATOR))
return true; // We already checked that patterns apply.
if (mbeanName.getDomain().endsWith(JMXNamespaces.NAMESPACE_SEPARATOR)) {
// only matches if pattern ends with //
return dompat.endsWith(JMXNamespaces.NAMESPACE_SEPARATOR);
}
// should not come here, unless mbeanName contains a // in the
// middle of its domain, which would be weird.
......
/*
* Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -25,44 +25,42 @@
package com.sun.jmx.mbeanserver;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.Set;
import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER;
import com.sun.jmx.interceptor.NamespaceDispatchInterceptor;
import java.io.ObjectInputStream;
import java.security.AccessController;
import java.security.Permission;
import java.security.PrivilegedExceptionAction;
import java.util.Iterator;
import java.util.Set;
import java.util.logging.Level;
// RI import
import javax.management.MBeanPermission;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.InvalidAttributeValueException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanException;
import javax.management.ReflectionException;
import javax.management.MBeanInfo;
import javax.management.QueryExp;
import javax.management.NotificationListener;
import javax.management.NotificationFilter;
import javax.management.ListenerNotFoundException;
import javax.management.IntrospectionException;
import javax.management.OperationsException;
import javax.management.InstanceNotFoundException;
import javax.management.NotCompliantMBeanException;
import javax.management.MBeanPermission;
import javax.management.MBeanRegistrationException;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InvalidAttributeValueException;
import javax.management.ObjectName;
import javax.management.ObjectInstance;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.RuntimeOperationsException;
import javax.management.MBeanServer;
import javax.management.MBeanServerDelegate;
import javax.management.NotCompliantMBeanException;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.OperationsException;
import javax.management.QueryExp;
import javax.management.ReflectionException;
import javax.management.RuntimeOperationsException;
import javax.management.loading.ClassLoaderRepository;
import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER;
import com.sun.jmx.interceptor.DefaultMBeanServerInterceptor;
import com.sun.jmx.interceptor.MBeanServerInterceptor;
/**
* This is the base class for MBean manipulation on the agent side. It
* contains the methods necessary for the creation, registration, and
......@@ -102,15 +100,14 @@ public final class JmxMBeanServer
/** true if interceptors are enabled **/
private final boolean interceptorsEnabled;
/** Revisit: transient ??? **/
private final transient MBeanServer outerShell;
private final MBeanServer outerShell;
/** Revisit: transient ??? **/
private transient MBeanServerInterceptor mbsInterceptor = null;
private volatile MBeanServer mbsInterceptor = null;
/** Revisit: transient ??? **/
/** The MBeanServerDelegate object representing the MBean Server */
private final transient MBeanServerDelegate mBeanServerDelegateObject;
private final MBeanServerDelegate mBeanServerDelegateObject;
private final String mbeanServerName;
/**
* <b>Package:</b> Creates an MBeanServer with the
......@@ -243,9 +240,10 @@ public final class JmxMBeanServer
final Repository repository = new Repository(domain,fairLock);
this.mbsInterceptor =
new DefaultMBeanServerInterceptor(outer, delegate, instantiator,
new NamespaceDispatchInterceptor(outer, delegate, instantiator,
repository);
this.interceptorsEnabled = interceptors;
this.mbeanServerName = Util.getMBeanServerSecurityName(delegate);
initialize();
}
......@@ -941,7 +939,8 @@ public final class JmxMBeanServer
throws ReflectionException, MBeanException {
/* Permission check */
checkMBeanPermission(className, null, null, "instantiate");
checkMBeanPermission(mbeanServerName, className, null, null,
"instantiate");
return instantiator.instantiate(className);
}
......@@ -978,7 +977,8 @@ public final class JmxMBeanServer
InstanceNotFoundException {
/* Permission check */
checkMBeanPermission(className, null, null, "instantiate");
checkMBeanPermission(mbeanServerName, className, null,
null, "instantiate");
ClassLoader myLoader = outerShell.getClass().getClassLoader();
return instantiator.instantiate(className, loaderName, myLoader);
......@@ -1016,7 +1016,8 @@ public final class JmxMBeanServer
throws ReflectionException, MBeanException {
/* Permission check */
checkMBeanPermission(className, null, null, "instantiate");
checkMBeanPermission(mbeanServerName, className, null, null,
"instantiate");
ClassLoader myLoader = outerShell.getClass().getClassLoader();
return instantiator.instantiate(className, params, signature,
......@@ -1059,7 +1060,8 @@ public final class JmxMBeanServer
InstanceNotFoundException {
/* Permission check */
checkMBeanPermission(className, null, null, "instantiate");
checkMBeanPermission(mbeanServerName, className, null,
null, "instantiate");
ClassLoader myLoader = outerShell.getClass().getClassLoader();
return instantiator.instantiate(className,loaderName,params,signature,
......@@ -1236,7 +1238,7 @@ public final class JmxMBeanServer
"Unexpected exception occurred", e);
}
throw new
IllegalStateException("Can't register delegate.");
IllegalStateException("Can't register delegate.",e);
}
......@@ -1278,7 +1280,7 @@ public final class JmxMBeanServer
* are not enabled on this object.
* @see #interceptorsEnabled
**/
public synchronized MBeanServerInterceptor getMBeanServerInterceptor() {
public synchronized MBeanServer getMBeanServerInterceptor() {
if (interceptorsEnabled) return mbsInterceptor;
else throw new UnsupportedOperationException(
"MBeanServerInterceptors are disabled.");
......@@ -1292,7 +1294,7 @@ public final class JmxMBeanServer
* @see #interceptorsEnabled
**/
public synchronized void
setMBeanServerInterceptor(MBeanServerInterceptor interceptor) {
setMBeanServerInterceptor(MBeanServer interceptor) {
if (!interceptorsEnabled) throw new UnsupportedOperationException(
"MBeanServerInterceptors are disabled.");
if (interceptor == null) throw new
......@@ -1330,7 +1332,8 @@ public final class JmxMBeanServer
**/
public ClassLoaderRepository getClassLoaderRepository() {
/* Permission check */
checkMBeanPermission(null, null, null, "getClassLoaderRepository");
checkMBeanPermission(mbeanServerName, null, null,
null, "getClassLoaderRepository");
return secureClr;
}
......@@ -1484,14 +1487,16 @@ public final class JmxMBeanServer
// SECURITY CHECKS
//----------------
private static void checkMBeanPermission(String classname,
private static void checkMBeanPermission(String serverName,
String classname,
String member,
ObjectName objectName,
String actions)
throws SecurityException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
Permission perm = new MBeanPermission(classname,
Permission perm = new MBeanPermission(serverName,
classname,
member,
objectName,
actions);
......
......@@ -224,7 +224,7 @@ public abstract class MXBeanLookup {
throws InvalidObjectException {
String domain = prefix + name.getDomain();
try {
name = switchDomain(domain, name);
name = name.withDomain(domain);
} catch (MalformedObjectNameException e) {
throw EnvHelp.initCause(
new InvalidObjectException(e.getMessage()), e);
......@@ -242,7 +242,7 @@ public abstract class MXBeanLookup {
"Proxy's name does not start with " + prefix + ": " + name);
}
try {
name = switchDomain(domain.substring(prefix.length()), name);
name = name.withDomain(domain.substring(prefix.length()));
} catch (MalformedObjectNameException e) {
throw EnvHelp.initCause(new OpenDataException(e.getMessage()), e);
}
......@@ -269,14 +269,6 @@ public abstract class MXBeanLookup {
currentLookup.set(lookup);
}
// Method temporarily added until we have ObjectName.switchDomain in the
// public API. Note that this method DOES NOT PRESERVE the order of
// keys in the ObjectName so it must not be used in the final release.
static ObjectName switchDomain(String domain, ObjectName name)
throws MalformedObjectNameException {
return new ObjectName(domain, name.getKeyPropertyList());
}
private static final ThreadLocal<MXBeanLookup> currentLookup =
new ThreadLocal<MXBeanLookup>();
......
......@@ -45,7 +45,6 @@ import javax.management.QueryExp;
import javax.management.RuntimeOperationsException;
/**
* The RepositorySupport implements the Repository interface.
* This repository does not support persistency.
*
* @since 1.5
......@@ -197,9 +196,9 @@ public class Repository {
if (isPropertyValuePattern &&
pattern.isPropertyValuePattern(keys[i])) {
// wildmatch key property values
final char[] val_pattern = values[i].toCharArray();
final char[] val_string = v.toCharArray();
if (wildmatch(val_string,val_pattern))
// values[i] is the pattern;
// v is the string
if (Util.wildmatch(v,values[i]))
continue;
else
return false;
......@@ -236,86 +235,6 @@ public class Repository {
}
}
/** Match a string against a shell-style pattern. The only pattern
characters recognised are <code>?</code>, standing for any one
character, and <code>*</code>, standing for any string of
characters, including the empty string.
@param str the string to match, as a character array.
@param pat the pattern to match the string against, as a
character array.
@return true if and only if the string matches the pattern.
*/
/* The algorithm is a classical one. We advance pointers in
parallel through str and pat. If we encounter a star in pat,
we remember its position and continue advancing. If at any
stage we get a mismatch between str and pat, we look to see if
there is a remembered star. If not, we fail. If so, we
retreat pat to just past that star and str to the position
after the last one we tried, and we let the match advance
again.
Even though there is only one remembered star position, the
algorithm works when there are several stars in the pattern.
When we encounter the second star, we forget the first one.
This is OK, because if we get to the second star in A*B*C
(where A etc are arbitrary strings), we have already seen AXB.
We're therefore setting up a match of *C against the remainder
of the string, which will match if that remainder looks like
YC, so the whole string looks like AXBYC.
*/
public static boolean wildmatch(char[] str, char[] pat) {
int stri; // index in str
int pati; // index in pat
int starstri; // index for backtrack if "*" attempt fails
int starpati; // index for backtrack if "*" attempt fails, +1
final int strlen = str.length;
final int patlen = pat.length;
stri = pati = 0;
starstri = starpati = -1;
/* On each pass through this loop, we either advance pati,
or we backtrack pati and advance starstri. Since starstri
is only ever assigned from pati, the loop must terminate. */
while (true) {
if (pati < patlen) {
final char patc = pat[pati];
switch (patc) {
case '?':
if (stri == strlen)
break;
stri++;
pati++;
continue;
case '*':
pati++;
starpati = pati;
starstri = stri;
continue;
default:
if (stri < strlen && str[stri] == patc) {
stri++;
pati++;
continue;
}
break;
}
} else if (stri == strlen)
return true;
// Mismatched, can we backtrack to a "*"?
if (starpati < 0 || starstri == strlen)
return false;
// Retry the match one position later in str
pati = starpati;
starstri++;
stri = starstri;
}
}
private void addNewDomMoi(final DynamicMBean object,
final String dom,
final ObjectName name,
......@@ -370,7 +289,7 @@ public class Repository {
if (name.isPattern()) return null;
// Extract the domain name.
String dom= name.getDomain().intern();
String dom = name.getDomain().intern();
// Default domain case
if (dom.length() == 0) {
......@@ -480,7 +399,7 @@ public class Repository {
name = Util.newObjectName(domain + name.toString());
// Do we have default domain ?
if (dom == domain) {
if (dom == domain) { // ES: OK (dom & domain are interned)
to_default_domain = true;
dom = domain;
} else {
......@@ -652,10 +571,9 @@ public class Repository {
}
// Pattern matching in the domain name (*, ?)
char[] dom2Match = name.getDomain().toCharArray();
final String dom2Match = name.getDomain();
for (String dom : domainTb.keySet()) {
char[] theDom = dom.toCharArray();
if (wildmatch(theDom, dom2Match)) {
if (Util.wildpathmatch(dom, dom2Match)) {
final Map<String,NamedObject> moiTb = domainTb.get(dom);
if (allNames)
result.addAll(moiTb.values());
......@@ -726,7 +644,7 @@ public class Repository {
// need to reinstantiate a hashtable because of possible
// big buckets array size inside table, never cleared,
// thus the new !
if (dom == domain)
if (dom == domain) // ES: OK dom and domain are interned.
domainTb.put(domain, new HashMap<String,NamedObject>());
}
......
......@@ -28,17 +28,16 @@ package com.sun.jmx.mbeanserver;
import javax.management.MBeanServer;
import javax.management.MBeanServerDelegate;
import com.sun.jmx.interceptor.MBeanServerInterceptor;
/**
* Extends the MBeanServer and MBeanServerInterceptor interface to
* Extends the MBeanServer interface to
* provide methods for getting the MetaData and MBeanServerInstantiator
* objects associated with an MBeanServer.
*
* @since 1.5
*/
public interface SunJmxMBeanServer
extends MBeanServerInterceptor, MBeanServer {
extends MBeanServer {
/**
* Return the MBeanInstantiator associated to this MBeanServer.
......@@ -68,7 +67,7 @@ public interface SunJmxMBeanServer
* are not enabled on this object.
* @see #interceptorsEnabled
**/
public MBeanServerInterceptor getMBeanServerInterceptor();
public MBeanServer getMBeanServerInterceptor();
/**
* Set the MBeanServerInterceptor.
......@@ -77,7 +76,7 @@ public interface SunJmxMBeanServer
* are not enabled on this object.
* @see #interceptorsEnabled
**/
public void setMBeanServerInterceptor(MBeanServerInterceptor interceptor);
public void setMBeanServerInterceptor(MBeanServer interceptor);
/**
* <p>Return the MBeanServerDelegate representing the MBeanServer.
......
/*
* 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 com.sun.jmx.mbeanserver.Util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.InstanceNotFoundException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanPermission;
import javax.management.MBeanServerDelegate;
import javax.management.MBeanServerNotification;
import javax.management.MalformedObjectNameException;
import javax.management.Notification;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.QueryExp;
import javax.management.namespace.JMXDomain;
/**
* A DomainInterceptor wraps a JMXDomain.
* <p><b>
* This API is a Sun internal API and is subject to changes without notice.
* </b></p>
* @since 1.7
*/
public class DomainInterceptor extends HandlerInterceptor<JMXDomain> {
// TODO: Ideally DomainInterceptor should be replaced by
// something at Repository level.
// The problem there will be that we may need to
// reinstantiate the 'queryPerformedByRepos' boolean
// [or we will need to wrap the repository in
// a 'RepositoryInterceptor'?]
// Also there's no real need for a DomainInterceptor to
// extend RewritingMBeanServerConnection.
/**
* A logger for this class.
**/
private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
private final String domainName;
private volatile ObjectName ALL;
private final String serverName;
private volatile NotificationListener mbsListener;
private static class PatternNotificationFilter
implements NotificationFilter {
final ObjectName pattern;
public PatternNotificationFilter(ObjectName pattern) {
this.pattern = pattern;
}
public boolean isNotificationEnabled(Notification notification) {
if (!(notification instanceof MBeanServerNotification))
return false;
final MBeanServerNotification mbsn =
(MBeanServerNotification) notification;
if (pattern == null || pattern.apply(mbsn.getMBeanName()))
return true;
return false;
}
static final long serialVersionUID = 7409950927025262111L;
}
/**
* Creates a new instance of NamespaceInterceptor
*/
public DomainInterceptor(String serverName,
JMXDomain handler,
String domainName) {
super(handler);
this.domainName = domainName;
this.serverName = serverName;
}
@Override
public String toString() {
return this.getClass().getName()+"(parent="+serverName+
", domain="+this.domainName+")";
}
public void connectDelegate(final MBeanServerDelegate delegate)
throws InstanceNotFoundException {
final NotificationFilter filter =
new PatternNotificationFilter(getPatternFor(null));
synchronized (this) {
if (mbsListener == null)
mbsListener = new NotificationListener() {
public void handleNotification(Notification notification,
Object handback) {
if (filter.isNotificationEnabled(notification))
delegate.sendNotification(notification);
}
};
}
getNamespace().
addMBeanServerNotificationListener(mbsListener, filter);
}
public void disconnectDelegate()
throws InstanceNotFoundException, ListenerNotFoundException {
final NotificationListener l;
synchronized (this) {
l = mbsListener;
if (l == null) return;
mbsListener = null;
}
getNamespace().removeMBeanServerNotificationListener(l);
}
public void addPostRegisterTask(Queue<Runnable> queue,
final MBeanServerDelegate delegate) {
if (queue == null)
throw new IllegalArgumentException("task queue must not be null");
final Runnable task1 = new Runnable() {
public void run() {
try {
connectDelegate(delegate);
} catch (Exception x) {
throw new UnsupportedOperationException("notification forwarding",x);
}
}
};
queue.add(task1);
}
public void addPostDeregisterTask(Queue<Runnable> queue,
final MBeanServerDelegate delegate) {
if (queue == null)
throw new IllegalArgumentException("task queue must not be null");
final Runnable task1 = new Runnable() {
public void run() {
try {
disconnectDelegate();
} catch (Exception x) {
throw new UnsupportedOperationException("notification forwarding",x);
}
}
};
queue.add(task1);
}
/**
* Throws IllegalArgumentException if targetName.getDomain() is not
* in the domain handled.
**/
@Override
protected ObjectName toSource(ObjectName targetName) {
if (targetName == null) return null;
if (targetName.isDomainPattern()) return targetName;
final String targetDomain = targetName.getDomain();
// TODO: revisit this. RuntimeOperationsException may be better?
//
if (!targetDomain.equals(domainName))
throw new IllegalArgumentException(targetName.toString());
return targetName;
}
@Override
protected ObjectName toTarget(ObjectName sourceName) {
return sourceName;
}
/**
* No rewriting: always return sources - stripping instances for which
* the caller doesn't have permissions.
**/
@Override
Set<ObjectInstance> processOutputInstances(Set<ObjectInstance> sources) {
if (sources == null || sources.isEmpty() || !checkOn())
return sources;
final Set<ObjectInstance> res = Util.equivalentEmptySet(sources);
for (ObjectInstance o : sources) {
if (checkQuery(o.getObjectName(), "queryMBeans"))
res.add(o);
}
return res;
}
/**
* No rewriting: always return sourceNames - stripping names for which
* the caller doesn't have permissions.
**/
@Override
Set<ObjectName> processOutputNames(Set<ObjectName> sourceNames) {
if (sourceNames == null || sourceNames.isEmpty() || !checkOn())
return sourceNames;
final Set<ObjectName> res = Util.equivalentEmptySet(sourceNames);
for (ObjectName o : sourceNames) {
if (checkQuery(o, "queryNames"))
res.add(o);
}
return res;
}
/** No rewriting: always return source **/
@Override
ObjectInstance processOutputInstance(ObjectInstance source) {
return source;
}
@Override
public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
try {
// We don't trust the wrapped JMXDomain...
final ObjectName pattern = getPatternFor(name);
final Set<ObjectName> res = super.queryNames(pattern,query);
return Util.filterMatchingNames(pattern,res);
} catch (Exception x) {
if (LOG.isLoggable(Level.FINE))
LOG.fine("Unexpected exception raised in queryNames: "+x);
LOG.log(Level.FINEST,"Unexpected exception raised in queryNames",x);
}
// We reach here only when an exception was raised.
//
final Set<ObjectName> empty = Collections.emptySet();
return empty;
}
private ObjectName getPatternFor(final ObjectName name) {
try {
if (ALL == null) ALL = ObjectName.getInstance(domainName + ":*");
if (name == null) return ALL;
if (name.getDomain().equals(domainName)) return name;
return name.withDomain(domainName);
} catch (MalformedObjectNameException x) {
throw new IllegalArgumentException(String.valueOf(name),x);
}
}
@Override
public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
try {
// We don't trust the wrapped JMXDomain...
final ObjectName pattern = getPatternFor(name);
final Set<ObjectInstance> res = super.queryMBeans(pattern,query);
return Util.filterMatchingInstances(pattern,res);
} catch (Exception x) {
if (LOG.isLoggable(Level.FINE))
LOG.fine("Unexpected exception raised in queryNames: "+x);
LOG.log(Level.FINEST,"Unexpected exception raised in queryNames",x);
}
// We reach here only when an exception was raised.
//
final Set<ObjectInstance> empty = Collections.emptySet();
return empty;
}
@Override
public String getDefaultDomain() {
return domainName;
}
@Override
public String[] getDomains() {
return new String[] {domainName};
}
// We call getMBeanCount() on the namespace rather than on the
// source server in order to avoid counting MBeans which are not
// in the domain.
@Override
public Integer getMBeanCount() {
return getNamespace().getMBeanCount();
}
private boolean checkOn() {
final SecurityManager sm = System.getSecurityManager();
return (sm != null);
}
//
// Implements permission checks.
//
@Override
void check(ObjectName routingName, String member, String action) {
if (!checkOn()) return;
final String act = (action==null)?"-":action.intern();
if(act == "queryMBeans" || act == "queryNames") { // ES: OK
// This is tricky. check with 3 parameters is called
// by queryNames/queryMBeans before performing the query.
// At this point we must check with no class name.
// Therefore we pass a className of "-".
// The filtering will be done later - processOutputNames and
// processOutputInstance will call checkQuery.
//
check(routingName, "-", "-", act);
} else {
// This is also tricky:
// passing null here will cause check to retrieve the classname,
// if needed.
check(routingName, null, member, act);
}
}
//
// Implements permission checks.
//
@Override
void checkCreate(ObjectName routingName, String className, String action) {
if (!checkOn()) return;
check(routingName,className,"-",action);
}
//
// Implements permission checks.
//
void check(ObjectName routingName, String className, String member,
String action) {
if (!checkOn()) return;
final MBeanPermission perm;
// action is most probably already an intern string.
// string literals are intern strings.
// we create a new intern string for 'action' - just to be on
// the safe side...
// We intern it in order to be able to use == rather than equals
// below, because if we don't, and if action is not one of the
// 4 literals below, we would have to do a full string comparison.
//
final String act = (action==null)?"-":action.intern();
if (act == "getDomains") { // ES: OK
perm = new MBeanPermission(serverName,"-",member,
routingName,act);
} else {
final String clazz =
(className==null)?getClassName(routingName):className;
perm = new MBeanPermission(serverName,clazz,member,
routingName,act);
}
final SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(perm);
}
String getClassName(ObjectName routingName) {
if (routingName == null || routingName.isPattern()) return "-";
try {
return getNamespace().getSourceServer().
getObjectInstance(routingName).getClassName();
} catch (InstanceNotFoundException ex) {
LOG.finest("Can't get class name for "+routingName+
", using \"-\". Cause is: "+ex);
return "-";
}
}
//
// Implements permission filters for attributes...
//
@Override
AttributeList checkAttributes(ObjectName routingName,
AttributeList attributes, String action) {
if (!checkOn()) return attributes;
final String className = getClassName(routingName);
check(routingName,className,"-",action);
if (attributes == null || attributes.isEmpty()) return attributes;
final AttributeList res = new AttributeList();
for (Attribute at : attributes.asList()) {
try {
check(routingName,className,at.getName(),action);
res.add(at);
} catch (SecurityException x) { // DLS: OK
continue;
}
}
return res;
}
//
// Implements permission filters for attributes...
//
@Override
String[] checkAttributes(ObjectName routingName, String[] attributes,
String action) {
if (!checkOn()) return attributes;
final String className = getClassName(routingName);
check(routingName,className,"-",action);
if (attributes == null || attributes.length==0) return attributes;
final List<String> res = new ArrayList<String>(attributes.length);
for (String at : attributes) {
try {
check(routingName,className,at,action);
res.add(at);
} catch (SecurityException x) { // DLS: OK
continue;
}
}
return res.toArray(new String[res.size()]);
}
//
// Implements permission filters for domains...
//
@Override
String[] checkDomains(String[] domains, String action) {
if (domains == null || domains.length==0 || !checkOn())
return domains;
int count=0;
for (int i=0;i<domains.length;i++) {
try {
check(Util.newObjectName(domains[i]+":x=x"),"-",
"-","getDomains");
} catch (SecurityException x) { // DLS: OK
count++;
domains[i]=null;
}
}
if (count == 0) return domains;
final String[] res = new String[domains.length-count];
count = 0;
for (int i=0;i<domains.length;i++)
if (domains[i]!=null) res[count++]=domains[i];
return res;
}
//
// Implements permission filters for queries...
//
@Override
boolean checkQuery(ObjectName routingName, String action) {
try {
final String className = getClassName(routingName);
check(routingName,className,"-",action);
return true;
} catch (SecurityException x) { // DLS: OK
return false;
}
}
}
/*
* 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.HashMap;
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.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>();
}
/** Creates a new instance of JMXNamespaces */
private JMXNamespaceUtils() {
}
/**
* Returns an unmodifiable option map in which the given keys have been
* filtered out.
* @param keys keys to filter out from the map.
* @return An unmodifiable option map in which the given keys have been
* filtered out.
*/
public static <K,V> Map<K,V> filterMap(Map<K,V> map, K... keys) {
final Map<K,V> filtered;
filtered=new HashMap<K,V>(map);
for (K key : keys) {
filtered.remove(key);
}
return unmodifiableMap(filtered);
}
// 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;
}
}
此差异已折叠。
......@@ -51,4 +51,16 @@ public class InstanceNotFoundException extends OperationsException {
public InstanceNotFoundException(String message) {
super(message);
}
/**
* Constructor for the frequent case where the message is the ObjectName
* of the missing MBean.
*
* @param name the ObjectName of the missing MBean.
*
* @since 1.7
*/
public InstanceNotFoundException(ObjectName name) {
this(name.toString());
}
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册