JMXNamespaceView.java 10.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
/*
 * 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 javax.management.namespace;

import java.io.IOException;
import java.util.Set;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;

/**
 * This class makes it possible to navigate easily within a hierarchical
 * namespace view.
 *
 * <pre>
 * MBeanServerConnnection rootConnection = ...;
 *
 * // create a view at the local root of the namespace hierarchy.
 * //
 * JMXNamespaceView view = new JMXNamespaceView(rootConnection);
 *
 * // list all top level namespaces
 * String[] list = view.list();
 *
 * // select one namespace from the list
 * String whereToGo = ... ;
 *
 * // go down to the selected namespace:
 * view = view.down(whereToGo);
 * System.out.println("I am now in: " + view.where());
 * System.out.println("I can see these MBeans:" +
 *    view.getMBeanServerConnection().queryNames(null,null));
 *
 * // list sub namespaces in current view ('whereToGo')
 * list = view.list();
 * System.out.println("Here are the sub namespaces of "+view.where()+": "+
 *                    Arrays.toString(list));
 *
 * // go up one level
 * view = view.up();
 * System.out.println("I am now back to: " +
 *    (view.isRoot() ? "root namespace" : view.where()));
 * </pre>
 * @since 1.7
 */
public class JMXNamespaceView {

    private static final ObjectName ALL_NAMESPACES;
    static {
        try {
            ALL_NAMESPACES = ObjectName.getInstance("*" +
                    JMXNamespaces.NAMESPACE_SEPARATOR + ":"+
                    JMXNamespace.TYPE_ASSIGNMENT);
        } catch (MalformedObjectNameException x) {
            throw new ExceptionInInitializerError(x);
        }
    }
    private static final int NAMESPACE_SEPARATOR_LENGTH =
            JMXNamespaces.NAMESPACE_SEPARATOR.length();

    private final JMXNamespaceView parent;
    private final MBeanServerConnection here;
    private final String where;

    private static MBeanServerConnection checkRoot(MBeanServerConnection root) {
        if (root == null)
            throw new IllegalArgumentException(
                    "namespaceRoot: null is not a valid value");
        return root;
    }

    /**
     * Creates a view at the top of a JMX namespace hierarchy.
     * @param namespaceRoot The {@code MBeanServerConnection} at the
     *        top of the hierarchy.
     */
    public JMXNamespaceView(MBeanServerConnection namespaceRoot) {
        this(null,checkRoot(namespaceRoot),"");
    }

    // This constructor should remain private. A user can only create
    // JMXNamespaceView at the top of the hierarchy.
    // JMXNamespaceView sub nodes are created by their parent nodes.
    private JMXNamespaceView(JMXNamespaceView parent,
            MBeanServerConnection here, String where) {
        this.parent = parent;
        this.here   = here;
        this.where  = where;
    }

    /**
     * Returns the path leading to the namespace in this view, from
     * the top of the hierarchy.
     * @return The path to the namespace in this view.
     */
    public String where() {
        return where;
    }

    /**
     * Lists all direct sub namespaces in this view.  The returned strings
     * do not contain the {@code //} separator.
     *
     * @return A list of direct sub name spaces accessible from this
     *         namespace.
     * @throws IOException if the attempt to list the namespaces fails because
     * of a communication problem.
     */
    public String[] list() throws IOException {
        final Set<ObjectName> names =
                here.queryNames(ALL_NAMESPACES,null);
        final String[] res = new String[names.size()];
        int i = 0;
        for (ObjectName dirName : names) {
            final String dir = dirName.getDomain();
            res[i++]=dir.substring(0,dir.length()-NAMESPACE_SEPARATOR_LENGTH);
        }
        return res;
    }

