001: /*
002: * <copyright>
003: *
004: * Copyright 1997-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.tools.server.examples;
028:
029: import java.util.*;
030: import java.awt.*;
031: import java.awt.datatransfer.*;
032: import java.awt.event.*;
033: import java.net.URL;
034: import java.io.*;
035: import java.text.DateFormat;
036: import java.text.SimpleDateFormat;
037: import javax.swing.*;
038: import javax.swing.text.*;
039: import javax.swing.border.*;
040: import javax.swing.event.*;
041:
042: import org.cougaar.tools.server.*;
043: import org.cougaar.tools.server.system.ProcessStatus;
044:
045: // import org.cougaar.tools.server.examples.DocumentOutputStream;
046:
047: /**
048: * Sample GUI console for community control.
049: * <p>
050: * See "MinConsole" for a trimmed-down (no-UI) example.
051: */
052: public class GuiConsole {
053:
054: private NodePanel nodePanel; // for one main panel
055: private JPanel content; // for main pane
056:
057: private Properties properties = null;
058: private String HostFileName;
059: private String ConfigFileName;
060:
061: private static DateFormat fileDateFormat = new SimpleDateFormat(
062: "yyyyMMddHHmmss");
063:
064: // ------------------------------------------------------
065: // class which sets up the main gui NodePanel
066: // ------------------------------------------------------
067:
068: protected class NodePanel extends JPanel {
069:
070: private JTabbedPane nodePane;
071:
072: private JTextArea stdoutArea;
073: private JScrollPane stdoutPane;
074: private Box buttons;
075: private JPanel buttons1;
076:
077: private JPanel buttons2;
078: // panel for typelist & configlist
079: private JPanel typePanel;
080: private JList configList;
081: private JList hostList;
082: // add scrolling for config
083: JScrollPane configscroll;
084:
085: private JButton runButton;
086: private JButton flushButton;
087: private JButton dumpThreadsButton;
088: private JButton listNodeProcessesButton;
089: private JButton listAllProcessesButton;
090: private JButton readFileButton;
091: private JButton writeFileButton;
092: private JButton listFilesButton;
093: private JButton stopButton;
094:
095: // node & host from gui
096: private String selectedHostName;
097: private String selectedNodeName;
098:
099: // the first host to be launched. Used to determine where the
100: // nameserver runs.
101: private String firstHost;
102:
103: RemoteHostRegistry remoteHostReg;
104: Map myNodes;
105: URLListener ul;
106:
107: public NodePanel(RemoteHostRegistry remoteHostReg)
108: throws IOException {
109:
110: stdoutArea = new JTextArea();
111: stdoutPane = new JScrollPane(stdoutArea);
112: buttons = Box.createVerticalBox();
113: buttons1 = new JPanel();
114: buttons2 = new JPanel(new GridLayout(3, 3));
115:
116: // panel for typelist & configlist
117: typePanel = new JPanel(new BorderLayout());
118: // get configs
119: configList = new JList(
120: getHosts(GuiConsole.this .ConfigFileName));
121: // get hosts
122: hostList = new JList(getHosts(GuiConsole.this .HostFileName));
123: // add scrolling for config
124: configscroll = new JScrollPane(configList);
125:
126: runButton = new JButton("Run");
127: dumpThreadsButton = new JButton("Trigger-Stack-Trace");
128: listNodeProcessesButton = new JButton("List-Node-Procs");
129: listAllProcessesButton = new JButton("List-All-Procs");
130: readFileButton = new JButton("Read-\"./dir/test.txt\"");
131: writeFileButton = new JButton("Write-\"./dir/test.txt\"");
132: listFilesButton = new JButton("List-Files");
133: flushButton = new JButton("Flush-Output");
134: stopButton = new JButton("Stop");
135:
136: // the first host to be launched. Used to determine where the
137: // nameserver runs.
138: firstHost = null;
139:
140: myNodes = new HashMap();
141:
142: // save the community controller
143: this .remoteHostReg = remoteHostReg;
144:
145: // configure list select
146: configList
147: .setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
148:
149: // handle config from gui
150: configList
151: .addListSelectionListener(new ListSelectionListener() {
152: public void valueChanged(ListSelectionEvent e) {
153: JList source = (JList) e.getSource();
154: selectedNodeName = (String) source
155: .getSelectedValue();
156: }
157: });
158:
159: // handle host from gui
160: hostList
161: .addListSelectionListener(new ListSelectionListener() {
162: public void valueChanged(ListSelectionEvent e) {
163: JList source = (JList) e.getSource();
164: selectedHostName = (String) source
165: .getSelectedValue();
166: }
167: });
168:
169: // handle run from gui by calling
170: runButton.addActionListener(new ActionListener() {
171: // createNode() which spawns remote nodes
172: public void actionPerformed(ActionEvent e) {
173: int port = 8484;
174: if ((selectedHostName == null)
175: || (selectedNodeName == null)) {
176: System.err
177: .println("Must select a node and host");
178: //hostList.setSelectedIndex(0);
179: //selectedHostName = (String) hostList.getSelectedValue();
180: return;
181: }
182: createNode(selectedHostName, port, selectedNodeName);
183: }
184: });
185:
186: // handle the "Flush-Output" button
187: flushButton.addActionListener(new ActionListener() {
188: public void actionPerformed(ActionEvent e) {
189: flushOutput(selectedNodeName);
190: }
191: });
192:
193: // handle "Trigger-Stack-Trace" button
194: dumpThreadsButton.addActionListener(new ActionListener() {
195: public void actionPerformed(ActionEvent e) {
196: dumpThreads(selectedNodeName);
197: }
198: });
199:
200: // handle "List-Node-Procs" button
201: listNodeProcessesButton
202: .addActionListener(new ActionListener() {
203: public void actionPerformed(ActionEvent e) {
204: listProcesses(selectedNodeName, false);
205: }
206: });
207:
208: // handle "List-All-Procs" button
209: listAllProcessesButton
210: .addActionListener(new ActionListener() {
211: public void actionPerformed(ActionEvent e) {
212: listProcesses(selectedNodeName, true);
213: }
214: });
215:
216: // handle "Read-File" button
217: readFileButton.addActionListener(new ActionListener() {
218: public void actionPerformed(ActionEvent e) {
219: readFile();
220: }
221: });
222:
223: // handle "Write-File" button
224: writeFileButton.addActionListener(new ActionListener() {
225: public void actionPerformed(ActionEvent e) {
226: writeFile();
227: }
228: });
229:
230: // handle "List-File" button
231: listFilesButton.addActionListener(new ActionListener() {
232: public void actionPerformed(ActionEvent e) {
233: listFiles();
234: }
235: });
236:
237: // handle stop button
238: stopButton.addActionListener(new ActionListener() {
239: // createNode() which spawns remote nodes
240: public void actionPerformed(ActionEvent e) {
241: stopNode(selectedNodeName);
242: }
243: });
244:
245: // label for NodePanel name
246: JLabel titleLabel = new JLabel(ConfigFileName,
247: JLabel.CENTER);
248: titleLabel.setFont(new Font("sans", Font.BOLD, 16));
249: titleLabel.setForeground(Color.black);
250:
251: // gui aesthetics & setup
252: setLayout(new BorderLayout(5, 10));
253: Border blackline = BorderFactory
254: .createLineBorder(Color.black);
255:
256: // typePanel corresponds to configList and hostList in gui
257: typePanel.setLayout(new GridLayout(1, 3));
258: typePanel.setBorder(blackline);
259:
260: TitledBorder title1 = BorderFactory.createTitledBorder(
261: BorderFactory.createEmptyBorder(), "hosts");
262: TitledBorder title2 = BorderFactory.createTitledBorder(
263: BorderFactory.createEmptyBorder(), "nodes");
264: title1.setTitlePosition(TitledBorder.ABOVE_TOP);
265: title2.setTitlePosition(TitledBorder.ABOVE_TOP);
266:
267: hostList.setBorder(title1);
268: configList.setBorder(title2);
269: configList.setVisibleRowCount(10);
270:
271: // add gui components
272: typePanel.add(configscroll);
273: typePanel.add(hostList);
274: add(typePanel, BorderLayout.WEST);
275: add(titleLabel, BorderLayout.NORTH);
276:
277: // adds stdout tabs dynamically
278: stdoutArea.setLineWrap(true);
279: nodePane = new JTabbedPane();
280: add(nodePane, BorderLayout.CENTER);
281:
282: add(buttons, BorderLayout.SOUTH);
283: buttons.add(buttons1, BorderLayout.NORTH);
284: buttons.add(buttons2, BorderLayout.SOUTH);
285:
286: buttons2.add(runButton);
287: buttons2.add(stopButton);
288: buttons2.add(flushButton);
289:
290: buttons2.add(dumpThreadsButton);
291: buttons2.add(listNodeProcessesButton);
292: buttons2.add(listAllProcessesButton);
293:
294: buttons2.add(readFileButton);
295: buttons2.add(writeFileButton);
296: buttons2.add(listFilesButton);
297: // empty slot
298: }
299:
300: public void start() {
301: if (ul == null) {
302: ul = new URLListener(9999);
303: ul.start();
304: }
305: }
306:
307: public void stop() {
308: if (ul != null) {
309: ul.stop();
310: }
311: }
312:
313: /**
314: * Get all the hostname specified in the given file.
315: */
316: public String[] getHosts(String afilename) {
317: java.util.List hosts = new ArrayList();
318:
319: // read hosts, one per line
320: try {
321: RandomAccessFile host_input = new RandomAccessFile(
322: afilename, "r");
323: while (true) {
324: String ihost = host_input.readLine(); // get their name
325: if (ihost == null) {
326: break;
327: }
328: hosts.add(ihost);
329: }
330: host_input.close();
331: } catch (IOException e) {
332: System.err.println("Error during read/open from file\n"
333: + e.toString());
334: System.exit(1);
335: }
336:
337: // convert List to a String[]
338: return (String[]) hosts.toArray(new String[hosts.size()]);
339: }
340:
341: /**
342: * Create a Node on the given host.
343: */
344: private void createNode(String hostName, int controlPort,
345: String nodeName) {
346:
347: // remember the first host that is spawned
348: if (this .firstHost == null) {
349: this .firstHost = hostName;
350: }
351:
352: // add a tab and panel to the main window
353: DefaultStyledDocument doc;
354: try {
355: // create an output pane
356: doc = new DefaultStyledDocument();
357: JTextPane pane = new JTextPane(doc);
358: JScrollPane stdoutPane = new JScrollPane(pane);
359: nodePane.add(nodeName, stdoutPane);
360: } catch (Exception e) {
361: return;
362: }
363:
364: // add new properties specifying the configuration
365: Properties c_props = new Properties();
366: c_props.putAll(GuiConsole.this .properties);
367: c_props.put("org.cougaar.node.name", nodeName);
368:
369: String nsps = GuiConsole.this .properties.getProperty(
370: "org.cougaar.tools.server.nameserver.ports",
371: "8888:5555");
372: c_props.put("org.cougaar.name.server", this .firstHost + ":"
373: + nsps);
374:
375: OutputListener ol;
376: try {
377: ol = new MyListener(getLogFileName(nodeName), doc);
378: } catch (Exception e) {
379: System.err.println("Unable to create output for \""
380: + nodeName + "\"");
381: e.printStackTrace();
382: // remove panel!
383: return;
384: }
385:
386: URL url = ul.addListener(nodeName, ol);
387:
388: OutputPolicy op = new OutputPolicy(20);
389:
390: RemoteProcess remoteProc;
391: try {
392: RemoteHost remoteHost = remoteHostReg.lookupRemoteHost(
393: hostName, controlPort, true);
394: ProcessDescription desc = new ProcessDescription(
395: nodeName, null, c_props, null);
396: RemoteListenableConfig conf = new RemoteListenableConfig(
397: url, //ol,
398: op);
399: remoteProc = remoteHost.createRemoteProcess(desc, conf);
400: } catch (Exception e) {
401: System.err.println("Unable to create node \""
402: + nodeName + "\" on host \"" + hostName + "\"");
403: e.printStackTrace();
404: // remove panel!
405: return;
406: }
407:
408: myNodes.put(nodeName, remoteProc);
409: }
410:
411: private void flushOutput(String name) {
412: RemoteProcess remoteProc = (RemoteProcess) myNodes
413: .get(name);
414: if (remoteProc == null) {
415: System.err.println("Unknown node name: " + name);
416: return;
417: }
418:
419: try {
420: RemoteListenable rl = remoteProc.getRemoteListenable();
421: rl.flushOutput();
422: } catch (Exception e) {
423: System.err.println("Unable to flush output");
424: e.printStackTrace();
425: }
426: }
427:
428: private void dumpThreads(String name) {
429: RemoteProcess remoteProc = (RemoteProcess) myNodes
430: .get(name);
431: if (remoteProc == null) {
432: System.err.println("Unknown node name: " + name);
433: return;
434: }
435:
436: try {
437: remoteProc.dumpThreads();
438: } catch (Exception e) {
439: System.err
440: .println("Unable to trigger a stack dump for node: "
441: + name);
442: e.printStackTrace();
443: return;
444: }
445:
446: // replace with pretty GUI code...
447: System.out.println("Triggered a stack dump for node: "
448: + name);
449: }
450:
451: private void listProcesses(String name, boolean showAll) {
452: RemoteProcess remoteProc = (RemoteProcess) myNodes
453: .get(name);
454: if (remoteProc == null) {
455: System.err.println("Unknown node name: " + name);
456: return;
457: }
458:
459: ProcessStatus[] psa;
460: try {
461: psa = remoteProc.listProcesses(showAll);
462: } catch (Exception e) {
463: System.err
464: .println("Unable to list processes for node: "
465: + name);
466: e.printStackTrace();
467: return;
468: }
469:
470: // replace with pretty GUI code...
471: int n = ((psa != null) ? psa.length : 0);
472: System.out.println(((showAll) ? "All" : "Node's")
473: + " ProcessStatus[" + n + "] for node " + name
474: + ":");
475: for (int i = 0; i < n; i++) {
476: ProcessStatus pi = psa[i];
477: // show terse details
478: System.out.println(" " + pi.toString(false));
479: }
480: }
481:
482: private void readFile() {
483: // for now this test is hard-coded
484: System.out
485: .println("Test -- read file \"./dir/test.txt\" from \"localhost:8484\"");
486: try {
487: RemoteHost remoteHost = remoteHostReg.lookupRemoteHost(
488: "localhost", 8484, true);
489: RemoteFileSystem rfs = remoteHost.getRemoteFileSystem();
490: InputStream in = rfs.read("./dir/test.txt");
491: BufferedReader r = new BufferedReader(
492: new InputStreamReader(in));
493: for (int i = 0;;) {
494: String s = r.readLine();
495: if (s == null) {
496: break;
497: }
498: System.out.println(i + "\t" + s);
499: }
500: } catch (Exception e) {
501: System.err.println("Failed: " + e);
502: e.printStackTrace();
503: }
504: }
505:
506: private void writeFile() {
507: // for now this test is hard-coded
508: System.out
509: .println("Test -- write file \"./dir/test.txt\" from \"localhost:8484\"");
510: try {
511: RemoteHost remoteHost = remoteHostReg.lookupRemoteHost(
512: "localhost", 8484, true);
513: RemoteFileSystem rfs = remoteHost.getRemoteFileSystem();
514: OutputStream os = rfs.write("./dir/test.txt");
515: os.write("test foo\ncontents".getBytes());
516: os.close();
517: } catch (Exception e) {
518: System.err.println("Failed: " + e);
519: e.printStackTrace();
520: }
521: }
522:
523: private void listFiles() {
524: // for now this test is hard-coded
525: System.out
526: .println("Test -- list files in \"./dir/\" from \"localhost:8484\"");
527: try {
528: RemoteHost remoteHost = remoteHostReg.lookupRemoteHost(
529: "localhost", 8484, true);
530: RemoteFileSystem rfs = remoteHost.getRemoteFileSystem();
531: String[] ret = rfs.list("./dir/");
532: int nret = ((ret != null) ? ret.length : 0);
533: System.out.println("listing[" + nret + "]:");
534: for (int i = 0; i < nret; i++) {
535: System.out.println(i + "\t" + ret[i]);
536: }
537: } catch (Exception e) {
538: System.err.println("Failed: " + e);
539: e.printStackTrace();
540: }
541: }
542:
543: private void stopNode(String name) {
544: RemoteProcess remoteProc = (RemoteProcess) myNodes
545: .get(name);
546: if (remoteProc == null) {
547: System.err.println("Unknown node name: " + name);
548: return;
549: }
550:
551: try {
552: // should flush first!
553: RemoteListenable rl = remoteProc.getRemoteListenable();
554: rl.flushOutput();
555:
556: // now destroy
557: remoteProc.destroy();
558: } catch (Exception e) {
559: System.err.println("Unable to destroy node \"" + name
560: + "\"");
561: }
562:
563: myNodes.remove(name);
564:
565: // remove the pane from the tabs
566: int i = nodePane.indexOfTab(name);
567: if (i != -1) {
568: nodePane.removeTabAt(i);
569: }
570: }
571:
572: private String getLogFileName(String prefix) {
573: return prefix
574: + GuiConsole.this .fileDateFormat.format(new Date())
575: + ".log";
576: }
577:
578: public Dimension getPreferredSize() {
579: return new Dimension(850, 350);
580: }
581:
582: /**
583: * Listener for "pushed" Node activities.
584: */
585: protected class MyListener implements OutputListener {
586:
587: private OutputStream toFile;
588:
589: private OutputStream toGuiOut;
590: private OutputStream toGuiErr;
591: private OutputStream toGuiOther;
592:
593: private MyListener(String toFileName, Document toText)
594: throws IOException {
595:
596: // create file stream
597: toFile = new BufferedOutputStream(new FileOutputStream(
598: toFileName));
599:
600: // create gui streams
601: toGuiOut = new DocumentOutputStream(toText, Color.black);
602: toGuiErr = new DocumentOutputStream(toText, Color.red);
603: toGuiOther = new DocumentOutputStream(toText,
604: Color.blue);
605: }
606:
607: public void handleOutputBundle(final OutputBundle ob) {
608: // append to file
609: fileWrite(ob);
610:
611: // append to text-area
612: // must use swing "invokeLater" to be thread-safe
613: Runnable r = new Runnable() {
614: public void run() {
615: guiWrite(ob);
616: }
617: };
618: SwingUtilities.invokeLater(r);
619: }
620:
621: private void fileWrite(OutputBundle ob) {
622: // just write std-out and std-err
623: try {
624: ob.getDualStreamBuffer().writeTo(toFile);
625: } catch (Exception e) {
626: // file dead?
627: }
628: }
629:
630: private void guiWrite(OutputBundle ob) {
631: try {
632: if (ob.getCreated()) {
633: toGuiOther.write("<created>".getBytes());
634: }
635: ob.getDualStreamBuffer()
636: .writeTo(toGuiOut, toGuiErr);
637: if (ob.getDestroyed()) {
638: toGuiOther.write("<destroyed>".getBytes());
639: }
640: } catch (IOException ioe) {
641: System.err.println("GUI IO failure: ");
642: ioe.printStackTrace();
643: } catch (Exception e) {
644: // gui dead?
645: }
646: }
647: }
648:
649: }
650:
651: private GuiConsole() {
652: }
653:
654: public Component init(RemoteHostRegistry remoteHostReg,
655: String args[]) throws IOException {
656: int l = args.length;
657: if (l < 2) {
658: System.err
659: .println("Usage: GuiConsole hostfile configfile [propertiesfile]");
660: System.exit(1);
661: }
662:
663: HostFileName = args[0];
664: ConfigFileName = args[1];
665:
666: properties = new Properties();
667: if (l > 2) {
668: String pfile = args[2];
669: if (!("-".equals(pfile))) {
670: try {
671: InputStream is = new FileInputStream(pfile);
672: properties.load(is);
673: } catch (Exception ex) {
674: ex.printStackTrace();
675: }
676: }
677: }
678:
679: content = new JPanel(new BorderLayout());
680: //content.setPreferredSize(new Dimension(800, 400));
681:
682: nodePanel = new NodePanel(remoteHostReg);
683: content.add(nodePanel);
684:
685: return content;
686: }
687:
688: public void start() {
689: nodePanel.start();
690: }
691:
692: public void stop() {
693: nodePanel.stop();
694: }
695:
696: // --------------------------------------------
697: // main() creates a GuiConsole instance and fires up the gui
698: // --------------------------------------------
699:
700: public static void main(String[] args) {
701: try {
702: // create the support hook
703: RemoteHostRegistry remoteHostReg = RemoteHostRegistry
704: .getInstance();
705:
706: // create the console
707: final GuiConsole guiconsole = new GuiConsole();
708: Component component = guiconsole.init(remoteHostReg, args);
709:
710: // wrap in a GUI frame
711: JFrame frame = new JFrame("GuiConsole");
712: frame.getContentPane().add(component);
713: frame.addWindowListener(new WindowAdapter() {
714: public void windowClosing(WindowEvent e) {
715: guiconsole.stop();
716: System.exit(0);
717: }
718: });
719: frame.pack();
720: frame.show();
721:
722: // start the guiconsole
723: guiconsole.start();
724: } catch (Exception e) {
725: e.printStackTrace();
726: System.exit(-1);
727: }
728: }
729: }
|