jdk/src/share/classes/com/sun/jmx/interceptor/DomainDispatchInterceptor.java
changeset 1156 bbc2d15aaf7a
child 1222 78e3d021d528
equal deleted inserted replaced
1155:a9a142fcf1b5 1156:bbc2d15aaf7a
       
     1 /*
       
     2  * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 package com.sun.jmx.interceptor;
       
    27 
       
    28 import com.sun.jmx.defaults.JmxProperties;
       
    29 import com.sun.jmx.mbeanserver.MBeanInstantiator;
       
    30 import com.sun.jmx.mbeanserver.Repository;
       
    31 import com.sun.jmx.mbeanserver.Util;
       
    32 import com.sun.jmx.namespace.DomainInterceptor;
       
    33 import java.util.Queue;
       
    34 import java.util.Set;
       
    35 
       
    36 import java.util.logging.Level;
       
    37 import java.util.logging.Logger;
       
    38 
       
    39 import javax.management.MBeanServer;
       
    40 import javax.management.MBeanServerDelegate;
       
    41 import javax.management.MalformedObjectNameException;
       
    42 import javax.management.ObjectName;
       
    43 import javax.management.QueryExp;
       
    44 import javax.management.namespace.JMXDomain;
       
    45 import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR;
       
    46 
       
    47 /**
       
    48  * A dispatcher that dispatch incoming MBeanServer requests to
       
    49  * DomainInterceptors.
       
    50  * <p><b>
       
    51  * This API is a Sun internal API and is subject to changes without notice.
       
    52  * </b></p>
       
    53  * @since 1.7
       
    54  */
       
    55 //
       
    56 // See comments in  DispatchInterceptor.
       
    57 //
       
    58 class DomainDispatchInterceptor
       
    59         extends DispatchInterceptor<DomainInterceptor, JMXDomain> {
       
    60 
       
    61     /**
       
    62      * A logger for this class.
       
    63      **/
       
    64     private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
       
    65 
       
    66     private static final ObjectName ALL_DOMAINS =
       
    67             JMXDomain.getDomainObjectName("*");
       
    68 
       
    69 
       
    70     /**
       
    71      *  A QueryInterceptor that perform & aggregates queries spanning several
       
    72      *  domains.
       
    73      */
       
    74     final static class AggregatingQueryInterceptor extends QueryInterceptor {
       
    75 
       
    76         private final DomainDispatchInterceptor parent;
       
    77         AggregatingQueryInterceptor(DomainDispatchInterceptor dispatcher) {
       
    78             super(dispatcher.localNamespace);
       
    79             parent = dispatcher;
       
    80         }
       
    81 
       
    82         /**
       
    83          * Perform queryNames or queryMBeans, depending on which QueryInvoker
       
    84          * is passed as argument. This is closures without closures.
       
    85          **/
       
    86         @Override
       
    87         <T> Set<T> query(ObjectName pattern, QueryExp query,
       
    88                 QueryInvoker<T> invoker, MBeanServer localNamespace) {
       
    89             final Set<T> local = invoker.query(localNamespace, pattern, query);
       
    90 
       
    91             // Add all matching MBeans from local namespace.
       
    92             final Set<T> res = Util.cloneSet(local);
       
    93 
       
    94             final boolean all = (pattern == null ||
       
    95                     pattern.getDomain().equals("*"));
       
    96             if (pattern == null) pattern = ObjectName.WILDCARD;
       
    97 
       
    98             final String domain = pattern.getDomain();
       
    99 
       
   100             // If there's no domain pattern, just include the pattern's domain.
       
   101             // Otherwiae, loop over all virtual domains (parent.getKeys()).
       
   102             final String[] keys =
       
   103                 (pattern.isDomainPattern() ?
       
   104                     parent.getKeys() : new String[]{domain});
       
   105 
       
   106             // Add all matching MBeans from each virtual domain
       
   107             //
       
   108             for (String key : keys) {
       
   109                 // Only invoke those virtual domain which are selected
       
   110                 // by the domain pattern
       
   111                 //
       
   112                 if (!all && !Util.isDomainSelected(key, domain))
       
   113                     continue;
       
   114 
       
   115                 try {
       
   116                     final MBeanServer mbs = parent.getInterceptor(key);
       
   117 
       
   118                     // mbs can be null if the interceptor was removed
       
   119                     // concurrently...
       
   120                     // See handlerMap and getKeys() in DispatchInterceptor
       
   121                     //
       
   122                     if (mbs == null) continue;
       
   123 
       
   124                     // If the domain is selected, we can replace the pattern
       
   125                     // by the actual domain. This is safer if we want to avoid
       
   126                     // a domain (which could be backed up by an MBeanServer) to
       
   127                     // return names from outside the domain.
       
   128                     // So instead of asking the domain handler for "foo" to
       
   129                     // return all names which match "?o*:type=Bla,*" we're
       
   130                     // going to ask it to return all names which match
       
   131                     // "foo:type=Bla,*"
       
   132                     //
       
   133                     final ObjectName subPattern = pattern.withDomain(key);
       
   134                     res.addAll(invoker.query(mbs, subPattern, query));
       
   135                 } catch (Exception x) {
       
   136                     LOG.finest("Ignoring exception " +
       
   137                             "when attempting to query namespace "+key+": "+x);
       
   138                     continue;
       
   139                 }
       
   140             }
       
   141             return res;
       
   142         }
       
   143     }
       
   144 
       
   145     private final DefaultMBeanServerInterceptor localNamespace;
       
   146     private final String mbeanServerName;
       
   147     private final MBeanServerDelegate delegate;
       
   148 
       
   149     /**
       
   150      * Creates a DomainDispatchInterceptor with the specified
       
   151      * repository instance.
       
   152      *
       
   153      * @param outer A pointer to the MBeanServer object that must be
       
   154      *        passed to the MBeans when invoking their
       
   155      *        {@link javax.management.MBeanRegistration} interface.
       
   156      * @param delegate A pointer to the MBeanServerDelegate associated
       
   157      *        with the new MBeanServer. The new MBeanServer must register
       
   158      *        this MBean in its MBean repository.
       
   159      * @param instantiator The MBeanInstantiator that will be used to
       
   160      *        instantiate MBeans and take care of class loading issues.
       
   161      * @param repository The repository to use for this MBeanServer
       
   162      */
       
   163     public DomainDispatchInterceptor(MBeanServer         outer,
       
   164                             MBeanServerDelegate delegate,
       
   165                             MBeanInstantiator   instantiator,
       
   166                             Repository          repository,
       
   167                             NamespaceDispatchInterceptor namespaces)  {
       
   168            localNamespace = new DefaultMBeanServerInterceptor(outer,
       
   169                    delegate, instantiator,repository,namespaces);
       
   170            mbeanServerName = Util.getMBeanServerSecurityName(delegate);
       
   171            this.delegate = delegate;
       
   172     }
       
   173 
       
   174     final boolean isLocalHandlerNameFor(String domain,
       
   175             ObjectName handlerName) {
       
   176         if (domain == null) return true;
       
   177         return handlerName.getDomain().equals(domain) &&
       
   178                JMXDomain.TYPE_ASSIGNMENT.equals(
       
   179                handlerName.getKeyPropertyListString());
       
   180     }
       
   181 
       
   182     @Override
       
   183     void validateHandlerNameFor(String key, ObjectName name) {
       
   184         super.validateHandlerNameFor(key,name);
       
   185         final String[] domains = localNamespace.getDomains();
       
   186         for (int i=0;i<domains.length;i++) {
       
   187             if (domains[i].equals(key))
       
   188                 throw new IllegalArgumentException("domain "+key+
       
   189                         " is not empty");
       
   190         }
       
   191     }
       
   192 
       
   193     @Override
       
   194     final MBeanServer getInterceptorOrNullFor(ObjectName name) {
       
   195         if (name == null) return localNamespace;
       
   196         final String domain = name.getDomain();
       
   197         if (domain.endsWith(NAMESPACE_SEPARATOR)) return localNamespace;
       
   198         if (domain.contains(NAMESPACE_SEPARATOR)) return null;
       
   199         final String localDomain = domain;
       
   200         if (isLocalHandlerNameFor(localDomain,name)) {
       
   201             LOG.finer("dispatching to local namespace");
       
   202             return localNamespace;
       
   203         }
       
   204         final DomainInterceptor ns = getInterceptor(localDomain);
       
   205         if (ns == null) {
       
   206             if (LOG.isLoggable(Level.FINER)) {
       
   207                 LOG.finer("dispatching to local namespace: " + localDomain);
       
   208             }
       
   209             return getNextInterceptor();
       
   210         }
       
   211         if (LOG.isLoggable(Level.FINER)) {
       
   212             LOG.finer("dispatching to domain: " + localDomain);
       
   213         }
       
   214         return ns;
       
   215     }
       
   216 
       
   217     private boolean multipleQuery(ObjectName pattern) {
       
   218         if (pattern == null) return true;
       
   219         if (pattern.isDomainPattern()) return true;
       
   220 
       
   221         try {
       
   222             // This is a bit of a hack. If there's any chance that a JMXDomain
       
   223             // MBean name is selected by the given pattern then we must include
       
   224             // the local namespace in our search.
       
   225             // Returning true will have this effect.
       
   226             if (pattern.apply(ALL_DOMAINS.withDomain(pattern.getDomain())))
       
   227                 return true;
       
   228         } catch (MalformedObjectNameException x) {
       
   229             // should not happen
       
   230             throw new IllegalArgumentException(String.valueOf(pattern), x);
       
   231         }
       
   232         return false;
       
   233     }
       
   234 
       
   235     @Override
       
   236     final QueryInterceptor getInterceptorForQuery(ObjectName pattern) {
       
   237 
       
   238         // Check if we need to aggregate.
       
   239         if (multipleQuery(pattern))
       
   240             return new AggregatingQueryInterceptor(this);
       
   241 
       
   242         // We don't need to aggregate: do the "simple" thing...
       
   243         final String domain = pattern.getDomain();
       
   244 
       
   245         // Do we have a virtual domain?
       
   246         final DomainInterceptor ns = getInterceptor(domain);
       
   247         if (ns != null) {
       
   248             if (LOG.isLoggable(Level.FINER))
       
   249                 LOG.finer("dispatching to domain: " + domain);
       
   250             return new QueryInterceptor(ns);
       
   251         }
       
   252 
       
   253         // We don't have a virtual domain. Send to local domains.
       
   254         if (LOG.isLoggable(Level.FINER))
       
   255              LOG.finer("dispatching to local namespace: " + domain);
       
   256         return new QueryInterceptor(localNamespace);
       
   257     }
       
   258 
       
   259     @Override
       
   260     final ObjectName getHandlerNameFor(String key)
       
   261         throws MalformedObjectNameException {
       
   262         return JMXDomain.getDomainObjectName(key);
       
   263     }
       
   264 
       
   265     @Override
       
   266     final public String getHandlerKey(ObjectName name) {
       
   267         return name.getDomain();
       
   268     }
       
   269 
       
   270     @Override
       
   271     final DomainInterceptor createInterceptorFor(String key,
       
   272             ObjectName name, JMXDomain handler,
       
   273             Queue<Runnable> postRegisterQueue) {
       
   274         final DomainInterceptor ns =
       
   275                 new DomainInterceptor(mbeanServerName,handler,key);
       
   276         ns.addPostRegisterTask(postRegisterQueue, delegate);
       
   277         if (LOG.isLoggable(Level.FINER)) {
       
   278             LOG.finer("DomainInterceptor created: "+ns);
       
   279         }
       
   280         return ns;
       
   281     }
       
   282 
       
   283     @Override
       
   284     final void interceptorReleased(DomainInterceptor interceptor,
       
   285             Queue<Runnable> postDeregisterQueue) {
       
   286         interceptor.addPostDeregisterTask(postDeregisterQueue, delegate);
       
   287     }
       
   288 
       
   289     @Override
       
   290     final DefaultMBeanServerInterceptor getNextInterceptor() {
       
   291         return localNamespace;
       
   292     }
       
   293 
       
   294     /**
       
   295      * Returns the list of domains in which any MBean is currently
       
   296      * registered.
       
   297      */
       
   298     @Override
       
   299     public String[] getDomains() {
       
   300         // A JMXDomain is registered in its own domain.
       
   301         // Therefore, localNamespace.getDomains() contains all domains.
       
   302         // In addition, localNamespace will perform the necessary
       
   303         // MBeanPermission checks for getDomains().
       
   304         //
       
   305         return localNamespace.getDomains();
       
   306     }
       
   307 
       
   308     /**
       
   309      * Returns the number of MBeans registered in the MBean server.
       
   310      */
       
   311     @Override
       
   312     public Integer getMBeanCount() {
       
   313         int count = getNextInterceptor().getMBeanCount().intValue();
       
   314         final String[] keys = getKeys();
       
   315         for (String key:keys) {
       
   316             final MBeanServer mbs = getInterceptor(key);
       
   317             if (mbs == null) continue;
       
   318             count += mbs.getMBeanCount().intValue();
       
   319         }
       
   320         return Integer.valueOf(count);
       
   321     }
       
   322 }