    /**
     * Go down into a sub namespace.
     * @param namespace the namespace to go down to.  It can contain one or
     * more {@code //} separators, to traverse intermediate namespaces, but
     * it must not begin or end with {@code //} or contain an empty
     * intermediate namespace.  If it is the empty string, then {@code this} is
     * returned.
     * @return A view of the named sub namespace.
     * @throws IllegalArgumentException if the {@code namespace} begins or
     * ends with {@code //}.
     */
    public JMXNamespaceView down(String namespace) {
        if (namespace.equals("")) return this;
        if (namespace.startsWith(JMXNamespaces.NAMESPACE_SEPARATOR))
            throw new IllegalArgumentException(namespace+": can't start with "+
                    JMXNamespaces.NAMESPACE_SEPARATOR);

        // This is a convenience to handle paths like xxx//yyy
        final String[] elts =
                namespace.split(JMXNamespaces.NAMESPACE_SEPARATOR);

        // Go down the path, creating all sub namespaces along the way.
        // Usually there will be a single element in the given namespace
        // name, but we don't want to forbid things like
        // down("xxx//yyy/www");
        //
        JMXNamespaceView previous = this;
        String cursor = where;
        for (String elt : elts) {
            // empty path elements are not allowed. It means we
            // had something like "xxx////yyy"
            if (elt.equals(""))
                throw new IllegalArgumentException(namespace+
                        ": invalid path element");

            // compute the "where" for the child.
            cursor = JMXNamespaces.concat(cursor, elt);

            // create the child...
            final JMXNamespaceView next =
                    makeJMXNamespaceView(root(), previous, cursor);

            // the current child will be the parent of the next child...
            previous = next;
        }

        // We return the last child that was created.
        return previous;
    }

    /**
     * Go back up one level. If this view is at the root of the
     * hierarchy, returns {@code null}.
     * @return A view of the parent namespace, or {@code null} if we're at
     *         the root of the hierarchy.
     */
    public JMXNamespaceView up() {
        return parent;
    }

    /**
     * Tells whether this view is at the root of the hierarchy.
     * @return {@code true} if this view is at the root of the hierachy.
     */
    public boolean isRoot() {
        return parent == null;
    }

    /**
     * Returns the view at the root of the hierarchy.
     * If we are already at the root, this is {@code this}.
     * @return the view at the root of the hierarchy.
     */
    public JMXNamespaceView root() {
        if (parent == null) return this;
        return parent.root();
    }

    /**
     * A MBeanServerConnection to the namespace shown by this view.
     * This is what would have been obtained by doing:
     * <pre>
     *   JMX.narrowToNamespace(this.root().getMBeanServerConnection(),
     *       this.where());
     * </pre>
     * @return A MBeanServerConnection to the namespace shown by this view.
     */
    public MBeanServerConnection getMBeanServerConnection() {
        return here;
    }

    /**
     * <p>Get the name of the JMXNamespaceMBean handling the namespace shown by
     * this view, relative to the root of the hierarchy.  If we are at the root
     * of the hierarchy, this method returns {@code null}.</p>
     *
     * <p>You can use this method to make a proxy for the JMXNamespaceMBean
     * as follows:</p>
     *
     * <pre>
     * JMXNamespaceView view = ...;
     * ObjectName namespaceMBeanName = view.getJMXNamespaceMBeanName();
     * JMXNamespaceMBean namespaceMBean = JMX.newMBeanProxy(
     *     view.root().getMBeanServerConnection(), namespaceMBeanName,
     *     JMXNamespaceMBean.class);
     * </pre>
     *
     * @return The name of the {@code JMXNamespaceMBean} handling the namespace
     *         shown by this view, or {@code null}.
     */
    public ObjectName getJMXNamespaceMBeanName() {
        if (parent == null)
            return null;
        else
            return JMXNamespaces.getNamespaceObjectName(where);
    }

    @Override
    public int hashCode() {
        return where.hashCode();
    }

    /**
     * Returns true if this object is equal to the given object.  The
     * two objects are equal if the other object is also a {@code
     * JMXNamespaceView} and both objects have the same {@linkplain #root root}
     * MBeanServerConnection and the same {@linkplain #where path}.
     * @param o the other object to compare to.
     * @return true if both objects are equal.
     */
    @Override
    public boolean equals(Object o) {
        if (o==this) return true;
        if (! (o instanceof JMXNamespaceView)) return false;
        if (!where.equals(((JMXNamespaceView)o).where)) return false;
        return root().getMBeanServerConnection().equals(
                ((JMXNamespaceView)o).root().getMBeanServerConnection());
    }

    private JMXNamespaceView makeJMXNamespaceView(final JMXNamespaceView root,
            final JMXNamespaceView directParent, final String pathFromRoot) {
        if (pathFromRoot.equals("")) return root;

        return new JMXNamespaceView(directParent,
                narrowToNamespace(root.getMBeanServerConnection(),
                pathFromRoot),pathFromRoot);
    }

    private MBeanServerConnection narrowToNamespace(MBeanServerConnection root,
            String path) {
        if (root instanceof MBeanServer)
            return JMXNamespaces.narrowToNamespace((MBeanServer)root, path);
        return JMXNamespaces.narrowToNamespace(root, path);
    }

}