--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/jmx/interceptor/DispatchInterceptor.java Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,547 @@
+/*
+ * 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.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+
+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.MBeanInfo;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.NotCompliantMBeanException;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.QueryExp;
+import javax.management.ReflectionException;
+import javax.management.namespace.JMXNamespace;
+
+/**
+ * A dispatcher that dispatches to MBeanServers.
+ * <p><b>
+ * This API is a Sun internal API and is subject to changes without notice.
+ * </b></p>
+ * @since 1.7
+ */
+//
+// This is the base class for implementing dispatchers. We have two concrete
+// dispatcher implementations:
+//
+// * A NamespaceDispatchInterceptor, which dispatch calls to existing
+// namespace interceptors
+// * A DomainDispatchInterceptor, which dispatch calls to existing domain
+// interceptors.
+//
+// With the JMX Namespaces feature, the JMX MBeanServer is now structured
+// as follows:
+//
+// The JMX MBeanServer delegates to a NamespaceDispatchInterceptor,
+// which either dispatches to a namespace, or delegates to the
+// DomainDispatchInterceptor (if the object name contained no namespace).
+// The DomainDispatchInterceptor in turn either dispatches to a domain (if
+// there is a JMXDomain for that domain) or delegates to the
+// DefaultMBeanServerInterceptor (if there is no JMXDomain for that
+// domain). This makes the following picture:
+//
+// JMX MBeanServer (outer shell)
+// |
+// |
+// NamespaceDispatchInterceptor
+// / \
+// no namespace in object name? \
+// / \
+// / dispatch to namespace
+// DomainDispatchInterceptor
+// / \
+// no JMXDomain for domain? \
+// / \
+// / dispatch to domain
+// DefaultMBeanServerInterceptor
+// /
+// invoke locally registered MBean
+//
+// The logic for maintaining a map of interceptors
+// and dispatching to impacted interceptor, is implemented in this
+// base class, which both NamespaceDispatchInterceptor and
+// DomainDispatchInterceptor extend.
+//
+public abstract class DispatchInterceptor
+ <T extends MBeanServer, N extends JMXNamespace>
+ extends MBeanServerInterceptorSupport {
+
+ /**
+ * This is an abstraction which allows us to handle queryNames
+ * and queryMBeans with the same algorithm. There are some subclasses
+ * where we need to override both queryNames & queryMBeans to apply
+ * the same transformation (usually aggregation of results when
+ * several namespaces/domains are impacted) to both algorithms.
+ * Usually the only thing that varies between the algorithm of
+ * queryNames & the algorithm of queryMBean is the type of objects
+ * in the returned Set. By using a QueryInvoker we can implement the
+ * transformation only once and apply it to both queryNames &
+ * queryMBeans.
+ * @see QueryInterceptor below, and its subclass in
+ * {@link DomainDispatcher}.
+ **/
+ static abstract class QueryInvoker<T> {
+ abstract Set<T> query(MBeanServer mbs,
+ ObjectName pattern, QueryExp query);
+ }
+
+ /**
+ * Used to perform queryNames. A QueryInvoker that invokes
+ * queryNames on an MBeanServer.
+ **/
+ final static QueryInvoker<ObjectName> queryNamesInvoker =
+ new QueryInvoker<ObjectName>() {
+ Set<ObjectName> query(MBeanServer mbs,
+ ObjectName pattern, QueryExp query) {
+ return mbs.queryNames(pattern,query);
+ }
+ };
+
+ /**
+ * Used to perform queryMBeans. A QueryInvoker that invokes
+ * queryMBeans on an MBeanServer.
+ **/
+ final static QueryInvoker<ObjectInstance> queryMBeansInvoker =
+ new QueryInvoker<ObjectInstance>() {
+ Set<ObjectInstance> query(MBeanServer mbs,
+ ObjectName pattern, QueryExp query) {
+ return mbs.queryMBeans(pattern,query);
+ }
+ };
+
+ /**
+ * We use this class to intercept queries.
+ * There's a special case for JMXNamespace MBeans, because
+ * "namespace//*:*" matches both "namespace//domain:k=v" and
+ * "namespace//:type=JMXNamespace".
+ * Therefore, queries may need to be forwarded to more than
+ * on interceptor and the results aggregated...
+ */
+ static class QueryInterceptor {
+ final MBeanServer wrapped;
+ QueryInterceptor(MBeanServer mbs) {
+ wrapped = mbs;
+ }
+ <X> Set<X> query(ObjectName pattern, QueryExp query,
+ QueryInvoker<X> invoker, MBeanServer server) {
+ return invoker.query(server, pattern, query);
+ }
+
+ public Set<ObjectName> queryNames(ObjectName pattern, QueryExp query) {
+ return query(pattern,query,queryNamesInvoker,wrapped);
+ }
+
+ public Set<ObjectInstance> queryMBeans(ObjectName pattern,
+ QueryExp query) {
+ return query(pattern,query,queryMBeansInvoker,wrapped);
+ }
+ }
+
+ // We don't need a ConcurrentHashMap here because getkeys() returns
+ // an array of keys. Therefore there's no risk to have a
+ // ConcurrentModificationException. We must however take into
+ // account the fact that there can be no interceptor for
+ // some of the returned keys if the map is being modified by
+ // another thread, or by a callback within the same thread...
+ // See getKeys() in this class and query() in DomainDispatcher.
+ //
+ private final Map<String,T> handlerMap =
+ Collections.synchronizedMap(
+ new HashMap<String,T>());
+
+ // The key at which an interceptor for accessing the named MBean can be
+ // found in the handlerMap. Note: there doesn't need to be an interceptor
+ // for that key in the Map.
+ //
+ public abstract String getHandlerKey(ObjectName name);
+
+ // Returns an interceptor for that name, or null if there's no interceptor
+ // for that name.
+ abstract MBeanServer getInterceptorOrNullFor(ObjectName name);
+
+ // Returns a QueryInterceptor for that pattern.
+ abstract QueryInterceptor getInterceptorForQuery(ObjectName pattern);
+
+ // Returns the ObjectName of the JMXNamespace (or JMXDomain) for that
+ // key (a namespace or a domain name).
+ abstract ObjectName getHandlerNameFor(String key)
+ throws MalformedObjectNameException;
+
+ // Creates an interceptor for the given key, name, JMXNamespace (or
+ // JMXDomain). Note: this will be either a NamespaceInterceptor
+ // wrapping a JMXNamespace, if this object is an instance of
+ // NamespaceDispatchInterceptor, or a DomainInterceptor wrapping a
+ // JMXDomain, if this object is an instance of DomainDispatchInterceptor.
+ abstract T createInterceptorFor(String key, ObjectName name,
+ N jmxNamespace, Queue<Runnable> postRegisterQueue);
+ //
+ // The next interceptor in the chain.
+ //
+ // For the NamespaceDispatchInterceptor, this the DomainDispatchInterceptor.
+ // For the DomainDispatchInterceptor, this is the
+ // DefaultMBeanServerInterceptor.
+ //
+ // The logic of when to invoke the next interceptor in the chain depends
+ // on the logic of the concrete dispatcher class.
+ //
+ // For instance, the NamespaceDispatchInterceptor invokes the next
+ // interceptor when the object name doesn't contain any namespace.
+ //
+ // On the other hand, the DomainDispatchInterceptor invokes the
+ // next interceptor when there's no interceptor for the accessed domain.
+ //
+ abstract MBeanServer getNextInterceptor();
+
+ // hook for cleanup in subclasses.
+ void interceptorReleased(T interceptor,
+ Queue<Runnable> postDeregisterQueue) {
+ // hook
+ }
+
+ // Hook for subclasses.
+ MBeanServer getInterceptorForCreate(ObjectName name)
+ throws MBeanRegistrationException {
+ final MBeanServer ns = getInterceptorOrNullFor(name);
+ if (ns == null) // name cannot be null here.
+ throw new MBeanRegistrationException(
+ new IllegalArgumentException("No such MBean handler: " +
+ getHandlerKey(name) + " for " +name));
+ return ns;
+ }
+
+ // Hook for subclasses.
+ MBeanServer getInterceptorForInstance(ObjectName name)
+ throws InstanceNotFoundException {
+ final MBeanServer ns = getInterceptorOrNullFor(name);
+ if (ns == null) // name cannot be null here.
+ throw new InstanceNotFoundException(String.valueOf(name));
+ return ns;
+ }
+
+ // sanity checks
+ void validateHandlerNameFor(String key, ObjectName name) {
+ if (key == null || key.equals(""))
+ throw new IllegalArgumentException("invalid key for "+name+": "+key);
+ try {
+ final ObjectName handlerName = getHandlerNameFor(key);
+ if (!name.equals(handlerName))
+ throw new IllegalArgumentException("bad handler name: "+name+
+ ". Should be: "+handlerName);
+ } catch (MalformedObjectNameException x) {
+ throw new IllegalArgumentException(name.toString(),x);
+ }
+ }
+
+ // Called by the DefaultMBeanServerInterceptor when an instance
+ // of JMXNamespace (or a subclass of it) is registered as an MBean.
+ // This method is usually invoked from within the repository lock,
+ // hence the necessity of the postRegisterQueue.
+ public void addNamespace(ObjectName name, N jmxNamespace,
+ Queue<Runnable> postRegisterQueue) {
+ final String key = getHandlerKey(name);
+ validateHandlerNameFor(key,name);
+ synchronized (handlerMap) {
+ final T exists =
+ handlerMap.get(key);
+ if (exists != null)
+ throw new IllegalArgumentException(key+
+ ": handler already exists");
+
+ final T ns = createInterceptorFor(key,name,jmxNamespace,
+ postRegisterQueue);
+ handlerMap.put(key,ns);
+ }
+ }
+
+ // Called by the DefaultMBeanServerInterceptor when an instance
+ // of JMXNamespace (or a subclass of it) is deregistered.
+ // This method is usually invoked from within the repository lock,
+ // hence the necessity of the postDeregisterQueue.
+ public void removeNamespace(ObjectName name, N jmxNamespace,
+ Queue<Runnable> postDeregisterQueue) {
+ final String key = getHandlerKey(name);
+ final T ns;
+ synchronized(handlerMap) {
+ ns = handlerMap.remove(key);
+ }
+ interceptorReleased(ns,postDeregisterQueue);
+ }
+
+ // Get the interceptor for that key.
+ T getInterceptor(String key) {
+ synchronized (handlerMap) {
+ return handlerMap.get(key);
+ }
+ }
+
+ // We return an array of keys, which makes it possible to make
+ // concurrent modifications of the handlerMap, provided that
+ // the code which loops over the keys is prepared to handle null
+ // interceptors.
+ // See declaration of handlerMap above, and see also query() in
+ // DomainDispatcher
+ //
+ public String[] getKeys() {
+ synchronized (handlerMap) {
+ final int size = handlerMap.size();
+ return handlerMap.keySet().toArray(new String[size]);
+ }
+ }
+
+ // From MBeanServer
+ public ObjectInstance createMBean(String className, ObjectName name)
+ throws ReflectionException, InstanceAlreadyExistsException,
+ MBeanRegistrationException, MBeanException,
+ NotCompliantMBeanException {
+ return getInterceptorForCreate(name).createMBean(className,name);
+ }
+
+ // From MBeanServer
+ public ObjectInstance createMBean(String className, ObjectName name,
+ ObjectName loaderName)
+ throws ReflectionException, InstanceAlreadyExistsException,
+ MBeanRegistrationException, MBeanException,
+ NotCompliantMBeanException, InstanceNotFoundException{
+ return getInterceptorForCreate(name).createMBean(className,name,loaderName);
+ }
+
+ // From MBeanServer
+ public ObjectInstance createMBean(String className, ObjectName name,
+ Object params[], String signature[])
+ throws ReflectionException, InstanceAlreadyExistsException,
+ MBeanRegistrationException, MBeanException,
+ NotCompliantMBeanException{
+ return getInterceptorForCreate(name).
+ createMBean(className,name,params,signature);
+ }
+
+ // From MBeanServer
+ public ObjectInstance createMBean(String className, ObjectName name,
+ ObjectName loaderName, Object params[],
+ String signature[])
+ throws ReflectionException, InstanceAlreadyExistsException,
+ MBeanRegistrationException, MBeanException,
+ NotCompliantMBeanException, InstanceNotFoundException{
+ return getInterceptorForCreate(name).createMBean(className,name,loaderName,
+ params,signature);
+ }
+
+ // From MBeanServer
+ public ObjectInstance registerMBean(Object object, ObjectName name)
+ throws InstanceAlreadyExistsException, MBeanRegistrationException,
+ NotCompliantMBeanException {
+ return getInterceptorForCreate(name).registerMBean(object,name);
+ }
+
+ // From MBeanServer
+ public void unregisterMBean(ObjectName name)
+ throws InstanceNotFoundException, MBeanRegistrationException {
+ getInterceptorForInstance(name).unregisterMBean(name);
+ }
+
+ // From MBeanServer
+ public ObjectInstance getObjectInstance(ObjectName name)
+ throws InstanceNotFoundException {
+ return getInterceptorForInstance(name).getObjectInstance(name);
+ }
+
+ // From MBeanServer
+ public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
+ final QueryInterceptor mbs =
+ getInterceptorForQuery(name);
+ if (mbs == null) return Collections.emptySet();
+ else return mbs.queryMBeans(name,query);
+ }
+
+ // From MBeanServer
+ public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
+ final QueryInterceptor mbs =
+ getInterceptorForQuery(name);
+ if (mbs == null) return Collections.emptySet();
+ else return mbs.queryNames(name,query);
+ }
+
+ // From MBeanServer
+ public boolean isRegistered(ObjectName name) {
+ final MBeanServer mbs = getInterceptorOrNullFor(name);
+ if (mbs == null) return false;
+ else return mbs.isRegistered(name);
+ }
+
+ // From MBeanServer
+ public Integer getMBeanCount() {
+ return getNextInterceptor().getMBeanCount();
+ }
+
+ // From MBeanServer
+ public Object getAttribute(ObjectName name, String attribute)
+ throws MBeanException, AttributeNotFoundException,
+ InstanceNotFoundException, ReflectionException {
+ return getInterceptorForInstance(name).getAttribute(name,attribute);
+ }
+
+ // From MBeanServer
+ public AttributeList getAttributes(ObjectName name, String[] attributes)
+ throws InstanceNotFoundException, ReflectionException {
+ return getInterceptorForInstance(name).getAttributes(name,attributes);
+ }
+
+ // From MBeanServer
+ public void setAttribute(ObjectName name, Attribute attribute)
+ throws InstanceNotFoundException, AttributeNotFoundException,
+ InvalidAttributeValueException, MBeanException,
+ ReflectionException {
+ getInterceptorForInstance(name).setAttribute(name,attribute);
+ }
+
+ // From MBeanServer
+ public AttributeList setAttributes(ObjectName name,
+ AttributeList attributes)
+ throws InstanceNotFoundException, ReflectionException {
+ return getInterceptorForInstance(name).setAttributes(name,attributes);
+ }
+
+ // From MBeanServer
+ public Object invoke(ObjectName name, String operationName,
+ Object params[], String signature[])
+ throws InstanceNotFoundException, MBeanException,
+ ReflectionException {
+ return getInterceptorForInstance(name).invoke(name,operationName,params,
+ signature);
+ }
+
+ // From MBeanServer
+ public String getDefaultDomain() {
+ return getNextInterceptor().getDefaultDomain();
+ }
+
+ /**
+ * Returns the list of domains in which any MBean is currently
+ * registered.
+ */
+ public abstract String[] getDomains();
+
+ // From MBeanServer
+ public void addNotificationListener(ObjectName name,
+ NotificationListener listener,
+ NotificationFilter filter,
+ Object handback)
+ throws InstanceNotFoundException {
+ getInterceptorForInstance(name).addNotificationListener(name,listener,filter,
+ handback);
+ }
+
+
+ // From MBeanServer
+ public void addNotificationListener(ObjectName name,
+ ObjectName listener,
+ NotificationFilter filter,
+ Object handback)
+ throws InstanceNotFoundException {
+ getInterceptorForInstance(name).addNotificationListener(name,listener,filter,
+ handback);
+ }
+
+ // From MBeanServer
+ public void removeNotificationListener(ObjectName name,
+ ObjectName listener)
+ throws InstanceNotFoundException, ListenerNotFoundException {
+ getInterceptorForInstance(name).removeNotificationListener(name,listener);
+ }
+
+ // From MBeanServer
+ public void removeNotificationListener(ObjectName name,
+ ObjectName listener,
+ NotificationFilter filter,
+ Object handback)
+ throws InstanceNotFoundException, ListenerNotFoundException {
+ getInterceptorForInstance(name).removeNotificationListener(name,listener,filter,
+ handback);
+ }
+
+
+ // From MBeanServer
+ public void removeNotificationListener(ObjectName name,
+ NotificationListener listener)
+ throws InstanceNotFoundException, ListenerNotFoundException {
+ getInterceptorForInstance(name).removeNotificationListener(name,listener);
+ }
+
+ // From MBeanServer
+ public void removeNotificationListener(ObjectName name,
+ NotificationListener listener,
+ NotificationFilter filter,
+ Object handback)
+ throws InstanceNotFoundException, ListenerNotFoundException {
+ getInterceptorForInstance(name).removeNotificationListener(name,listener,filter,
+ handback);
+ }
+
+ // From MBeanServer
+ public MBeanInfo getMBeanInfo(ObjectName name)
+ throws InstanceNotFoundException, IntrospectionException,
+ ReflectionException {
+ return getInterceptorForInstance(name).getMBeanInfo(name);
+ }
+
+
+ // From MBeanServer
+ public boolean isInstanceOf(ObjectName name, String className)
+ throws InstanceNotFoundException {
+ return getInterceptorForInstance(name).isInstanceOf(name,className);
+ }
+
+ // From MBeanServer
+ public ClassLoader getClassLoaderFor(ObjectName mbeanName)
+ throws InstanceNotFoundException {
+ return getInterceptorForInstance(mbeanName).getClassLoaderFor(mbeanName);
+ }
+
+ // From MBeanServer
+ public ClassLoader getClassLoader(ObjectName loaderName)
+ throws InstanceNotFoundException {
+ return getInterceptorForInstance(loaderName).getClassLoader(loaderName);
+ }
+
+}