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.inspector.XNodeInfo; 36 import sun.tools.jconsole.resources.Messages; 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 Messages.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 Messages.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 Messages.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 }