Showing the changes to java/pkg/sun/tools/jconsole/inspector/XTree.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,834 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2004, 2008, Oracle and/or its affiliates. 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. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package sun.tools.jconsole.inspector; | ||
| 27 | |||
| 28 | import java.io.IOException; | ||
| 29 | import java.util.*; | ||
| 30 | import javax.management.*; | ||
| 31 | import javax.swing.*; | ||
| 32 | import javax.swing.tree.*; | ||
| 33 | import sun.tools.jconsole.JConsole; | ||
| 34 | import sun.tools.jconsole.MBeansTab; | ||
| 35 | import sun.tools.jconsole.Resources; | ||
| 36 | import sun.tools.jconsole.inspector.XNodeInfo; | ||
| 37 | import static sun.tools.jconsole.inspector.XNodeInfo.Type; | ||
| 38 | |||
| 39 | @SuppressWarnings("serial") | ||
| 40 | public class XTree extends JTree { | ||
| 41 | |||
| 42 | private static final List<String> orderedKeyPropertyList = | ||
| 43 | new ArrayList<String>(); | ||
| 44 | |||
| 45 | static { | ||
| 46 | String keyPropertyList = | ||
| 47 | System.getProperty("com.sun.tools.jconsole.mbeans.keyPropertyList"); | ||
| 48 | if (keyPropertyList == null) { | ||
| 49 | orderedKeyPropertyList.add("type"); | ||
| 50 | orderedKeyPropertyList.add("j2eeType"); | ||
| 51 | } else { | ||
| 52 | StringTokenizer st = new StringTokenizer(keyPropertyList, ","); | ||
| 53 | while (st.hasMoreTokens()) { | ||
| 54 | orderedKeyPropertyList.add(st.nextToken()); | ||
| 55 | } | ||
| 56 | } | ||
| 57 | } | ||
| 58 | private MBeansTab mbeansTab; | ||
| 59 | private Map<String, DefaultMutableTreeNode> nodes = | ||
| 60 | new HashMap<String, DefaultMutableTreeNode>(); | ||
| 61 | |||
| 62 | public XTree(MBeansTab mbeansTab) { | ||
| 63 | this(new DefaultMutableTreeNode("MBeanTreeRootNode"), mbeansTab); | ||
| 64 | } | ||
| 65 | |||
| 66 | public XTree(TreeNode root, MBeansTab mbeansTab) { | ||
| 67 | super(root, true); | ||
| 68 | this.mbeansTab = mbeansTab; | ||
| 69 | setRootVisible(false); | ||
| 70 | setShowsRootHandles(true); | ||
| 71 | ToolTipManager.sharedInstance().registerComponent(this); | ||
| 72 | } | ||
| 73 | |||
| 74 | /** | ||
| 75 | * This method removes the node from its parent | ||
| 76 | */ | ||
| 77 | // Call on EDT | ||
| 78 | private synchronized void removeChildNode(DefaultMutableTreeNode child) { | ||
| 79 | DefaultTreeModel model = (DefaultTreeModel) getModel(); | ||
| 80 | model.removeNodeFromParent(child); | ||
| 81 | } | ||
| 82 | |||
| 83 | /** | ||
| 84 | * This method adds the child to the specified parent node | ||
| 85 | * at specific index. | ||
| 86 | */ | ||
| 87 | // Call on EDT | ||
| 88 | private synchronized void addChildNode( | ||
| 89 | DefaultMutableTreeNode parent, | ||
| 90 | DefaultMutableTreeNode child, | ||
| 91 | int index) { | ||
| 92 | DefaultTreeModel model = (DefaultTreeModel) getModel(); | ||
| 93 | model.insertNodeInto(child, parent, index); | ||
| 94 | } | ||
| 95 | |||
| 96 | /** | ||
| 97 | * This method adds the child to the specified parent node. | ||
| 98 | * The index where the child is to be added depends on the | ||
| 99 | * child node being Comparable or not. If the child node is | ||
| 100 | * not Comparable then it is added at the end, i.e. right | ||
| 101 | * after the current parent's children. | ||
| 102 | */ | ||
| 103 | // Call on EDT | ||
| 104 | private synchronized void addChildNode( | ||
| 105 | DefaultMutableTreeNode parent, DefaultMutableTreeNode child) { | ||
| 106 | int childCount = parent.getChildCount(); | ||
| 107 | if (childCount == 0) { | ||
| 108 | addChildNode(parent, child, 0); | ||
| 109 | return; | ||
| 110 | } | ||
| 111 | if (child instanceof ComparableDefaultMutableTreeNode) { | ||
| 112 | ComparableDefaultMutableTreeNode comparableChild = | ||
| 113 | (ComparableDefaultMutableTreeNode) child; | ||
| 114 | for (int i = childCount - 1; i >= 0; i--) { | ||
| 115 | DefaultMutableTreeNode brother = | ||
| 116 | (DefaultMutableTreeNode) parent.getChildAt(i); | ||
| 117 | // expr1: child node must be inserted after metadata nodes | ||
| 118 | // - OR - | ||
| 119 | // expr2: "child >= brother" | ||
| 120 | if ((i <= 2 && isMetadataNode(brother)) || | ||
| 121 | comparableChild.compareTo(brother) >= 0) { | ||
| 122 | addChildNode(parent, child, i + 1); | ||
| 123 | return; | ||
| 124 | } | ||
| 125 | } | ||
| 126 | // "child < all brothers", add at the beginning | ||
| 127 | addChildNode(parent, child, 0); | ||
| 128 | return; | ||
| 129 | } | ||
| 130 | // "child not comparable", add at the end | ||
| 131 | addChildNode(parent, child, childCount); | ||
| 132 | } | ||
| 133 | |||
| 134 | /** | ||
| 135 | * This method removes all the displayed nodes from the tree, | ||
| 136 | * but does not affect actual MBeanServer contents. | ||
| 137 | */ | ||
| 138 | // Call on EDT | ||
| 139 | @Override | ||
| 140 | public synchronized void removeAll() { | ||
| 141 | DefaultTreeModel model = (DefaultTreeModel) getModel(); | ||
| 142 | DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot(); | ||
| 143 | root.removeAllChildren(); | ||
| 144 | model.nodeStructureChanged(root); | ||
| 145 | nodes.clear(); | ||
| 146 | } | ||
| 147 | |||
| 148 | // Call on EDT | ||
| 149 | public synchronized void removeMBeanFromView(ObjectName mbean) { | ||
| 150 | // We assume here that MBeans are removed one by one (on MBean | ||
| 151 | // unregistered notification). Deletes the tree node associated | ||
| 152 | // with the given MBean and recursively all the node parents | ||
| 153 | // which are leaves and non XMBean. | ||
| 154 | // | ||
| 155 | DefaultMutableTreeNode node = null; | ||
| 156 | Dn dn = new Dn(mbean); | ||
| 157 | if (dn.getTokenCount() > 0) { | ||
| 158 | DefaultTreeModel model = (DefaultTreeModel) getModel(); | ||
| 159 | Token token = dn.getToken(0); | ||
| 160 | String hashKey = dn.getHashKey(token); | ||
| 161 | node = nodes.get(hashKey); | ||
| 162 | if ((node != null) && (!node.isRoot())) { | ||
| 163 | if (hasNonMetadataNodes(node)) { | ||
| 164 | removeMetadataNodes(node); | ||
| 165 | String label = token.getValue(); | ||
| 166 | XNodeInfo userObject = new XNodeInfo( | ||
| 167 | Type.NONMBEAN, label, | ||
| 168 | label, token.getTokenValue()); | ||
| 169 | changeNodeValue(node, userObject); | ||
| 170 | } else { | ||
| 171 | DefaultMutableTreeNode parent = | ||
| 172 | (DefaultMutableTreeNode) node.getParent(); | ||
| 173 | model.removeNodeFromParent(node); | ||
| 174 | nodes.remove(hashKey); | ||
| 175 | removeParentFromView(dn, 1, parent); | ||
| 176 | } | ||
| 177 | } | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | /** | ||
| 182 | * Returns true if any of the children nodes is a non MBean metadata node. | ||
| 183 | */ | ||
| 184 | private boolean hasNonMetadataNodes(DefaultMutableTreeNode node) { | ||
| 185 | for (Enumeration e = node.children(); e.hasMoreElements();) { | ||
| 186 | DefaultMutableTreeNode n = (DefaultMutableTreeNode) e.nextElement(); | ||
| 187 | Object uo = n.getUserObject(); | ||
| 188 | if (uo instanceof XNodeInfo) { | ||
| 189 | switch (((XNodeInfo) uo).getType()) { | ||
| 190 | case ATTRIBUTES: | ||
| 191 | case NOTIFICATIONS: | ||
| 192 | case OPERATIONS: | ||
| 193 | break; | ||
| 194 | default: | ||
| 195 | return true; | ||
| 196 | } | ||
| 197 | } else { | ||
| 198 | return true; | ||
| 199 | } | ||
| 200 | } | ||
| 201 | return false; | ||
| 202 | } | ||
| 203 | |||
| 204 | /** | ||
| 205 | * Returns true if any of the children nodes is an MBean metadata node. | ||
| 206 | */ | ||
| 207 | public boolean hasMetadataNodes(DefaultMutableTreeNode node) { | ||
| 208 | for (Enumeration e = node.children(); e.hasMoreElements();) { | ||
| 209 | DefaultMutableTreeNode n = (DefaultMutableTreeNode) e.nextElement(); | ||
| 210 | Object uo = n.getUserObject(); | ||
| 211 | if (uo instanceof XNodeInfo) { | ||
| 212 | switch (((XNodeInfo) uo).getType()) { | ||
| 213 | case ATTRIBUTES: | ||
| 214 | case NOTIFICATIONS: | ||
| 215 | case OPERATIONS: | ||
| 216 | return true; | ||
| 217 | default: | ||
| 218 | break; | ||
| 219 | } | ||
| 220 | } else { | ||
| 221 | return false; | ||
| 222 | } | ||
| 223 | } | ||
| 224 | return false; | ||
| 225 | } | ||
| 226 | |||
| 227 | /** | ||
| 228 | * Returns true if the given node is an MBean metadata node. | ||
| 229 | */ | ||
| 230 | public boolean isMetadataNode(DefaultMutableTreeNode node) { | ||
| 231 | Object uo = node.getUserObject(); | ||
| 232 | if (uo instanceof XNodeInfo) { | ||
| 233 | switch (((XNodeInfo) uo).getType()) { | ||
| 234 | case ATTRIBUTES: | ||
| 235 | case NOTIFICATIONS: | ||
| 236 | case OPERATIONS: | ||
| 237 | return true; | ||
| 238 | default: | ||
| 239 | return false; | ||
| 240 | } | ||
| 241 | } else { | ||
| 242 | return false; | ||
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 246 | /** | ||
| 247 | * Remove the metadata nodes associated with a given MBean node. | ||
| 248 | */ | ||
| 249 | // Call on EDT | ||
| 250 | private void removeMetadataNodes(DefaultMutableTreeNode node) { | ||
| 251 | Set<DefaultMutableTreeNode> metadataNodes = | ||
| 252 | new HashSet<DefaultMutableTreeNode>(); | ||
| 253 | DefaultTreeModel model = (DefaultTreeModel) getModel(); | ||
| 254 | for (Enumeration e = node.children(); e.hasMoreElements();) { | ||
| 255 | DefaultMutableTreeNode n = (DefaultMutableTreeNode) e.nextElement(); | ||
| 256 | Object uo = n.getUserObject(); | ||
| 257 | if (uo instanceof XNodeInfo) { | ||
| 258 | switch (((XNodeInfo) uo).getType()) { | ||
| 259 | case ATTRIBUTES: | ||
| 260 | case NOTIFICATIONS: | ||
| 261 | case OPERATIONS: | ||
| 262 | metadataNodes.add(n); | ||
| 263 | break; | ||
| 264 | default: | ||
| 265 | break; | ||
| 266 | } | ||
| 267 | } | ||
| 268 | } | ||
| 269 | for (DefaultMutableTreeNode n : metadataNodes) { | ||
| 270 | model.removeNodeFromParent(n); | ||
| 271 | } | ||
| 272 | } | ||
| 273 | |||
| 274 | /** | ||
| 275 | * Removes only the parent nodes which are non MBean and leaf. | ||
| 276 | * This method assumes the child nodes have been removed before. | ||
| 277 | */ | ||
| 278 | // Call on EDT | ||
| 279 | private DefaultMutableTreeNode removeParentFromView( | ||
| 280 | Dn dn, int index, DefaultMutableTreeNode node) { | ||
| 281 | if ((!node.isRoot()) && node.isLeaf() && | ||
| 282 | (!(((XNodeInfo) node.getUserObject()).getType().equals(Type.MBEAN)))) { | ||
| 283 | DefaultMutableTreeNode parent = | ||
| 284 | (DefaultMutableTreeNode) node.getParent(); | ||
| 285 | removeChildNode(node); | ||
| 286 | String hashKey = dn.getHashKey(dn.getToken(index)); | ||
| 287 | nodes.remove(hashKey); | ||
| 288 | removeParentFromView(dn, index + 1, parent); | ||
| 289 | } | ||
| 290 | return node; | ||
| 291 | } | ||
| 292 | |||
| 293 | // Call on EDT | ||
| 294 | public synchronized void addMBeansToView(Set<ObjectName> mbeans) { | ||
| 295 | Set<Dn> dns = new TreeSet<Dn>(); | ||
| 296 | for (ObjectName mbean : mbeans) { | ||
| 297 | Dn dn = new Dn(mbean); | ||
| 298 | dns.add(dn); | ||
| 299 | } | ||
| 300 | for (Dn dn : dns) { | ||
| 301 | ObjectName mbean = dn.getObjectName(); | ||
| 302 | XMBean xmbean = new XMBean(mbean, mbeansTab); | ||
| 303 | addMBeanToView(mbean, xmbean, dn); | ||
| 304 | } | ||
| 305 | } | ||
| 306 | |||
| 307 | // Call on EDT | ||
| 308 | public synchronized void addMBeanToView(ObjectName mbean) { | ||
| 309 | // Build XMBean for the given MBean | ||
| 310 | // | ||
| 311 | XMBean xmbean = new XMBean(mbean, mbeansTab); | ||
| 312 | // Build Dn for the given MBean | ||
| 313 | // | ||
| 314 | Dn dn = new Dn(mbean); | ||
| 315 | // Add the new nodes to the MBean tree from leaf to root | ||
| 316 | // | ||
| 317 | addMBeanToView(mbean, xmbean, dn); | ||
| 318 | } | ||
| 319 | |||
| 320 | // Call on EDT | ||
| 321 | private synchronized void addMBeanToView( | ||
| 322 | ObjectName mbean, XMBean xmbean, Dn dn) { | ||
| 323 | |||
| 324 | DefaultMutableTreeNode childNode = null; | ||
| 325 | DefaultMutableTreeNode parentNode = null; | ||
| 326 | |||
| 327 | // Add the node or replace its user object if already added | ||
| 328 | // | ||
| 329 | Token token = dn.getToken(0); | ||
| 330 | String hashKey = dn.getHashKey(token); | ||
| 331 | if (nodes.containsKey(hashKey)) { | ||
| 332 | // Found existing node previously created when adding another node | ||
| 333 | // | ||
| 334 | childNode = nodes.get(hashKey); | ||
| 335 | // Replace user object to reflect that this node is an MBean | ||
| 336 | // | ||
| 337 | Object data = createNodeValue(xmbean, token); | ||
| 338 | String label = data.toString(); | ||
| 339 | XNodeInfo userObject = | ||
| 340 | new XNodeInfo(Type.MBEAN, data, label, mbean.toString()); | ||
| 341 | changeNodeValue(childNode, userObject); | ||
| 342 | return; | ||
| 343 | } | ||
| 344 | |||
| 345 | // Create new leaf node | ||
| 346 | // | ||
| 347 | childNode = createDnNode(dn, token, xmbean); | ||
| 348 | nodes.put(hashKey, childNode); | ||
| 349 | |||
| 350 | // Add intermediate non MBean nodes | ||
| 351 | // | ||
| 352 | for (int i = 1; i < dn.getTokenCount(); i++) { | ||
| 353 | token = dn.getToken(i); | ||
| 354 | hashKey = dn.getHashKey(token); | ||
| 355 | if (nodes.containsKey(hashKey)) { | ||
| 356 | // Intermediate node already present, add new node as child | ||
| 357 | // | ||
| 358 | parentNode = nodes.get(hashKey); | ||
| 359 | addChildNode(parentNode, childNode); | ||
| 360 | return; | ||
| 361 | } else { | ||
| 362 | // Create new intermediate node | ||
| 363 | // | ||
| 364 | if ("domain".equals(token.getTokenType())) { | ||
| 365 | parentNode = createDomainNode(dn, token); | ||
| 366 | DefaultMutableTreeNode root = | ||
| 367 | (DefaultMutableTreeNode) getModel().getRoot(); | ||
| 368 | addChildNode(root, parentNode); | ||
| 369 | } else { | ||
| 370 | parentNode = createSubDnNode(dn, token); | ||
| 371 | } | ||
| 372 | nodes.put(hashKey, parentNode); | ||
| 373 | addChildNode(parentNode, childNode); | ||
| 374 | } | ||
| 375 | childNode = parentNode; | ||
| 376 | } | ||
| 377 | } | ||
| 378 | |||
| 379 | // Call on EDT | ||
| 380 | private synchronized void changeNodeValue( | ||
| 381 | DefaultMutableTreeNode node, XNodeInfo nodeValue) { | ||
| 382 | if (node instanceof ComparableDefaultMutableTreeNode) { | ||
| 383 | // should it stay at the same place? | ||
| 384 | DefaultMutableTreeNode clone = | ||
| 385 | (DefaultMutableTreeNode) node.clone(); | ||
| 386 | clone.setUserObject(nodeValue); | ||
| 387 | if (((ComparableDefaultMutableTreeNode) node).compareTo(clone) == 0) { | ||
| 388 | // the order in the tree didn't change | ||
| 389 | node.setUserObject(nodeValue); | ||
| 390 | DefaultTreeModel model = (DefaultTreeModel) getModel(); | ||
| 391 | model.nodeChanged(node); | ||
| 392 | } else { | ||
| 393 | // delete the node and re-order it in case the | ||
| 394 | // node value modifies the order in the tree | ||
| 395 | DefaultMutableTreeNode parent = | ||
| 396 | (DefaultMutableTreeNode) node.getParent(); | ||
| 397 | removeChildNode(node); | ||
| 398 | node.setUserObject(nodeValue); | ||
| 399 | addChildNode(parent, node); | ||
| 400 | } | ||
| 401 | } else { | ||
| 402 | // not comparable stays at the same place | ||
| 403 | node.setUserObject(nodeValue); | ||
| 404 | DefaultTreeModel model = (DefaultTreeModel) getModel(); | ||
| 405 | model.nodeChanged(node); | ||
| 406 | } | ||
| 407 | // Load the MBean metadata if type is MBEAN | ||
| 408 | if (nodeValue.getType().equals(Type.MBEAN)) { | ||
| 409 | removeMetadataNodes(node); | ||
| 410 | TreeNode[] treeNodes = node.getPath(); | ||
| 411 | TreePath path = new TreePath(treeNodes); | ||
| 412 | if (isExpanded(path)) { | ||
| 413 | addMetadataNodes(node); | ||
| 414 | } | ||
| 415 | } | ||
| 416 | // Clear the current selection and set it | ||
| 417 | // again so valueChanged() gets called | ||
| 418 | if (node == getLastSelectedPathComponent()) { | ||
| 419 | TreePath selectionPath = getSelectionPath(); | ||
| 420 | clearSelection(); | ||
| 421 | setSelectionPath(selectionPath); | ||
| 422 | } | ||
| 423 | } | ||
| 424 | |||
| 425 | /** | ||
| 426 | * Creates the domain node. | ||
| 427 | */ | ||
| 428 | private DefaultMutableTreeNode createDomainNode(Dn dn, Token token) { | ||
| 429 | DefaultMutableTreeNode node = new ComparableDefaultMutableTreeNode(); | ||
| 430 | String label = dn.getDomain(); | ||
| 431 | XNodeInfo userObject = | ||
| 432 | new XNodeInfo(Type.NONMBEAN, label, label, label); | ||
| 433 | node.setUserObject(userObject); | ||
| 434 | return node; | ||
| 435 | } | ||
| 436 | |||
| 437 | /** | ||
| 438 | * Creates the node corresponding to the whole Dn, i.e. an MBean. | ||
| 439 | */ | ||
| 440 | private DefaultMutableTreeNode createDnNode( | ||
| 441 | Dn dn, Token token, XMBean xmbean) { | ||
| 442 | DefaultMutableTreeNode node = new ComparableDefaultMutableTreeNode(); | ||
| 443 | Object data = createNodeValue(xmbean, token); | ||
| 444 | String label = data.toString(); | ||
| 445 | XNodeInfo userObject = new XNodeInfo(Type.MBEAN, data, label, | ||
| 446 | xmbean.getObjectName().toString()); | ||
| 447 | node.setUserObject(userObject); | ||
| 448 | return node; | ||
| 449 | } | ||
| 450 | |||
| 451 | /** | ||
| 452 | * Creates the node corresponding to a subDn, i.e. a non-MBean | ||
| 453 | * intermediate node. | ||
| 454 | */ | ||
| 455 | private DefaultMutableTreeNode createSubDnNode(Dn dn, Token token) { | ||
| 456 | DefaultMutableTreeNode node = new ComparableDefaultMutableTreeNode(); | ||
| 457 | String label = isKeyValueView() ? token.getTokenValue() : token.getValue(); | ||
| 458 | XNodeInfo userObject = | ||
| 459 | new XNodeInfo(Type.NONMBEAN, label, label, token.getTokenValue()); | ||
| 460 | node.setUserObject(userObject); | ||
| 461 | return node; | ||
| 462 | } | ||
| 463 | |||
| 464 | private Object createNodeValue(XMBean xmbean, Token token) { | ||
| 465 | String label = isKeyValueView() ? token.getTokenValue() : token.getValue(); | ||
| 466 | xmbean.setText(label); | ||
| 467 | return xmbean; | ||
| 468 | } | ||
| 469 | |||
| 470 | /** | ||
| 471 | * Parses the MBean ObjectName comma-separated properties string and puts | ||
| 472 | * the individual key/value pairs into the map. Key order in the properties | ||
| 473 | * string is preserved by the map. | ||
| 474 | */ | ||
| 475 | private static Map<String, String> extractKeyValuePairs( | ||
| 476 | String props, ObjectName mbean) { | ||
| 477 | Map<String, String> map = new LinkedHashMap<String, String>(); | ||
| 478 | int eq = props.indexOf("="); | ||
| 479 | while (eq != -1) { | ||
| 480 | String key = props.substring(0, eq); | ||
| 481 | String value = mbean.getKeyProperty(key); | ||
| 482 | map.put(key, value); | ||
| 483 | props = props.substring(key.length() + 1 + value.length()); | ||
| 484 | if (props.startsWith(",")) { | ||
| 485 | props = props.substring(1); | ||
| 486 | } | ||
| 487 | eq = props.indexOf("="); | ||
| 488 | } | ||
| 489 | return map; | ||
| 490 | } | ||
| 491 | |||
| 492 | /** | ||
| 493 | * Returns the ordered key property list that will be used to build the | ||
| 494 | * MBean tree. If the "com.sun.tools.jconsole.mbeans.keyPropertyList" system | ||
| 495 | * property is not specified, then the ordered key property list used | ||
| 496 | * to build the MBean tree will be the one returned by the method | ||
| 497 | * ObjectName.getKeyPropertyListString() with "type" as first key, | ||
| 498 | * and "j2eeType" as second key, if present. If any of the keys specified | ||
| 499 | * in the comma-separated key property list does not apply to the given | ||
| 500 | * MBean then it will be discarded. | ||
| 501 | */ | ||
| 502 | private static String getKeyPropertyListString(ObjectName mbean) { | ||
| 503 | String props = mbean.getKeyPropertyListString(); | ||
| 504 | Map<String, String> map = extractKeyValuePairs(props, mbean); | ||
| 505 | StringBuilder sb = new StringBuilder(); | ||
| 506 | // Add the key/value pairs to the buffer following the | ||
| 507 | // key order defined by the "orderedKeyPropertyList" | ||
| 508 | for (String key : orderedKeyPropertyList) { | ||
| 509 | if (map.containsKey(key)) { | ||
| 510 | sb.append(key + "=" + map.get(key) + ","); | ||
| 511 | map.remove(key); | ||
| 512 | } | ||
| 513 | } | ||
| 514 | // Add the remaining key/value pairs to the buffer | ||
| 515 | for (Map.Entry<String, String> entry : map.entrySet()) { | ||
| 516 | sb.append(entry.getKey() + "=" + entry.getValue() + ","); | ||
| 517 | } | ||
| 518 | String orderedKeyPropertyListString = sb.toString(); | ||
| 519 | orderedKeyPropertyListString = orderedKeyPropertyListString.substring( | ||
| 520 | 0, orderedKeyPropertyListString.length() - 1); | ||
| 521 | return orderedKeyPropertyListString; | ||
| 522 | } | ||
| 523 | |||
| 524 | // Call on EDT | ||
| 525 | public void addMetadataNodes(DefaultMutableTreeNode node) { | ||
| 526 | XMBean mbean = (XMBean) ((XNodeInfo) node.getUserObject()).getData(); | ||
| 527 | DefaultTreeModel model = (DefaultTreeModel) getModel(); | ||
| 528 | MBeanInfoNodesSwingWorker sw = | ||
| 529 | new MBeanInfoNodesSwingWorker(model, node, mbean); | ||
| 530 | if (sw != null) { | ||
| 531 | sw.execute(); | ||
| 532 | } | ||
| 533 | } | ||
| 534 | |||
| 535 | private static class MBeanInfoNodesSwingWorker | ||
| 536 | extends SwingWorker<Object[], Void> { | ||
| 537 | |||
| 538 | private final DefaultTreeModel model; | ||
| 539 | private final DefaultMutableTreeNode node; | ||
| 540 | private final XMBean mbean; | ||
| 541 | |||
| 542 | public MBeanInfoNodesSwingWorker( | ||
| 543 | DefaultTreeModel model, | ||
| 544 | DefaultMutableTreeNode node, | ||
| 545 | XMBean mbean) { | ||
| 546 | this.model = model; | ||
| 547 | this.node = node; | ||
| 548 | this.mbean = mbean; | ||
| 549 | } | ||
| 550 | |||
| 551 | @Override | ||
| 552 | public Object[] doInBackground() throws InstanceNotFoundException, | ||
| 553 | IntrospectionException, ReflectionException, IOException { | ||
| 554 | Object result[] = new Object[2]; | ||
| 555 | // Retrieve MBeanInfo for this MBean | ||
| 556 | result[0] = mbean.getMBeanInfo(); | ||
| 557 | // Check if this MBean is a notification emitter | ||
| 558 | result[1] = mbean.isBroadcaster(); | ||
| 559 | return result; | ||
| 560 | } | ||
| 561 | |||
| 562 | @Override | ||
| 563 | protected void done() { | ||
| 564 | try { | ||
| 565 | Object result[] = get(); | ||
| 566 | MBeanInfo mbeanInfo = (MBeanInfo) result[0]; | ||
| 567 | Boolean isBroadcaster = (Boolean) result[1]; | ||
| 568 | if (mbeanInfo != null) { | ||
| 569 | addMBeanInfoNodes(model, node, mbean, mbeanInfo, isBroadcaster); | ||
| 570 | } | ||
| 571 | } catch (Exception e) { | ||
| 572 | Throwable t = Utils.getActualException(e); | ||
| 573 | if (JConsole.isDebug()) { | ||
| 574 | t.printStackTrace(); | ||
| 575 | } | ||
| 576 | } | ||
| 577 | } | ||
| 578 | |||
| 579 | // Call on EDT | ||
| 580 | private void addMBeanInfoNodes( | ||
| 581 | DefaultTreeModel tree, DefaultMutableTreeNode node, | ||
| 582 | XMBean mbean, MBeanInfo mbeanInfo, Boolean isBroadcaster) { | ||
| 583 | MBeanAttributeInfo[] ai = mbeanInfo.getAttributes(); | ||
| 584 | MBeanOperationInfo[] oi = mbeanInfo.getOperations(); | ||
| 585 | MBeanNotificationInfo[] ni = mbeanInfo.getNotifications(); | ||
| 586 | |||
| 587 | // Insert the Attributes/Operations/Notifications metadata nodes as | ||
| 588 | // the three first children of this MBean node. This is only useful | ||
| 589 | // when this MBean node denotes an MBean but it's not a leaf in the | ||
| 590 | // MBean tree | ||
| 591 | // | ||
| 592 | int childIndex = 0; | ||
| 593 | |||
| 594 | // MBeanAttributeInfo node | ||
| 595 | // | ||
| 596 | if (ai != null && ai.length > 0) { | ||
| 597 | DefaultMutableTreeNode attributes = new DefaultMutableTreeNode(); | ||
| 598 | XNodeInfo attributesUO = new XNodeInfo(Type.ATTRIBUTES, mbean, | ||
| 599 | Resources.getText("Attributes"), null); | ||
| 600 | attributes.setUserObject(attributesUO); | ||
| 601 | node.insert(attributes, childIndex++); | ||
| 602 | for (MBeanAttributeInfo mbai : ai) { | ||
| 603 | DefaultMutableTreeNode attribute = new DefaultMutableTreeNode(); | ||
| 604 | XNodeInfo attributeUO = new XNodeInfo(Type.ATTRIBUTE, | ||
| 605 | new Object[]{mbean, mbai}, mbai.getName(), null); | ||
| 606 | attribute.setUserObject(attributeUO); | ||
| 607 | attribute.setAllowsChildren(false); | ||
| 608 | attributes.add(attribute); | ||
| 609 | } | ||
| 610 | } | ||
| 611 | // MBeanOperationInfo node | ||
| 612 | // | ||
| 613 | if (oi != null && oi.length > 0) { | ||
| 614 | DefaultMutableTreeNode operations = new DefaultMutableTreeNode(); | ||
| 615 | XNodeInfo operationsUO = new XNodeInfo(Type.OPERATIONS, mbean, | ||
| 616 | Resources.getText("Operations"), null); | ||
| 617 | operations.setUserObject(operationsUO); | ||
| 618 | node.insert(operations, childIndex++); | ||
| 619 | for (MBeanOperationInfo mboi : oi) { | ||
| 620 | // Compute the operation's tool tip text: | ||
| 621 | // "operationname(param1type,param2type,...)" | ||
| 622 | // | ||
| 623 | StringBuilder sb = new StringBuilder(); | ||
| 624 | for (MBeanParameterInfo mbpi : mboi.getSignature()) { | ||
| 625 | sb.append(mbpi.getType() + ","); | ||
| 626 | } | ||
| 627 | String signature = sb.toString(); | ||
| 628 | if (signature.length() > 0) { | ||
| 629 | // Remove the trailing ',' | ||
| 630 | // | ||
| 631 | signature = signature.substring(0, signature.length() - 1); | ||
| 632 | } | ||
| 633 | String toolTipText = mboi.getName() + "(" + signature + ")"; | ||
| 634 | // Create operation node | ||
| 635 | // | ||
| 636 | DefaultMutableTreeNode operation = new DefaultMutableTreeNode(); | ||
| 637 | XNodeInfo operationUO = new XNodeInfo(Type.OPERATION, | ||
| 638 | new Object[]{mbean, mboi}, mboi.getName(), toolTipText); | ||
| 639 | operation.setUserObject(operationUO); | ||
| 640 | operation.setAllowsChildren(false); | ||
| 641 | operations.add(operation); | ||
| 642 | } | ||
| 643 | } | ||
| 644 | // MBeanNotificationInfo node | ||
| 645 | // | ||
| 646 | if (isBroadcaster != null && isBroadcaster.booleanValue()) { | ||
| 647 | DefaultMutableTreeNode notifications = new DefaultMutableTreeNode(); | ||
| 648 | XNodeInfo notificationsUO = new XNodeInfo(Type.NOTIFICATIONS, mbean, | ||
| 649 | Resources.getText("Notifications"), null); | ||
| 650 | notifications.setUserObject(notificationsUO); | ||
| 651 | node.insert(notifications, childIndex++); | ||
| 652 | if (ni != null && ni.length > 0) { | ||
| 653 | for (MBeanNotificationInfo mbni : ni) { | ||
| 654 | DefaultMutableTreeNode notification = | ||
| 655 | new DefaultMutableTreeNode(); | ||
| 656 | XNodeInfo notificationUO = new XNodeInfo(Type.NOTIFICATION, | ||
| 657 | mbni, mbni.getName(), null); | ||
| 658 | notification.setUserObject(notificationUO); | ||
| 659 | notification.setAllowsChildren(false); | ||
| 660 | notifications.add(notification); | ||
| 661 | } | ||
| 662 | } | ||
| 663 | } | ||
| 664 | // Update tree model | ||
| 665 | // | ||
| 666 | model.reload(node); | ||
| 667 | } | ||
| 668 | } | ||
| 669 | // | ||
| 670 | // Tree preferences | ||
| 671 | // | ||
| 672 | private static boolean treeView; | ||
| 673 | private static boolean treeViewInit = false; | ||
| 674 | |||
| 675 | private static boolean isTreeView() { | ||
| 676 | if (!treeViewInit) { | ||
| 677 | treeView = getTreeViewValue(); | ||
| 678 | treeViewInit = true; | ||
| 679 | } | ||
| 680 | return treeView; | ||
| 681 | } | ||
| 682 | |||
| 683 | private static boolean getTreeViewValue() { | ||
| 684 | String tv = System.getProperty("treeView"); | ||
| 685 | return ((tv == null) ? true : !(tv.equals("false"))); | ||
| 686 | } | ||
| 687 | // | ||
| 688 | // MBean key-value preferences | ||
| 689 | // | ||
| 690 | private boolean keyValueView = Boolean.getBoolean("keyValueView"); | ||
| 691 | |||
| 692 | private boolean isKeyValueView() { | ||
| 693 | return keyValueView; | ||
| 694 | } | ||
| 695 | |||
| 696 | // | ||
| 697 | // Utility classes | ||
| 698 | // | ||
| 699 | private static class ComparableDefaultMutableTreeNode | ||
| 700 | extends DefaultMutableTreeNode | ||
| 701 | implements Comparable<DefaultMutableTreeNode> { | ||
| 702 | |||
| 703 | public int compareTo(DefaultMutableTreeNode node) { | ||
| 704 | return (this.toString().compareTo(node.toString())); | ||
| 705 | } | ||
| 706 | } | ||
| 707 | |||
| 708 | private static class Dn implements Comparable<Dn> { | ||
| 709 | |||
| 710 | private ObjectName mbean; | ||
| 711 | private String domain; | ||
| 712 | private String keyPropertyList; | ||
| 713 | private String hashDn; | ||
| 714 | private List<Token> tokens = new ArrayList<Token>(); | ||
| 715 | |||
| 716 | public Dn(ObjectName mbean) { | ||
| 717 | this.mbean = mbean; | ||
| 718 | this.domain = mbean.getDomain(); | ||
| 719 | this.keyPropertyList = getKeyPropertyListString(mbean); | ||
| 720 | |||
| 721 | if (isTreeView()) { | ||
| 722 | // Tree view | ||
| 723 | Map<String, String> map = | ||
| 724 | extractKeyValuePairs(keyPropertyList, mbean); | ||
| 725 | for (Map.Entry<String, String> entry : map.entrySet()) { | ||
| 726 | tokens.add(new Token("key", entry.getKey() + "=" + entry.getValue())); | ||
| 727 | } | ||
| 728 | } else { | ||
| 729 | // Flat view | ||
| 730 | tokens.add(new Token("key", "properties=" + keyPropertyList)); | ||
| 731 | } | ||
| 732 | |||
| 733 | // Add the domain as the first token in the Dn | ||
| 734 | tokens.add(0, new Token("domain", "domain=" + domain)); | ||
| 735 | |||
| 736 | // Reverse the Dn (from leaf to root) | ||
| 737 | Collections.reverse(tokens); | ||
| 738 | |||
| 739 | // Compute hash for Dn | ||
| 740 | computeHashDn(); | ||
| 741 | } | ||
| 742 | |||
| 743 | public ObjectName getObjectName() { | ||
| 744 | return mbean; | ||
| 745 | } | ||
| 746 | |||
| 747 | public String getDomain() { | ||
| 748 | return domain; | ||
| 749 | } | ||
| 750 | |||
| 751 | public String getKeyPropertyList() { | ||
| 752 | return keyPropertyList; | ||
| 753 | } | ||
| 754 | |||
| 755 | public Token getToken(int index) { | ||
| 756 | return tokens.get(index); | ||
| 757 | } | ||
| 758 | |||
| 759 | public int getTokenCount() { | ||
| 760 | return tokens.size(); | ||
| 761 | } | ||
| 762 | |||
| 763 | public String getHashDn() { | ||
| 764 | return hashDn; | ||
| 765 | } | ||
| 766 | |||
| 767 | public String getHashKey(Token token) { | ||
| 768 | final int begin = hashDn.indexOf(token.getTokenValue()); | ||
| 769 | return hashDn.substring(begin, hashDn.length()); | ||
| 770 | } | ||
| 771 | |||
| 772 | private void computeHashDn() { | ||
| 773 | if (tokens.isEmpty()) { | ||
| 774 | return; | ||
| 775 | } | ||
| 776 | final StringBuilder hdn = new StringBuilder(); | ||
| 777 | for (int i = 0; i < tokens.size(); i++) { | ||
| 778 | hdn.append(tokens.get(i).getTokenValue()); | ||
| 779 | hdn.append(","); | ||
| 780 | } | ||
| 781 | hashDn = hdn.substring(0, hdn.length() - 1); | ||
| 782 | } | ||
| 783 | |||
| 784 | @Override | ||
| 785 | public String toString() { | ||
| 786 | return domain + ":" + keyPropertyList; | ||
| 787 | } | ||
| 788 | |||
| 789 | public int compareTo(Dn dn) { | ||
| 790 | return this.toString().compareTo(dn.toString()); | ||
| 791 | } | ||
| 792 | } | ||
| 793 | |||
| 794 | private static class Token { | ||
| 795 | |||
| 796 | private String tokenType; | ||
| 797 | private String tokenValue; | ||
| 798 | private String key; | ||
| 799 | private String value; | ||
| 800 | |||
| 801 | public Token(String tokenType, String tokenValue) { | ||
| 802 | this.tokenType = tokenType; | ||
| 803 | this.tokenValue = tokenValue; | ||
| 804 | buildKeyValue(); | ||
| 805 | } | ||
| 806 | |||
| 807 | public String getTokenType() { | ||
| 808 | return tokenType; | ||
| 809 | } | ||
| 810 | |||
| 811 | public String getTokenValue() { | ||
| 812 | return tokenValue; | ||
| 813 | } | ||
| 814 | |||
| 815 | public String getKey() { | ||
| 816 | return key; | ||
| 817 | } | ||
| 818 | |||
| 819 | public String getValue() { | ||
| 820 | return value; | ||
| 821 | } | ||
| 822 | |||
| 823 | private void buildKeyValue() { | ||
| 824 | int index = tokenValue.indexOf("="); | ||
| 825 | if (index < 0) { | ||
| 826 | key = tokenValue; | ||
| 827 | value = tokenValue; | ||
| 828 | } else { | ||
| 829 | key = tokenValue.substring(0, index); | ||
| 830 | value = tokenValue.substring(index + 1, tokenValue.length()); | ||
| 831 | } | ||
| 832 | } | ||
| 833 | } | ||
| 834 | } | ||