001: // $Id: ReplicatedTreeDemo.java,v 1.7 2005/06/14 08:36:49 belaban Exp $
002:
003: package org.jgroups.demos;
004:
005: import org.jgroups.View;
006: import org.jgroups.blocks.ReplicatedTree;
007:
008: import javax.swing.*;
009: import javax.swing.event.TableModelEvent;
010: import javax.swing.event.TableModelListener;
011: import javax.swing.event.TreeSelectionEvent;
012: import javax.swing.event.TreeSelectionListener;
013: import javax.swing.table.DefaultTableModel;
014: import javax.swing.table.TableColumn;
015: import javax.swing.tree.*;
016: import java.awt.*;
017: import java.awt.event.*;
018: import java.io.File;
019: import java.util.*;
020:
021: /**
022: * Graphical view of a ReplicatedTree
023: *
024: * @author Bela Ban
025: */
026: public class ReplicatedTreeDemo {
027:
028: /**
029: * Graphical view of a ReplicatedTree (using the MVC paradigm). An instance of this class needs to be given a
030: * reference to the underlying model (ReplicatedTree) and needs to registers as a ReplicatedTreeListener. Changes
031: * to the tree structure are propagated from the model to the view (via ReplicatedTreeListener), changes from the
032: * GUI (e.g. by a user) are executed on the tree model (which will broadcast the changes to all replicas).<p>
033: * The view itself caches only the nodes, but doesn't cache any of the data (HashMap) associated with it. When
034: * data needs to be displayed, the underlying tree will be accessed directly.
035: *
036: * @author Bela Ban
037: */
038: static class ReplicatedTreeView extends JFrame implements
039: WindowListener, ReplicatedTree.ReplicatedTreeListener,
040: TreeSelectionListener, TableModelListener {
041: DefaultTreeModel tree_model = null;
042: JTree jtree = null;
043: final DefaultTableModel table_model = new DefaultTableModel();
044: final JTable table = new JTable(table_model);
045: final MyNode root = new MyNode(SEP);
046: final String props = null;
047: String selected_node = null;
048: ReplicatedTree tree = null; // the underlying model
049: JPanel tablePanel = null;
050: JMenu operationsMenu = null;
051: JPopupMenu operationsPopup = null;
052: JMenuBar menubar = null;
053: static final String SEP = ReplicatedTree.SEPARATOR;
054: private static final int KEY_COL_WIDTH = 20;
055: private static final int VAL_COL_WIDTH = 300;
056:
057: public ReplicatedTreeView(ReplicatedTree tree, Object title)
058: throws Exception {
059: this .tree = tree;
060: tree.addReplicatedTreeListener(this );
061:
062: addNotify();
063: setTitle("ReplicatedTreeDemo: mbr=" + title);
064:
065: tree_model = new DefaultTreeModel(root);
066: jtree = new JTree(tree_model);
067: jtree.setDoubleBuffered(true);
068: jtree.getSelectionModel().setSelectionMode(
069: TreeSelectionModel.SINGLE_TREE_SELECTION);
070:
071: JScrollPane scroll_pane = new JScrollPane(jtree);
072:
073: populateTree();
074:
075: getContentPane().add(scroll_pane, BorderLayout.CENTER);
076: addWindowListener(this );
077:
078: table_model.setColumnIdentifiers(new String[] { "Name",
079: "Value" });
080: table_model.addTableModelListener(this );
081:
082: setTableColumnWidths();
083:
084: tablePanel = new JPanel();
085: tablePanel.setLayout(new BorderLayout());
086: tablePanel.add(table.getTableHeader(), BorderLayout.NORTH);
087: tablePanel.add(table, BorderLayout.CENTER);
088:
089: getContentPane().add(tablePanel, BorderLayout.SOUTH);
090:
091: jtree.addTreeSelectionListener(this );//REVISIT
092:
093: MouseListener ml = new MouseAdapter() {
094: public void mouseClicked(MouseEvent e) {
095: int selRow = jtree.getRowForLocation(e.getX(), e
096: .getY());
097: TreePath selPath = jtree.getPathForLocation(e
098: .getX(), e.getY());
099: if (selRow != -1) {
100: selected_node = makeFQN(selPath.getPath());
101: jtree.setSelectionPath(selPath);
102:
103: if (e.getModifiers() == java.awt.event.InputEvent.BUTTON3_MASK) {
104: operationsPopup.show(e.getComponent(), e
105: .getX(), e.getY());
106: }
107: }
108: }
109: };
110:
111: jtree.addMouseListener(ml);
112:
113: createMenus();
114: setLocation(50, 50);
115: setSize(getInsets().left + getInsets().right + 485,
116: getInsets().top + getInsets().bottom + 367);
117:
118: init();
119: setVisible(true);
120: }
121:
122: public void windowClosed(WindowEvent event) {
123: }
124:
125: public void windowDeiconified(WindowEvent event) {
126: }
127:
128: public void windowIconified(WindowEvent event) {
129: }
130:
131: public void windowActivated(WindowEvent event) {
132: }
133:
134: public void windowDeactivated(WindowEvent event) {
135: }
136:
137: public void windowOpened(WindowEvent event) {
138: }
139:
140: public void windowClosing(WindowEvent event) {
141: System.exit(0);
142: }
143:
144: public void tableChanged(TableModelEvent evt) {
145: int row, col;
146: String key, val;
147:
148: if (evt.getType() == TableModelEvent.UPDATE) {
149: row = evt.getFirstRow();
150: col = evt.getColumn();
151: if (col == 0) { // set()
152: key = (String) table_model.getValueAt(row, col);
153: val = (String) table_model.getValueAt(row, col + 1);
154: if (key != null && val != null) {
155: tree.put(selected_node, key, val);
156: }
157: } else { // add()
158: key = (String) table_model.getValueAt(row, col - 1);
159: val = (String) table.getValueAt(row, col);
160: if (key != null && val != null) {
161: tree.put(selected_node, key, val);
162: }
163: }
164: }
165: }
166:
167: public void valueChanged(TreeSelectionEvent evt) {
168: TreePath path = evt.getPath();
169: String fqn = SEP;
170: String component_name;
171: HashMap data = null;
172:
173: for (int i = 0; i < path.getPathCount(); i++) {
174: component_name = ((MyNode) path.getPathComponent(i)).name;
175: if (component_name.equals(SEP))
176: continue;
177: if (fqn.equals(SEP))
178: fqn += component_name;
179: else
180: fqn = fqn + SEP + component_name;
181: }
182: data = getData(tree, fqn);
183: if (data != null) {
184: getContentPane().add(tablePanel, BorderLayout.SOUTH);
185: populateTable(data);
186: validate();
187: } else {
188: clearTable();
189: getContentPane().remove(tablePanel);
190: validate();
191: }
192: }
193:
194: /* ------------------ ReplicatedTree.ReplicatedTreeListener interface ------------ */
195:
196: public void nodeAdded(String fqn) {
197: MyNode n, p;
198:
199: n = root.add(fqn);
200: if (n != null) {
201: p = (MyNode) n.getParent();
202: tree_model.reload(p);
203: jtree.scrollPathToVisible(new TreePath(n.getPath()));
204: }
205: }
206:
207: public void nodeRemoved(String fqn) {
208: MyNode n;
209: TreeNode par;
210:
211: n = root.findNode(fqn);
212: if (n != null) {
213: n.removeAllChildren();
214: par = n.getParent();
215: n.removeFromParent();
216: tree_model.reload(par);
217: }
218: }
219:
220: public void nodeModified(String fqn) {
221: // HashMap data;
222: // data=getData(tree, fqn);
223: //populateTable(data); REVISIT
224: /*
225: poulateTable is the current table being shown is the info of the node. that is modified.
226: */
227: }
228:
229: public void viewChange(View new_view) {
230: Vector mbrship;
231: if (new_view != null
232: && (mbrship = new_view.getMembers()) != null) {
233: tree._put(SEP, "members", mbrship);
234: tree._put(SEP, "coordinator", mbrship.firstElement());
235: }
236: }
237:
238: /* ---------------- End of ReplicatedTree.ReplicatedTreeListener interface -------- */
239:
240: /*----------------- Runnable implementation to make View change calles in AWT Thread ---*/
241:
242: public void run() {
243:
244: }
245:
246: /* ----------------------------- Private Methods ---------------------------------- */
247:
248: /**
249: * Fetches all data from underlying tree model and display it graphically
250: */
251: void init() {
252: Vector mbrship = null;
253:
254: addGuiNode(SEP);
255:
256: mbrship = tree != null && tree.getMembers() != null ? (Vector) tree
257: .getMembers().clone()
258: : null;
259: if (mbrship != null) {
260: tree._put(SEP, "members", mbrship);
261: tree._put(SEP, "coordinator", mbrship.firstElement());
262: }
263: }
264:
265: /**
266: * Fetches all data from underlying tree model and display it graphically
267: */
268: private void populateTree() {
269: addGuiNode(SEP);
270: }
271:
272: /**
273: * Recursively adds GUI nodes starting from fqn
274: */
275: void addGuiNode(String fqn) {
276: Set children;
277: String child_name;
278:
279: if (fqn == null)
280: return;
281:
282: // 1 . Add myself
283: root.add(fqn);
284:
285: // 2. Then add my children
286: children = tree.getChildrenNames(fqn);
287: if (children != null) {
288: for (Iterator it = children.iterator(); it.hasNext();) {
289: child_name = (String) it.next();
290: addGuiNode(fqn + SEP + child_name);
291: }
292: }
293: }
294:
295: String makeFQN(Object[] path) {
296: StringBuffer sb = new StringBuffer("");
297: String tmp_name;
298:
299: if (path == null)
300: return null;
301: for (int i = 0; i < path.length; i++) {
302: tmp_name = ((MyNode) path[i]).name;
303: if (tmp_name.equals(SEP))
304: continue;
305: else
306: sb.append(SEP + tmp_name);
307: }
308: tmp_name = sb.toString();
309: if (tmp_name.length() == 0)
310: return SEP;
311: else
312: return tmp_name;
313: }
314:
315: void clearTable() {
316: int num_rows = table.getRowCount();
317:
318: if (num_rows > 0) {
319: for (int i = 0; i < num_rows; i++)
320: table_model.removeRow(0);
321: table_model.fireTableRowsDeleted(0, num_rows - 1);
322: repaint();
323: }
324: }
325:
326: void populateTable(HashMap data) {
327: String key, strval = "<null>";
328: Object val;
329: int num_rows = 0;
330: Map.Entry entry;
331:
332: if (data == null)
333: return;
334: num_rows = data.size();
335: clearTable();
336:
337: if (num_rows > 0) {
338: for (Iterator it = data.entrySet().iterator(); it
339: .hasNext();) {
340: entry = (Map.Entry) it.next();
341: key = (String) entry.getKey();
342: val = entry.getValue();
343: if (val != null)
344: strval = val.toString();
345: table_model.addRow(new Object[] { key, strval });
346: }
347: table_model.fireTableRowsInserted(0, num_rows - 1);
348: validate();
349: }
350: }
351:
352: private void setTableColumnWidths() {
353: table.sizeColumnsToFit(JTable.AUTO_RESIZE_NEXT_COLUMN);
354: TableColumn column = null;
355: column = table.getColumnModel().getColumn(0);
356: column.setMinWidth(KEY_COL_WIDTH);
357: column.setPreferredWidth(KEY_COL_WIDTH);
358: column = table.getColumnModel().getColumn(1);
359: column.setPreferredWidth(VAL_COL_WIDTH);
360: }
361:
362: private void createMenus() {
363: menubar = new JMenuBar();
364: operationsMenu = new JMenu("Operations");
365: AddNodeAction addNode = new AddNodeAction();
366: addNode.putValue(AbstractAction.NAME, "Add to this node");
367: RemoveNodeAction removeNode = new RemoveNodeAction();
368: removeNode
369: .putValue(AbstractAction.NAME, "Remove this node");
370: AddModifyDataForNodeAction addModAction = new AddModifyDataForNodeAction();
371: addModAction.putValue(AbstractAction.NAME,
372: "Add/Modify data");
373: ExitAction exitAction = new ExitAction();
374: exitAction.putValue(AbstractAction.NAME, "Exit");
375: operationsMenu.add(addNode);
376: operationsMenu.add(removeNode);
377: operationsMenu.add(addModAction);
378: operationsMenu.add(exitAction);
379: menubar.add(operationsMenu);
380: setJMenuBar(menubar);
381:
382: operationsPopup = new JPopupMenu();
383: operationsPopup.add(addNode);
384: operationsPopup.add(removeNode);
385: operationsPopup.add(addModAction);
386: }
387:
388: HashMap getData(ReplicatedTree tree, String fqn) {
389: HashMap data;
390: Set keys;
391: String key;
392: Object value;
393:
394: if (tree == null || fqn == null)
395: return null;
396: keys = tree.getKeys(fqn);
397: if (keys == null)
398: return null;
399: data = new HashMap();
400: for (Iterator it = keys.iterator(); it.hasNext();) {
401: key = (String) it.next();
402: value = tree.get(fqn, key);
403: if (value != null)
404: data.put(key, value);
405: }
406: return data;
407: }
408:
409: /* -------------------------- End of Private Methods ------------------------------ */
410:
411: /*----------------------- Actions ---------------------------*/
412: class ExitAction extends AbstractAction {
413: public void actionPerformed(ActionEvent e) {
414: System.exit(0);
415: }
416: }
417:
418: class AddNodeAction extends AbstractAction {
419: public void actionPerformed(ActionEvent e) {
420: JTextField fqnTextField = new JTextField();
421: if (selected_node != null)
422: fqnTextField.setText(selected_node);
423: Object[] information = { "Enter fully qualified name",
424: fqnTextField };
425: final String btnString1 = "OK";
426: final String btnString2 = "Cancel";
427: Object[] options = { btnString1, btnString2 };
428: int userChoice = JOptionPane.showOptionDialog(null,
429: information, "Add Node",
430: JOptionPane.YES_NO_OPTION,
431: JOptionPane.PLAIN_MESSAGE, null, options,
432: options[0]);
433: if (userChoice == 0) {
434: String userInput = fqnTextField.getText();
435: tree.put(userInput, null);
436: }
437: }
438: }
439:
440: class RemoveNodeAction extends AbstractAction {
441: public void actionPerformed(ActionEvent e) {
442: tree.remove(selected_node);
443: }
444: }
445:
446: class AddModifyDataForNodeAction extends AbstractAction {
447: public void actionPerformed(ActionEvent e) {
448: HashMap data = getData(tree, selected_node);
449: if (data != null) {
450: } else {
451: clearTable();
452: data = new HashMap();
453: data.put("Add Key", "Add Value");
454:
455: }
456: populateTable(data);
457: getContentPane().add(tablePanel, BorderLayout.SOUTH);
458: validate();
459:
460: }
461: }
462:
463: // public static void main(String args[]) {
464: // ReplicatedTree tree;
465: //
466: // for(int i=0; i < args.length; i++) {
467: // if(args[i].equals("-help")) {
468: // System.out.println("ReplicatedTreeView [-help]");
469: // return;
470: // }
471: // }
472: //
473: // try {
474: // tree=new ReplicatedTree(null);
475: // tree.setRemoteCalls(false);
476: // HashMap map=new HashMap();
477: // map.put("name", "Framework");
478: // map.put("pid", new Integer(322649));
479: // tree.put("/federations/fed1/servers/Framework", map);
480: // tree.put("/federations/fed1/servers/Security", null);
481: //
482: // // demo.setVisible(true);
483: // new ReplicatedTreeView(tree, "<null address>");
484: //
485: // tree.put("/federations/fed1/servers/Security/components/RuntimeMonitor", null);
486: // tree.put("/federations/fed1/servers/fenics", null);
487: //
488: //
489: // }
490: // catch(Exception ex) {
491: // ex.printStackTrace(System.err);
492: // }
493: // }
494:
495: class MyNode extends DefaultMutableTreeNode {
496: String name = "<unnamed>";
497:
498: MyNode(String name) {
499: this .name = name;
500: }
501:
502: /**
503: * Adds a new node to the view. Intermediary nodes will be created if they don't yet exist.
504: * Returns the first node that was created or null if node already existed
505: */
506: public MyNode add(String fqn) {
507: MyNode curr, n, ret = null;
508: StringTokenizer tok;
509: String child_name;
510:
511: if (fqn == null)
512: return null;
513: curr = this ;
514: tok = new StringTokenizer(fqn, ReplicatedTreeView.SEP);
515:
516: while (tok.hasMoreTokens()) {
517: child_name = tok.nextToken();
518: n = curr.findChild(child_name);
519: if (n == null) {
520: n = new MyNode(child_name);
521: if (ret == null)
522: ret = n;
523: curr.add(n);
524: }
525: curr = n;
526: }
527: return ret;
528: }
529:
530: /**
531: * Removes a node from the view. Child nodes will be removed as well
532: */
533: public void remove(String fqn) {
534: removeFromParent();
535: }
536:
537: MyNode findNode(String fqn) {
538: MyNode curr, n;
539: StringTokenizer tok;
540: String child_name;
541:
542: if (fqn == null)
543: return null;
544: curr = this ;
545: tok = new StringTokenizer(fqn, ReplicatedTreeView.SEP);
546:
547: while (tok.hasMoreTokens()) {
548: child_name = tok.nextToken();
549: n = curr.findChild(child_name);
550: if (n == null)
551: return null;
552: curr = n;
553: }
554: return curr;
555: }
556:
557: MyNode findChild(String relative_name) {
558: MyNode child;
559:
560: if (relative_name == null || getChildCount() == 0)
561: return null;
562: for (int i = 0; i < getChildCount(); i++) {
563: child = (MyNode) getChildAt(i);
564: if (child.name == null) {
565: continue;
566: }
567:
568: if (child.name.equals(relative_name))
569: return child;
570: }
571: return null;
572: }
573:
574: String print(int indent) {
575: StringBuffer sb = new StringBuffer();
576:
577: for (int i = 0; i < indent; i++)
578: sb.append(' ');
579: if (!isRoot()) {
580: if (name == null)
581: sb.append("/<unnamed>");
582: else {
583: sb.append(ReplicatedTreeView.SEP + name);
584: }
585: }
586: sb.append('\n');
587: if (getChildCount() > 0) {
588: if (isRoot())
589: indent = 0;
590: else
591: indent += 4;
592: for (int i = 0; i < getChildCount(); i++)
593: sb.append(((MyNode) getChildAt(i))
594: .print(indent));
595: }
596: return sb.toString();
597: }
598:
599: public String toString() {
600: return name;
601: }
602:
603: }
604:
605: }
606:
607: public static void main(String args[]) {
608: ReplicatedTree tree;
609: String start_directory = null;
610: boolean jmx = false;
611:
612: String props = "UDP(mcast_addr=224.0.0.36;mcast_port=55566;ip_ttl=32;"
613: + "mcast_send_buf_size=150000;mcast_recv_buf_size=80000):"
614: + "PING(timeout=2000;num_initial_members=3):"
615: + "MERGE2(min_interval=5000;max_interval=10000):"
616: + "FD_SOCK:"
617: + "VERIFY_SUSPECT(timeout=1500):"
618: + "pbcast.NAKACK(gc_lag=50;retransmit_timeout=600,1200,2400,4800):"
619: + "UNICAST(timeout=600,1200,2400,4800):"
620: + "pbcast.STABLE(desired_avg_gossip=20000):"
621: + "FRAG(frag_size=16000;down_thread=false;up_thread=false):"
622: + "pbcast.GMS(join_timeout=5000;join_retry_timeout=2000;"
623: + "shun=false;print_local_addr=true):"
624: + "pbcast.STATE_TRANSFER";
625: // "PERF(details=true)";
626:
627: for (int i = 0; i < args.length; i++) {
628: if ("-props".equals(args[i])) {
629: props = args[++i];
630: continue;
631: }
632: if ("-start_directory".equals(args[i])) {
633: start_directory = args[++i];
634: continue;
635: }
636: if ("-jmx".equals(args[i])) {
637: jmx = true;
638: continue;
639: }
640: help();
641: return;
642: }
643:
644: try {
645: tree = new ReplicatedTree("ReplicatedTreeDemo-Group",
646: props, 10000, jmx);
647: new ReplicatedTreeView(tree, tree.getLocalAddress());
648: // demo.setVisible(true);
649:
650: if (start_directory != null && start_directory.length() > 0) {
651: populateTree(tree, start_directory);
652: } else {
653: /*
654: HashMap map=new HashMap();
655: map.put("name", "Framework");
656: map.put("pid", new Integer(322649));
657: tree.put("/federations/fed1/servers/Framework", map);
658: tree.put("/federations/fed1/servers/Security", null);
659: tree.put("/federations/fed1/servers/Security/components/RuntimeMonitor", null);
660: tree.put("/federations/fed1/servers/fenics", null);
661: */
662: }
663: } catch (Exception ex) {
664: ex.printStackTrace(System.err);
665: }
666: }
667:
668: static void help() {
669: System.out
670: .println("ReplicatedTreeView [-help] "
671: + "[-props <channel properties>] [-start_directory <dirname>] [-jmx]");
672: }
673:
674: static void populateTree(ReplicatedTree tree, String dir) {
675: File file = new File(dir);
676:
677: if (!file.exists())
678: return;
679: tree.put(dir, null);
680:
681: if (file.isDirectory()) {
682: String[] children = file.list();
683: if (children != null && children.length > 0) {
684: for (int i = 0; i < children.length; i++)
685: populateTree(tree, dir + '/' + children[i]);
686: }
687: }
688: }
689:
690: }
|