jdk/src/share/classes/javax/management/namespace/JMXNamespaces.java
changeset 4156 acaa49a2768a
parent 4155 460e37d40f12
child 4159 9e3aae7675f1
equal deleted inserted replaced
4155:460e37d40f12 4156:acaa49a2768a
     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 javax.management.namespace;
       
    27 
       
    28 import com.sun.jmx.defaults.JmxProperties;
       
    29 import com.sun.jmx.namespace.ObjectNameRouter;
       
    30 import com.sun.jmx.namespace.serial.RewritingProcessor;
       
    31 import com.sun.jmx.namespace.RoutingConnectionProxy;
       
    32 import com.sun.jmx.namespace.RoutingServerProxy;
       
    33 
       
    34 import java.util.logging.Level;
       
    35 import java.util.logging.Logger;
       
    36 
       
    37 import javax.management.InstanceNotFoundException;
       
    38 import javax.management.MBeanServer;
       
    39 import javax.management.MBeanServerConnection;
       
    40 import javax.management.MalformedObjectNameException;
       
    41 import javax.management.ObjectName;
       
    42 
       
    43 /**
       
    44  * Static constants and utility methods to help work with
       
    45  * JMX name spaces.  There are no instances of this class.
       
    46  * @since 1.7
       
    47  */
       
    48 public class JMXNamespaces {
       
    49 
       
    50     /**
       
    51      * A logger for this class.
       
    52      **/
       
    53     private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
       
    54 
       
    55     /** Creates a new instance of JMXNamespaces */
       
    56     private JMXNamespaces() {
       
    57     }
       
    58 
       
    59     /**
       
    60      * The name space separator. This is an alias for {@link
       
    61      * ObjectName#NAMESPACE_SEPARATOR}.
       
    62      **/
       
    63     public static final String NAMESPACE_SEPARATOR =
       
    64             ObjectName.NAMESPACE_SEPARATOR;
       
    65     private static final int NAMESPACE_SEPARATOR_LENGTH =
       
    66             NAMESPACE_SEPARATOR.length();
       
    67 
       
    68 
       
    69     /**
       
    70      * Creates a new {@code MBeanServerConnection} proxy on a
       
    71      * {@linkplain javax.management.namespace sub name space}
       
    72      * of the given parent.
       
    73      *
       
    74      * @param parent The parent {@code MBeanServerConnection} that contains
       
    75      *               the name space.
       
    76      * @param namespace  The {@linkplain javax.management.namespace
       
    77      *               name space} in which to narrow.
       
    78      * @return A new {@code MBeanServerConnection} proxy that shows the content
       
    79      *         of that name space.
       
    80      * @throws IllegalArgumentException if either argument is null,
       
    81      * or the name space does not exist, or if a proxy for that name space
       
    82      * cannot be created.  The {@linkplain Throwable#getCause() cause} of
       
    83      * this exception will be an {@link InstanceNotFoundException} if and only
       
    84      * if the name space is found not to exist.
       
    85      */
       
    86     public static MBeanServerConnection narrowToNamespace(
       
    87                         MBeanServerConnection parent,
       
    88                         String namespace) {
       
    89         if (LOG.isLoggable(Level.FINER))
       
    90             LOG.finer("Making MBeanServerConnection for: " +namespace);
       
    91         return RoutingConnectionProxy.cd(parent, namespace, true);
       
    92     }
       
    93 
       
    94     /**
       
    95      * Creates a new {@code MBeanServer} proxy on a
       
    96      * {@linkplain javax.management.namespace sub name space}
       
    97      * of the given parent.
       
    98      *
       
    99      * @param parent The parent {@code MBeanServer} that contains
       
   100      *               the name space.
       
   101      * @param namespace  The {@linkplain javax.management.namespace
       
   102      *               name space} in which to narrow.
       
   103      * @return A new {@code MBeanServer} proxy that shows the content
       
   104      *         of that name space.
       
   105      * @throws IllegalArgumentException if either argument is null,
       
   106      * or the name space does not exist, or if a proxy for that name space
       
   107      * cannot be created.  The {@linkplain Throwable#getCause() cause} of
       
   108      * this exception will be an {@link InstanceNotFoundException} if and only
       
   109      * if the name space is found not to exist.
       
   110      */
       
   111     public static MBeanServer narrowToNamespace(MBeanServer parent,
       
   112             String namespace) {
       
   113         if (LOG.isLoggable(Level.FINER))
       
   114             LOG.finer("Making MBeanServer for: " +namespace);
       
   115         return RoutingServerProxy.cd(parent, namespace, true);
       
   116     }
       
   117 
       
   118     /**
       
   119      * Returns an object that is the same as the given object except that
       
   120      * any {@link ObjectName} it might contain has its domain modified.
       
   121      * The returned object might be identical to the given object if it
       
   122      * does not contain any {@code ObjectName} values or if none of them
       
   123      * were modified.
       
   124      * This method will replace a prefix ({@code toRemove}) from the path of
       
   125      * the ObjectNames contained in {@code obj} by another prefix
       
   126      * ({@code toAdd}).
       
   127      * Therefore, all contained ObjectNames must have a path that start with
       
   128      * the given {@code toRemove} prefix. If one of them doesn't, an {@link
       
   129      * IllegalArgumentException} is thrown.
       
   130      * <p>
       
   131      * For instance, if {@code obj} contains the ObjectName
       
   132      * {@code x//y//z//d:k=x}, and {@code toAdd} is {@code v//w}, and
       
   133      * {@code toRemove}
       
   134      *  is {@code x//y} this method will return a copy of {@code obj} that
       
   135      * contains {@code v//w//z//d:k=x}.<br>
       
   136      * On the other hand, if {@code obj} contains the ObjectName
       
   137      * {@code x//y//z//d:k=x}, and {@code toAdd} is {@code v//w}, and
       
   138      * {@code toRemove} is {@code v} this method
       
   139      * will raise an exception, because {@code x//y//z//d:k=x} doesn't start
       
   140      * with {@code v}
       
   141      * </p>
       
   142      * <p>Note: the default implementation of this method can use the
       
   143      *   Java serialization framework to clone and replace ObjectNames in the
       
   144      *   provided {@code obj}. It will usually fail if {@code obj} is not
       
   145      *   Java serializable, or contains objects which are not Java
       
   146      *   serializable.
       
   147      * </p>
       
   148      * @param obj    The object to deep-rewrite
       
   149      * @param toRemove a prefix already present in contained ObjectNames.
       
   150      *        If {@code toRemove} is the empty string {@code ""}, nothing
       
   151      *        will be removed from the contained ObjectNames.
       
   152      * @param toAdd the prefix that will replace (@code toRemove} in contained
       
   153      *  ObjectNames.
       
   154      *        If {@code toAdd} is the empty string {@code ""}, nothing
       
   155      *        will be added to the contained ObjectNames.
       
   156      * @return the rewritten object, or possibly {@code obj} if nothing needed
       
   157      * to be changed.
       
   158      * @throws IllegalArgumentException if {@code obj} couldn't be rewritten or
       
   159      * if {@code toRemove} or {@code toAdd} is null.
       
   160      **/
       
   161     public static <T> T deepReplaceHeadNamespace(T obj, String toRemove, String toAdd) {
       
   162         final RewritingProcessor processor =
       
   163                 RewritingProcessor.newRewritingProcessor(toAdd,toRemove);
       
   164         return processor.rewriteOutput(obj);
       
   165     }
       
   166 
       
   167     /**
       
   168      * Appends {@code namespace} to {@code path}.
       
   169      * This methods appends {@code namespace} to {@code path} to obtain a
       
   170      * a <i>full path</i>, and normalizes the result thus obtained:
       
   171      * <ul>
       
   172      * <li>If {@code path} is empty, the full path is
       
   173      *     {@code namespace}.</li>
       
   174      * <li>Otherwise, if {@code namespace} is empty,
       
   175      *     the full path is {@code path}</li>
       
   176      * <li>Otherwise, and this is the regular case, the full path is the
       
   177      *     result of the concatenation of
       
   178      *     {@code path}+{@value #NAMESPACE_SEPARATOR}+{@code namespace}</li>
       
   179      * <li>finally, the full path is normalized: multiple consecutive
       
   180      *     occurrences of {@value #NAMESPACE_SEPARATOR} are replaced by a
       
   181      *     single {@value #NAMESPACE_SEPARATOR} in the result, and trailing
       
   182      *     occurences of {@value #NAMESPACE_SEPARATOR} are removed.
       
   183      * </li>
       
   184      * </ul>
       
   185      * @param path a name space path prefix
       
   186      * @param namespace a name space name to append to the path
       
   187      * @return a syntactically valid name space path, or "" if both parameters
       
   188      * are null or empty.
       
   189      * @throws IllegalArgumentException if either argument is null or ends with
       
   190      * an odd number of {@code /} characters.
       
   191      **/
       
   192     public static String concat(String path, String namespace) {
       
   193         if (path == null || namespace == null)
       
   194             throw new IllegalArgumentException("Null argument");
       
   195         checkTrailingSlashes(path);
       
   196         checkTrailingSlashes(namespace);
       
   197         final String result;
       
   198         if (path.equals("")) result=namespace;
       
   199         else if (namespace.equals("")) result=path;
       
   200         else result=path+NAMESPACE_SEPARATOR+namespace;
       
   201         return ObjectNameRouter.normalizeNamespacePath(result,false,true,false);
       
   202     }
       
   203 
       
   204     /**
       
   205      * Returns a syntactically valid name space path.
       
   206      * If the provided {@code namespace} ends with {@code "//"},
       
   207      * recursively strips trailing {@code "//"}.  Each sequence of an
       
   208      * even number of {@code "/"} characters is also replaced by {@code "//"},
       
   209      * for example {@code "foo//bar////baz/////buh"} will become
       
   210      * {@code "foo//bar//baz///buh"}.
       
   211      *
       
   212      * @param namespace A name space path
       
   213      * @return {@code ""} - if the provided {@code namespace} resolves to
       
   214      * the empty string; otherwise a syntactically valid name space string
       
   215      * stripped of trailing and redundant {@code "//"}.
       
   216      * @throws IllegalArgumentException if {@code namespace} is null or
       
   217      * is not syntactically valid (e.g. it contains
       
   218      * invalid characters like ':', or it ends with an odd
       
   219      * number of '/').
       
   220      */
       
   221     public static String normalizeNamespaceName(String namespace) {
       
   222         if (namespace == null)
       
   223             throw new IllegalArgumentException("Null namespace");
       
   224         final String sourcePath =
       
   225                 ObjectNameRouter.normalizeNamespacePath(namespace,false,true,false);
       
   226         if (sourcePath.equals("")) return sourcePath;
       
   227 
       
   228         // Will throw an IllegalArgumentException if the namespace name
       
   229         // is not syntactically valid...
       
   230         //
       
   231         getNamespaceObjectName(sourcePath);
       
   232         return sourcePath;
       
   233     }
       
   234 
       
   235 
       
   236     /**
       
   237      * Return a canonical handler name for the provided {@code namespace},
       
   238      * The handler name returned will be
       
   239      * {@link #normalizeNamespaceName normalizeNamespaceName}{@code (namespace) +
       
   240      * "//:type=JMXNamespace"}.
       
   241      *
       
   242      * @param namespace A name space path
       
   243      * @return a canonical ObjectName for a name space handler.
       
   244      * @see #normalizeNamespaceName
       
   245      * @throws IllegalArgumentException if the provided
       
   246      *          {@code namespace} is null or not valid.
       
   247      */
       
   248     public static ObjectName getNamespaceObjectName(String namespace) {
       
   249         if (namespace == null || namespace.equals(""))
       
   250             throw new IllegalArgumentException("Null or empty namespace");
       
   251         final String sourcePath =
       
   252                 ObjectNameRouter.normalizeNamespacePath(namespace,false,
       
   253                             true,false);
       
   254         try {
       
   255             // We could use ObjectName.valueOf here - but throwing an
       
   256             // IllegalArgumentException that contains just the supplied
       
   257             // namespace instead of the whole ObjectName seems preferable.
       
   258             return ObjectName.getInstance(sourcePath+
       
   259                     NAMESPACE_SEPARATOR+":"+
       
   260                     JMXNamespace.TYPE_ASSIGNMENT);
       
   261         } catch (MalformedObjectNameException x) {
       
   262             throw new IllegalArgumentException("Invalid namespace: " +
       
   263                                                namespace,x);
       
   264         }
       
   265     }
       
   266 
       
   267     /**
       
   268      * Returns an ObjectName pattern that can be used to query for all MBeans
       
   269      * contained in the given name space.
       
   270      * For instance, if {@code namespace="foo//bar"}, this method will
       
   271      * return {@code "foo//bar//*:*"}
       
   272      * @return an ObjectName pattern that selects all MBeans in the given
       
   273      *         name space.
       
   274      **/
       
   275     public static ObjectName getWildcardFor(String namespace) {
       
   276             return insertPath(namespace,ObjectName.WILDCARD);
       
   277     }
       
   278 
       
   279 
       
   280     /**
       
   281      * Returns an ObjectName that can be used to access an MBean
       
   282      * contained in the given name space.
       
   283      * For instance, if {@code path="foo//bar"}, and
       
   284      * {@code to="domain:type=Thing"} this method will
       
   285      * return {@code "foo//bar//domain:type=Thing"}
       
   286      * @return an ObjectName that can be used to invoke an MBean located in a
       
   287      *         sub name space.
       
   288      * @throws IllegalArgumentException if {@code path} ends with an
       
   289      * odd number of {@code /} characters.
       
   290      **/
       
   291     public static ObjectName insertPath(String path, ObjectName to) {
       
   292         if (path == null || to == null)
       
   293             throw new IllegalArgumentException("Null argument");
       
   294         checkTrailingSlashes(path);
       
   295         String prefix = path;
       
   296         if (!prefix.equals(""))
       
   297             prefix = ObjectNameRouter.normalizeNamespacePath(
       
   298                         prefix + NAMESPACE_SEPARATOR,false,false,false);
       
   299          return to.withDomain(
       
   300                     ObjectNameRouter.normalizeDomain(
       
   301                         prefix+to.getDomain(),false));
       
   302     }
       
   303 
       
   304     /**
       
   305      * Returns the normalized name space path of the name space expected to
       
   306      * contain {@code ObjectName}.
       
   307      * For instance, for {@code "foo//domain:type=Thing"} this will be
       
   308      * {@code "foo"}. For {@code "//foo//bar//domain:type=Thing"} this will be
       
   309      * {@code "foo//bar"}. For {@code //foo//bar//baz//domain:type=Thing}
       
   310      * this will be {@code "foo//bar//baz"}. For
       
   311      * {@code //foo//bar//baz//:type=JMXNamespace}
       
   312      * this will be {@code "foo//bar"}.
       
   313      *
       
   314      * @param name an {@code ObjectName}
       
   315      * @return the name space path of the name space that could contain such
       
   316      *         a name. If {@code name} has no name space, returns {@code ""}.
       
   317      * @throws IllegalArgumentException if {@code name} is null.
       
   318      **/
       
   319     public static String getContainingNamespace(ObjectName name) {
       
   320         return getNormalizedPath(name,true);
       
   321     }
       
   322 
       
   323 
       
   324     static String getNormalizedPath(ObjectName name,
       
   325             boolean removeLeadingSep) {
       
   326         if (name == null)
       
   327             throw new IllegalArgumentException("Null name");
       
   328         String domain =
       
   329                 ObjectNameRouter.normalizeDomain(name.getDomain(),removeLeadingSep);
       
   330         int end = domain.length();
       
   331 
       
   332         // special case of domain part being a single '/'
       
   333         //
       
   334         if (domain.endsWith(NAMESPACE_SEPARATOR+"/"))
       
   335             return domain.substring(0,end-NAMESPACE_SEPARATOR_LENGTH-1);
       
   336 
       
   337         // special case of namespace handler
       
   338         //
       
   339         if (domain.endsWith(NAMESPACE_SEPARATOR))
       
   340             domain = domain.substring(0,end-NAMESPACE_SEPARATOR_LENGTH);
       
   341 
       
   342         int last = domain.lastIndexOf(NAMESPACE_SEPARATOR);
       
   343         if (last < 0) return "";
       
   344         if (last == 0) return domain;
       
   345 
       
   346         // special case of domain part starting with '/'
       
   347         // last=0 is not possible - we took care of this above.
       
   348         if (domain.charAt(last-1) == '/') last--;
       
   349 
       
   350         return domain.substring(0,last);
       
   351     }
       
   352 
       
   353     private static void checkTrailingSlashes(String path) {
       
   354         int i;
       
   355         for (i = path.length() - 1; i >= 0 && path.charAt(i) == '/'; i--)
       
   356             continue;
       
   357         if (path.length() - i % 2 == 0)
       
   358             throw new IllegalArgumentException("Path ends with odd number of /");
       
   359     }
       
   360 }