Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/altara/mars/swingui/MarsView.java


1   /* MARS Network Monitor Swing User Interface
2      Copyright (C) 1999 Brian H. Trammell
3      Copyright (C) 2002 Leapfrog Research & Development, LLC
4   
5     This program is free software; you can redistribute it and/or
6     modify it under the terms of the GNU General Public License
7     as published by the Free Software Foundation; either version 2
8     of the License, or (at your option) any later version.
9   
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14    
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, it is available at 
17    http:///www.gnu.org/copyleft/gpl.html, or by writing to the
18    Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA  02111-1307, USA.
20  */
21  
22  package org.altara.mars.swingui;
23  
24  import org.altara.util.*;
25  import org.altara.mars.*;
26  import org.altara.mars.engine.*;
27  import org.jdom.*;
28  import java.io.*;
29  import java.net.*;
30  import java.util.*;
31  import java.awt.*;
32  import java.awt.event.*;
33  import javax.swing.*;
34  import javax.swing.tree.*;
35  import javax.swing.event.*;
36  import javax.swing.border.*;
37  
38  /** MarsView is the root of the MARS Swing user interface.
39  */
40  
41  public class MarsView extends JFrame implements StatusView {
42  
43    // Toolbar actions
44    private Action newAction;
45    private Action openAction;
46    private Action saveAction;
47    private Action startAction;
48    private Action stopAction;
49    private boolean lastSaveActionState;
50  
51    // List and tree models
52    private FaultListModel flm;
53    private DefaultTreeModel stm;
54    private ServiceTreeChangeAdapter stca;
55    private DetailListModel dlm;
56  
57    // Panels
58    private ChangeListPanel clp;
59    private ConfigPanel cfgp;
60  
61      // Status bar
62      private Queue statusQ;
63      private JTextField statusbar;
64      private javax.swing.Timer statusQTimer;
65  
66      private static final int SB_THROTTLE = 2;
67      private static final int SB_DELAY = 2000;
68  
69    // Context menu support classes
70    private ServiceTreeContextMenuSupport stcms;
71    private FaultListContextMenuSupport flcms;
72  
73    // Lists and trees
74    private JTree serviceTree;
75  
76    // Save/Open files
77    private File curDir;
78    private File curSaveFile;
79  
80    public MarsView () {
81      // set the window's title
82      super("Mars "+Main.VERSION);
83  
84      // Get the controller (understood to be initialized
85      // and to have a bound MarsModel) - we'll use it a lot
86      // below
87      final Controller controller = Main.getMain().getController();
88      MarsModel model = controller.getModel();
89  
90      // initialize toolbar (required for stca connection)
91      JToolBar toolbar = initializeToolbar();
92  
93      // set file defaults
94      curDir = new File(".");
95      curSaveFile = null;
96  
97      // create a fault list and bind it to the controller
98      flm = new FaultListModel(this);
99      controller.addStatusChangeListener(
100       new StatusChangeThreadAdapter(flm));
101     final JList faultList = new JList(flm);
102     JScrollPane faultListSP = new JScrollPane(faultList);
103     // set up various list options
104     faultListSP.setBorder(new TitledBorder("Current Faults"));
105     // set up the renderer for the fault list
106     FaultListRenderer flr = new FaultListRenderer(flm);
107     faultList.setCellRenderer(flr);
108     // make it a change listener
109     model.addMarsModelListener(flm);
110 
111     // add context menus to the fault list
112     flcms = new FaultListContextMenuSupport(faultList);
113 
114     // create a change log panel 
115     clp = new ChangeListPanel(controller);
116 
117     // create a service tree bound to the model
118     stm = new DefaultTreeModel(controller.getModel());
119     serviceTree = new JTree(stm);
120     JScrollPane serviceTreeSP = new JScrollPane(serviceTree);
121     // set up various tree options
122     serviceTree.setRootVisible(false);
123     serviceTreeSP.setBorder(new TitledBorder("Hosts & Services"));
124     // add a renderer
125     ServiceTreeRenderer str = new ServiceTreeRenderer();
126     serviceTree.setCellRenderer(str);
127     // use a ServiceTreeChangeAdapter to route change events to the
128     // correct places...
129     stca = new ServiceTreeChangeAdapter(controller,model,serviceTree,this);
130 
131     // add context menus to the service tree
132     stcms = new ServiceTreeContextMenuSupport(this,serviceTree);
133 
134     // bind the service tree selection to the fault list
135     faultList.addListSelectionListener(new ListSelectionListener() {
136       public void valueChanged(ListSelectionEvent lse) {
137         if (lse.getValueIsAdjusting()) return;
138         Object selObj = faultList.getSelectedValue();
139         if (!(selObj instanceof Service)) return;
140         TreePath selPath = ((Service)selObj).getTreePath();
141         serviceTree.setSelectionPath(selPath);
142         serviceTree.makeVisible(selPath);
143       }
144     });
145 
146     // create a detail view bound to the controller
147     // and the service tree
148     dlm = new DetailListModel();
149     controller.addProbeListener(new ProbeThreadAdapter(dlm));
150     serviceTree.addTreeSelectionListener(dlm);
151     JList detailList = new JList(dlm);
152     JScrollPane detailListSP = new JScrollPane(detailList);
153     // set up various list options
154     detailListSP.setBorder(new TitledBorder("Service Detail"));
155     // no renderer as of yet - set font to size 10, though.
156     detailList.setFont(MarsAbstractRenderer.cellFont);
157 
158     // create a configuration panel
159     cfgp = new ConfigPanel(this);
160 
161     // add a status change listener to modify the titlebar
162     controller.addStatusChangeListener(new StatusChangeThreadAdapter(
163       new StatusChangeListener() {
164       public void statusChanged(StatusChangeEvent sce) {
165         updateTitleBar();
166       }
167     }));
168 
169     // ensure the controller is stopped and set appropriate
170     // default enable states
171     controller.stop();
172     stopAction.setEnabled(false);
173     saveAction.setEnabled(false);
174     stcms.setEditActionsEnabled(true);
175 
176     // stuff it all together into a tab pane
177     JSplitPane rightPane = new JSplitPane(
178       JSplitPane.VERTICAL_SPLIT, faultListSP, detailListSP);
179     JSplitPane frontPane = new JSplitPane(
180       JSplitPane.HORIZONTAL_SPLIT, serviceTreeSP, rightPane);
181     JTabbedPane tabPane = new JTabbedPane(JTabbedPane.BOTTOM);
182     tabPane.addTab("Current",frontPane);
183     tabPane.addTab("History",clp);
184     tabPane.addTab("Config",cfgp);
185 
186 
187         // create the status bar
188         statusQ = new Queue();
189         statusbar = new JTextField("Starting...");
190         statusbar.setEditable(false);
191 
192         controller.addProbeListener(new ProbeListener() {
193             public void probeRun(ProbeEvent ev) {
194                 showStatus("Checked "+ev.getService());
195             }
196         });
197 
198         statusQTimer =
199             new javax.swing.Timer(SB_DELAY,new ActionListener() {
200             public void actionPerformed(ActionEvent ae) {
201                 displayNextStatus();
202             }
203         });
204         statusQTimer.setCoalesce(true);
205         statusQTimer.setRepeats(true);
206         statusQTimer.start();
207 
208     // now put it into the window
209     getContentPane().setLayout(new BorderLayout());
210     getContentPane().add(toolbar,BorderLayout.NORTH);
211     getContentPane().add(tabPane,BorderLayout.CENTER);
212         getContentPane().add(statusbar,BorderLayout.SOUTH);
213 
214     // set up the window close listener
215     setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
216     addWindowListener(new WindowAdapter() {
217       public void windowClosing(WindowEvent we) {
218         // for now, do this all in-loop
219         // later, we'll want to ask the app master
220         // to shut down for us
221         if (confirmDiscardChanges()) {
222           setVisible(false);
223           controller.stop();
224           System.exit(0);
225         }
226       }
227     });
228 
229     // rock and roll
230     this.setSize(500,400);
231     this.show();
232     rightPane.setDividerLocation(0.5);
233     frontPane.setDividerLocation(0.4);
234   }
235 
236     public void showStatus(String status) {
237         statusQ.enqueue(status);
238     }
239 
240     private void displayNextStatus() {
241         String nextStatus = (String)statusQ.nb_dequeue();
242         if (nextStatus == null) {
243             if (Main.getMain().getController().isActive()) {
244                 statusbar.setText("Monitoring.");
245             } else {
246                 statusbar.setText("Idle.");
247             }
248         } else {
249             // check throttle
250             int backlog = statusQ.size();
251             if (backlog > SB_THROTTLE) {
252                 statusQTimer.setDelay((SB_THROTTLE * SB_DELAY)/backlog);
253             }
254             // display next status
255             statusbar.setText(nextStatus);
256         }
257     }
258 
259   void updateTitleBar() {
260     if (!Main.getMain().getController().isActive())
261       setTitle("Mars "+Main.VERSION);
262     else if (flm.getFaultCount() == 0) setTitle("Mars [No Faults]");
263     else if (flm.getFaultCount() == 1) setTitle("Mars [1 Fault]");
264     else setTitle("Mars ["+flm.getFaultCount()+" Faults]");
265   }
266 
267   private JToolBar initializeToolbar() {
268     // create the toolbar
269     JToolBar toolbar = new JToolBar();
270 
271     newAction = 
272       new AbstractAction("New...",  
273         IconService.getIcon("mi2_tbnew.gif")) {
274       public void actionPerformed(ActionEvent ae) {
275         newModel();
276       }
277     };
278 
279     openAction = 
280       new AbstractAction("Open...",
281         IconService.getIcon("mi2_tbopen.gif")) {
282       public void actionPerformed(ActionEvent ae) {
283         openModel();
284       }
285     };
286 
287     saveAction =
288       new AbstractAction("Save...",
289         IconService.getIcon("mi2_tbsave.gif")) {
290       public void actionPerformed(ActionEvent ae) {
291         saveModel();
292       }
293     };
294 
295     startAction =
296       new AbstractAction("Start",
297         IconService.getIcon("mi2_tbrun.gif")) {
298       public void actionPerformed(ActionEvent ae) {
299         startController();
300       }
301     };
302 
303     stopAction = 
304       new AbstractAction("Stop",
305         IconService.getIcon("mi2_tbstop.gif")) {
306       public void actionPerformed(ActionEvent ae) {
307         stopController();
308       }
309     };
310 
311     // add actions to the toolbar
312     int i = 0;
313     toolbar.add(newAction).setToolTipText("New Config");
314     toolbar.add(openAction).setToolTipText("Open Config File...");
315     toolbar.add(saveAction).setToolTipText("Save Config File...");
316     toolbar.addSeparator();
317     toolbar.add(startAction).setToolTipText("Start Monitoring");
318     toolbar.add(stopAction).setToolTipText("Stop Monitoring");
319 
320         // add toolbar keybindings to MarsView window
321         KeyActionSupport kas = new KeyActionSupport();
322         this.addKeyListener(kas);
323 
324         kas.addCommandKey(KeyEvent.VK_N,newAction);
325         kas.addCommandKey(KeyEvent.VK_O,openAction);
326         kas.addCommandKey(KeyEvent.VK_S,saveAction);
327         kas.addCommandKey(KeyEvent.VK_B,startAction);
328         kas.addCommandKey(KeyEvent.VK_E,stopAction);
329 
330     return toolbar;
331   }
332 
333   private void newModel() {
334     // don't chuck current model without giving
335     // the user a chance to save
336     if (!confirmDiscardChanges()) return;
337 
338     // disconnect the old model from the UI
339     Controller controller = Main.getMain().getController();
340     MarsModel oldModel = controller.getModel();
341     // FIXME: clearing listeners in discarded models should be the
342     // job of Main, or the controller.
343     oldModel.clearMarsModelListeners();
344 
345     // create the new model 
346     Main.getMain().newModel();
347     // ...and connect it to the UI
348     MarsModel newModel = controller.getModel();
349     stca.setModel(newModel);
350     stm.setRoot(newModel);
351     newModel.addMarsModelListener(flm);
352 
353     // disable the save button
354     markUnsavedChanges(false);
355   }
356 
357   private void openModel() {
358     // don't chuck current model without giving
359     // the user a chance to save
360     if (!confirmDiscardChanges()) return;
361 
362     // get the file to open
363     File inFile = selectFileOpen();
364     // make sure a file was selected
365     if (inFile == null) return;
366 
367     try {
368       // get the old model so we can disconnect it
369       MarsModel oldModel = Main.getMain().getController().getModel();
370       // try to load the new model
371       Main.getMain().loadConfig(inFile);
372       // that seemed to work. disconnect the old model
373       // FIXME: clearing listeners in discarded models should be the
374       // job of Main, or the controller.
375       oldModel.clearMarsModelListeners();
376       // and connect the new one
377       MarsModel newModel = Main.getMain().getController().getModel();
378       stca.setModel(newModel);
379       stm.setRoot(newModel);
380       newModel.addMarsModelListener(flm);
381       // disable the save button
382       markUnsavedChanges(false);
383     } catch (JDOMException ex) {
384       // report load exception to the user.
385       JOptionPane.showMessageDialog(this,
386         inFile.getName()+" could not be opened.\n"+
387         "The file is not a valid XML file.",
388         "File Open Error",JOptionPane.ERROR_MESSAGE);
389       ex.printStackTrace();
390     } catch (InvalidDocumentException ex) {
391       // report load exception to the user.
392       JOptionPane.showMessageDialog(this,
393         inFile.getName()+" could not be opened.\n"+
394         "The file contains the following error: "+ex.getMessage(),
395         "File Open Error",JOptionPane.ERROR_MESSAGE);
396       ex.printStackTrace();
397     } catch (UnknownHostException ex) {
398       // report load exception to the user.
399       JOptionPane.showMessageDialog(this,
400         inFile.getName()+" could not be opened.\n"+
401         "The host address "+ex.getMessage()+" could not be resolved.\n"+
402         "This may indicate a problem with your domain name servers.",
403         "File Open Error",JOptionPane.ERROR_MESSAGE);
404       ex.printStackTrace();
405     } catch (Exception ex) {
406       // report catch-all load exception to the user
407       JOptionPane.showMessageDialog(this,
408         inFile.getName()+" could not be opened.\n"+
409         "A plugin could not be properly configured\n"+
410         "because an unknown error occurred:\n"+ex.getMessage(),
411         "File Open Error",JOptionPane.ERROR_MESSAGE);
412       ex.printStackTrace();
413     }
414   }
415 
416   private boolean saveModel() {
417     // select a file to save to, default to last save
418     File outFile = selectFileSave();
419     if (outFile == null) return false;
420 
421     try {
422       // write the config out
423       Main.getMain().saveConfig(outFile);
424       // disable the save button
425       markUnsavedChanges(false);
426       // signal success
427       return true;
428     } catch (Exception ex) {
429       JOptionPane.showMessageDialog(this,
430         outFile.getName()+" could not be saved.\n"+
431         "You may not have permission to write the file, or the file\n"+
432         "may be open. Try saving to a different file or directory.",
433         "File Save Error",JOptionPane.ERROR_MESSAGE);
434       ex.printStackTrace();
435       return false;
436     }
437   }
438 
439   private void startController() {
440     startAction.setEnabled(false);
441     stopAction.setEnabled(true);
442     newAction.setEnabled(false);
443     openAction.setEnabled(false);
444     lastSaveActionState = saveAction.isEnabled();
445     saveAction.setEnabled(false);
446     stcms.setEditActionsEnabled(false);
447     Main.getMain().getController().start();
448     // now do necessary UI updates
449     stca.repaintServiceNodes();
450     flm.updateListCache();
451     updateTitleBar();
452   }
453 
454   private void stopController() {
455     stopAction.setEnabled(false);
456     startAction.setEnabled(true);
457     newAction.setEnabled(true);
458     openAction.setEnabled(true);
459     saveAction.setEnabled(lastSaveActionState);
460     stcms.setEditActionsEnabled(true);
461     Main.getMain().getController().stop();
462     // now do necessary UI updates
463     stca.repaintServiceNodes();
464     flm.updateListCache();
465     updateTitleBar();
466   }
467 
468   void markUnsavedChanges(boolean changed) {
469     if (Main.getMain().getController().isActive()) {
470       lastSaveActionState = changed;
471     } else {
472       saveAction.setEnabled(changed);
473     }
474   }
475 
476   boolean hasUnsavedChanges() {
477     if (Main.getMain().getController().isActive()) {
478       return lastSaveActionState;
479     } else {
480       return saveAction.isEnabled();
481     }
482   }
483 
484   private boolean confirmDiscardChanges() {
485     // if the model hasn't changed, assume tacit approval
486     if (!hasUnsavedChanges()) return true;
487 
488     // otherwise, ask the user
489     String changeMsg = "There are unsaved changes to the configuration.\n"+
490               "Do you wish to save them before proceeding?";
491     int confirmed = JOptionPane.showConfirmDialog(this,changeMsg,
492       "Unsaved Changes",JOptionPane.YES_NO_CANCEL_OPTION,
493       JOptionPane.WARNING_MESSAGE);
494     switch (confirmed) {
495       case JOptionPane.YES_OPTION:
496         return saveModel();
497       case JOptionPane.NO_OPTION:
498         return true;
499       case JOptionPane.CANCEL_OPTION:
500       default:
501         return false;
502     }
503   }
504 
505   private File selectFileOpen() {
506     // create a file chooser
507     JFileChooser chooser = new JFileChooser(curDir);
508     // ask for a file to open
509     int option = chooser.showOpenDialog(this);
510     if (option == JFileChooser.APPROVE_OPTION) {
511       // remember selected directory
512       curDir = chooser.getCurrentDirectory();
513       // and return selected file to open
514       return chooser.getSelectedFile();
515     } else {
516       // no file selected, return null
517       return null;
518     }
519   }
520 
521   private File selectFileSave() {
522     // create a file chooser
523     JFileChooser chooser = new JFileChooser(curDir);
524     if (curSaveFile != null) chooser.setSelectedFile(curSaveFile);
525     // overwrite check loop
526     while (true) {
527       int option = chooser.showSaveDialog(this);
528       if (option == JFileChooser.APPROVE_OPTION) {
529         // check for existing selected file
530         if (chooser.getSelectedFile().exists()) {
531           int confirm = JOptionPane.showConfirmDialog(this,
532             "Overwrite existing "+
533             chooser.getSelectedFile().getName()+"?",
534             "Confirm File Overwrite",
535             JOptionPane.YES_NO_OPTION,
536             JOptionPane.WARNING_MESSAGE);
537           if (confirm == JOptionPane.YES_OPTION) {
538             // confirmed overwrite, so no repeat
539             break;
540           }
541         } else {
542           // file doesn't exist, so don't repeat
543           break;
544         }
545       } else {
546         // not approved, so return null
547         return null;
548       }
549     }
550 
551     // remember selected directory and savefile
552     curDir = chooser.getCurrentDirectory();
553     curSaveFile = chooser.getSelectedFile();
554     // return file
555     return curSaveFile;
556   }
557 
558   public void setDefaultSaveFile(File file) {
559     this.curSaveFile = file;
560   }
561 
562   public static class SplashScreen extends JWindow implements StatusView {
563         JTextArea statusArea;
564         
565     public SplashScreen() {
566       super();
567 
568       // get the splash graphic
569       JLabel splashLabel = new JLabel(IconService.getIcon("splash.gif"));
570 
571             // create a status field
572             statusArea = new JTextArea();
573             statusArea.setFont(new Font("SansSerif",Font.PLAIN,10));
574             statusArea.setRows(6);
575             statusArea.setLineWrap(true);
576             statusArea.setWrapStyleWord(true);
577             statusArea.setEditable(false);
578 
579       // add it to the frame
580       getContentPane().setLayout(new BorderLayout());
581             getContentPane().add(splashLabel,BorderLayout.CENTER);
582             getContentPane().add(new JScrollPane(statusArea),BorderLayout.SOUTH);
583 
584       // display it
585       this.pack();
586       this.centerWindow();
587       this.show();
588     }
589 
590         public void showStatus(final String status) {
591             SwingUtilities.invokeLater(new Runnable() {
592                 public void run() {
593                     statusArea.append(status+"\n");
594                     statusArea.setCaretPosition(statusArea.getText().length()-1);
595                 }
596             });
597         }
598   
599     private void centerWindow() {
600       Rectangle virtualBounds = new Rectangle();
601       GraphicsEnvironment ge =
602         GraphicsEnvironment.getLocalGraphicsEnvironment();
603       GraphicsDevice[] gs = ge.getScreenDevices();
604       for (int j=0;j<gs.length;j++) { 
605         GraphicsDevice gd = gs[j];
606         GraphicsConfiguration[] gc = gd.getConfigurations();
607         for (int i=0; i < gc.length; i++) {
608           virtualBounds = virtualBounds.union(gc[i].getBounds());
609         }
610       }
611   
612       int cx = (int) virtualBounds.getWidth()/2;
613       int cy = (int) virtualBounds.getHeight()/2;
614       this.setLocation(cx - this.getWidth()/2,
615                 cy - this.getHeight()/2);
616     }
617   }
618 }