View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.log4j.lf5.viewer;
18  
19  import java.awt.BorderLayout;
20  import java.awt.Color;
21  import java.awt.Component;
22  import java.awt.Dimension;
23  import java.awt.FlowLayout;
24  import java.awt.Font;
25  import java.awt.GraphicsEnvironment;
26  import java.awt.Toolkit;
27  import java.awt.event.ActionEvent;
28  import java.awt.event.ActionListener;
29  import java.awt.event.WindowAdapter;
30  import java.awt.event.WindowEvent;
31  import java.io.File;
32  import java.io.IOException;
33  import java.io.InputStream;
34  import java.net.MalformedURLException;
35  import java.net.URL;
36  import java.util.ArrayList;
37  import java.util.HashMap;
38  import java.util.Iterator;
39  import java.util.List;
40  import java.util.Map;
41  import java.util.StringTokenizer;
42  import java.util.Vector;
43  
44  import javax.swing.BorderFactory;
45  import javax.swing.ImageIcon;
46  import javax.swing.JButton;
47  import javax.swing.JCheckBoxMenuItem;
48  import javax.swing.JColorChooser;
49  import javax.swing.JComboBox;
50  import javax.swing.JFileChooser;
51  import javax.swing.JFrame;
52  import javax.swing.JLabel;
53  import javax.swing.JMenu;
54  import javax.swing.JMenuBar;
55  import javax.swing.JMenuItem;
56  import javax.swing.JOptionPane;
57  import javax.swing.JPanel;
58  import javax.swing.JScrollPane;
59  import javax.swing.JSplitPane;
60  import javax.swing.JTextArea;
61  import javax.swing.JToolBar;
62  import javax.swing.KeyStroke;
63  import javax.swing.SwingUtilities;
64  
65  import org.apache.log4j.lf5.LogLevel;
66  import org.apache.log4j.lf5.LogRecord;
67  import org.apache.log4j.lf5.LogRecordFilter;
68  import org.apache.log4j.lf5.util.DateFormatManager;
69  import org.apache.log4j.lf5.util.LogFileParser;
70  import org.apache.log4j.lf5.viewer.categoryexplorer.CategoryExplorerTree;
71  import org.apache.log4j.lf5.viewer.categoryexplorer.CategoryPath;
72  import org.apache.log4j.lf5.viewer.configure.ConfigurationManager;
73  import org.apache.log4j.lf5.viewer.configure.MRUFileManager;
74  
75  /**
76   * LogBrokerMonitor
77   *.
78   * @author Michael J. Sikorsky
79   * @author Robert Shaw
80   * @author Brad Marlborough
81   * @author Richard Wan
82   * @author Brent Sprecher
83   * @author Richard Hurst
84   */
85  
86  // Contributed by ThoughtWorks Inc.
87  
88  public class LogBrokerMonitor {
89    //--------------------------------------------------------------------------
90    //   Constants:
91    //--------------------------------------------------------------------------
92  
93    public static final String DETAILED_VIEW = "Detailed";
94  //    public static final String STANDARD_VIEW = "Standard";
95  //    public static final String COMPACT_VIEW = "Compact";
96    //--------------------------------------------------------------------------
97    //   Protected Variables:
98    //--------------------------------------------------------------------------
99    protected JFrame _logMonitorFrame;
100   protected int _logMonitorFrameWidth = 550;
101   protected int _logMonitorFrameHeight = 500;
102   protected LogTable _table;
103   protected CategoryExplorerTree _categoryExplorerTree;
104   protected String _searchText;
105   protected String _NDCTextFilter = "";
106   protected LogLevel _leastSevereDisplayedLogLevel = LogLevel.DEBUG;
107 
108   protected JScrollPane _logTableScrollPane;
109   protected JLabel _statusLabel;
110   protected Object _lock = new Object();
111   protected JComboBox _fontSizeCombo;
112 
113   protected int _fontSize = 10;
114   protected String _fontName = "Dialog";
115   protected String _currentView = DETAILED_VIEW;
116 
117   protected boolean _loadSystemFonts = false;
118   protected boolean _trackTableScrollPane = true;
119   protected Dimension _lastTableViewportSize;
120   protected boolean _callSystemExitOnClose = false;
121   protected List _displayedLogBrokerProperties = new Vector();
122 
123   protected Map _logLevelMenuItems = new HashMap();
124   protected Map _logTableColumnMenuItems = new HashMap();
125 
126   protected List _levels = null;
127   protected List _columns = null;
128   protected boolean _isDisposed = false;
129 
130   protected ConfigurationManager _configurationManager = null;
131   protected MRUFileManager _mruFileManager = null;
132   protected File _fileLocation = null;
133 
134   //--------------------------------------------------------------------------
135   //   Private Variables:
136   //--------------------------------------------------------------------------
137 
138   //--------------------------------------------------------------------------
139   //   Constructors:
140   //--------------------------------------------------------------------------
141 
142   /**
143    * Construct a LogBrokerMonitor.
144    */
145   public LogBrokerMonitor(List logLevels) {
146 
147     _levels = logLevels;
148     _columns = LogTableColumn.getLogTableColumns();
149     // This allows us to use the LogBroker in command line tools and
150     // have the option for it to shutdown.
151 
152     String callSystemExitOnClose =
153         System.getProperty("monitor.exit");
154     if (callSystemExitOnClose == null) {
155       callSystemExitOnClose = "false";
156     }
157     callSystemExitOnClose = callSystemExitOnClose.trim().toLowerCase();
158 
159     if (callSystemExitOnClose.equals("true")) {
160       _callSystemExitOnClose = true;
161     }
162 
163     initComponents();
164 
165 
166     _logMonitorFrame.addWindowListener(
167         new LogBrokerMonitorWindowAdaptor(this));
168 
169   }
170 
171   //--------------------------------------------------------------------------
172   //   Public Methods:
173   //--------------------------------------------------------------------------
174 
175   /**
176    * Show the frame for the LogBrokerMonitor. Dispatched to the
177    * swing thread.
178    */
179   public void show(final int delay) {
180     if (_logMonitorFrame.isVisible()) {
181       return;
182     }
183     // This request is very low priority, let other threads execute first.
184     SwingUtilities.invokeLater(new Runnable() {
185       public void run() {
186         Thread.yield();
187         pause(delay);
188         _logMonitorFrame.setVisible(true);
189       }
190     });
191   }
192 
193   public void show() {
194     show(0);
195   }
196 
197   /**
198    * Dispose of the frame for the LogBrokerMonitor.
199    */
200   public void dispose() {
201     _logMonitorFrame.dispose();
202     _isDisposed = true;
203 
204     if (_callSystemExitOnClose == true) {
205       System.exit(0);
206     }
207   }
208 
209   /**
210    * Hide the frame for the LogBrokerMonitor.
211    */
212   public void hide() {
213     _logMonitorFrame.setVisible(false);
214   }
215 
216   /**
217    * Get the DateFormatManager for formatting dates.
218    */
219   public DateFormatManager getDateFormatManager() {
220     return _table.getDateFormatManager();
221   }
222 
223   /**
224    * Set the date format manager for formatting dates.
225    */
226   public void setDateFormatManager(DateFormatManager dfm) {
227     _table.setDateFormatManager(dfm);
228   }
229 
230   /**
231    * Get the value of whether or not System.exit() will be called
232    * when the LogBrokerMonitor is closed.
233    */
234   public boolean getCallSystemExitOnClose() {
235     return _callSystemExitOnClose;
236   }
237 
238   /**
239    * Set the value of whether or not System.exit() will be called
240    * when the LogBrokerMonitor is closed.
241    */
242   public void setCallSystemExitOnClose(boolean callSystemExitOnClose) {
243     _callSystemExitOnClose = callSystemExitOnClose;
244   }
245 
246   /**
247    * Add a log record message to be displayed in the LogTable.
248    * This method is thread-safe as it posts requests to the SwingThread
249    * rather than processing directly.
250    */
251   public void addMessage(final LogRecord lr) {
252     if (_isDisposed == true) {
253       // If the frame has been disposed of, do not log any more
254       // messages.
255       return;
256     }
257 
258     SwingUtilities.invokeLater(new Runnable() {
259       public void run() {
260         _categoryExplorerTree.getExplorerModel().addLogRecord(lr);
261         _table.getFilteredLogTableModel().addLogRecord(lr); // update table
262         updateStatusLabel(); // show updated counts
263       }
264     });
265   }
266 
267   public void setMaxNumberOfLogRecords(int maxNumberOfLogRecords) {
268     _table.getFilteredLogTableModel().setMaxNumberOfLogRecords(maxNumberOfLogRecords);
269   }
270 
271   public JFrame getBaseFrame() {
272     return _logMonitorFrame;
273   }
274 
275   public void setTitle(String title) {
276     _logMonitorFrame.setTitle(title + " - LogFactor5");
277   }
278 
279   public void setFrameSize(int width, int height) {
280     Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
281     if (0 < width && width < screen.width) {
282       _logMonitorFrameWidth = width;
283     }
284     if (0 < height && height < screen.height) {
285       _logMonitorFrameHeight = height;
286     }
287     updateFrameSize();
288   }
289 
290   public void setFontSize(int fontSize) {
291     changeFontSizeCombo(_fontSizeCombo, fontSize);
292     // setFontSizeSilently(actualFontSize); - changeFontSizeCombo fires event
293     // refreshDetailTextArea();
294   }
295 
296   public void addDisplayedProperty(Object messageLine) {
297     _displayedLogBrokerProperties.add(messageLine);
298   }
299 
300   public Map getLogLevelMenuItems() {
301     return _logLevelMenuItems;
302   }
303 
304   public Map getLogTableColumnMenuItems() {
305     return _logTableColumnMenuItems;
306   }
307 
308   public JCheckBoxMenuItem getTableColumnMenuItem(LogTableColumn column) {
309     return getLogTableColumnMenuItem(column);
310   }
311 
312   public CategoryExplorerTree getCategoryExplorerTree() {
313     return _categoryExplorerTree;
314   }
315 
316   // Added in version 1.2 - gets the value of the NDC text filter
317   // This value is set back to null each time the Monitor is initialized.
318   public String getNDCTextFilter() {
319     return _NDCTextFilter;
320   }
321 
322   // Added in version 1.2 - sets the NDC Filter based on
323   // a String passed in by the user.  This value is persisted
324   // in the XML Configuration file.
325   public void setNDCLogRecordFilter(String textFilter) {
326     _table.getFilteredLogTableModel().
327         setLogRecordFilter(createNDCLogRecordFilter(textFilter));
328   }
329   //--------------------------------------------------------------------------
330   //   Protected Methods:
331   //--------------------------------------------------------------------------
332 
333   protected void setSearchText(String text) {
334     _searchText = text;
335   }
336 
337   // Added in version 1.2 - Sets the text filter for the NDC
338   protected void setNDCTextFilter(String text) {
339     // if no value is set, set it to a blank string
340     // otherwise use the value provided
341     if (text == null) {
342       _NDCTextFilter = "";
343     } else {
344       _NDCTextFilter = text;
345     }
346   }
347 
348   // Added in version 1.2 - Uses a different filter that sorts
349   // based on an NDC string passed in by the user.  If the string
350   // is null or is an empty string, we do nothing.
351   protected void sortByNDC() {
352     String text = _NDCTextFilter;
353     if (text == null || text.length() == 0) {
354       return;
355     }
356 
357     // Use new NDC filter
358     _table.getFilteredLogTableModel().
359         setLogRecordFilter(createNDCLogRecordFilter(text));
360   }
361 
362   protected void findSearchText() {
363     String text = _searchText;
364     if (text == null || text.length() == 0) {
365       return;
366     }
367     int startRow = getFirstSelectedRow();
368     int foundRow = findRecord(
369         startRow,
370         text,
371         _table.getFilteredLogTableModel().getFilteredRecords()
372     );
373     selectRow(foundRow);
374   }
375 
376   protected int getFirstSelectedRow() {
377     return _table.getSelectionModel().getMinSelectionIndex();
378   }
379 
380   protected void selectRow(int foundRow) {
381     if (foundRow == -1) {
382       String message = _searchText + " not found.";
383       JOptionPane.showMessageDialog(
384           _logMonitorFrame,
385           message,
386           "Text not found",
387           JOptionPane.INFORMATION_MESSAGE
388       );
389       return;
390     }
391     LF5SwingUtils.selectRow(foundRow, _table, _logTableScrollPane);
392   }
393 
394   protected int findRecord(
395       int startRow,
396       String searchText,
397       List records
398       ) {
399     if (startRow < 0) {
400       startRow = 0; // start at first element if no rows are selected
401     } else {
402       startRow++; // start after the first selected row
403     }
404     int len = records.size();
405 
406     for (int i = startRow; i < len; i++) {
407       if (matches((LogRecord) records.get(i), searchText)) {
408         return i; // found a record
409       }
410     }
411     // wrap around to beginning if when we reach the end with no match
412     len = startRow;
413     for (int i = 0; i < len; i++) {
414       if (matches((LogRecord) records.get(i), searchText)) {
415         return i; // found a record
416       }
417     }
418     // nothing found
419     return -1;
420   }
421 
422   /**
423    * Check to see if the any records contain the search string.
424    * Searching now supports NDC messages and date.
425    */
426   protected boolean matches(LogRecord record, String text) {
427     String message = record.getMessage();
428     String NDC = record.getNDC();
429 
430     if (message == null && NDC == null || text == null) {
431       return false;
432     }
433     if (message.toLowerCase().indexOf(text.toLowerCase()) == -1 &&
434         NDC.toLowerCase().indexOf(text.toLowerCase()) == -1) {
435       return false;
436     }
437 
438     return true;
439   }
440 
441   /**
442    * When the fontsize of a JTextArea is changed, the word-wrapped lines
443    * may become garbled.  This method clears and resets the text of the
444    * text area.
445    */
446   protected void refresh(JTextArea textArea) {
447     String text = textArea.getText();
448     textArea.setText("");
449     textArea.setText(text);
450   }
451 
452   protected void refreshDetailTextArea() {
453     refresh(_table._detailTextArea);
454   }
455 
456   protected void clearDetailTextArea() {
457     _table._detailTextArea.setText("");
458   }
459 
460   /**
461    * Changes the font selection in the combo box and returns the
462    * size actually selected.
463    * @return -1 if unable to select an appropriate font
464    */
465   protected int changeFontSizeCombo(JComboBox box, int requestedSize) {
466     int len = box.getItemCount();
467     int currentValue;
468     Object currentObject;
469     Object selectedObject = box.getItemAt(0);
470     int selectedValue = Integer.parseInt(String.valueOf(selectedObject));
471     for (int i = 0; i < len; i++) {
472       currentObject = box.getItemAt(i);
473       currentValue = Integer.parseInt(String.valueOf(currentObject));
474       if (selectedValue < currentValue && currentValue <= requestedSize) {
475         selectedValue = currentValue;
476         selectedObject = currentObject;
477       }
478     }
479     box.setSelectedItem(selectedObject);
480     return selectedValue;
481   }
482 
483   /**
484    * Does not update gui or cause any events to be fired.
485    */
486   protected void setFontSizeSilently(int fontSize) {
487     _fontSize = fontSize;
488     setFontSize(_table._detailTextArea, fontSize);
489     selectRow(0);
490     setFontSize(_table, fontSize);
491   }
492 
493   protected void setFontSize(Component component, int fontSize) {
494     Font oldFont = component.getFont();
495     Font newFont =
496         new Font(oldFont.getFontName(), oldFont.getStyle(), fontSize);
497     component.setFont(newFont);
498   }
499 
500   protected void updateFrameSize() {
501     _logMonitorFrame.setSize(_logMonitorFrameWidth, _logMonitorFrameHeight);
502     centerFrame(_logMonitorFrame);
503   }
504 
505   protected void pause(int millis) {
506     try {
507       Thread.sleep(millis);
508     } catch (InterruptedException e) {
509 
510     }
511   }
512 
513   protected void initComponents() {
514     //
515     // Configure the Frame.
516     //
517     _logMonitorFrame = new JFrame("LogFactor5");
518 
519     _logMonitorFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
520 
521     String resource =
522         "/org/apache/log4j/lf5/viewer/images/lf5_small_icon.gif";
523     URL lf5IconURL = getClass().getResource(resource);
524 
525     if (lf5IconURL != null) {
526       _logMonitorFrame.setIconImage(new ImageIcon(lf5IconURL).getImage());
527     }
528     updateFrameSize();
529 
530     //
531     // Configure the LogTable.
532     //
533     JTextArea detailTA = createDetailTextArea();
534     JScrollPane detailTAScrollPane = new JScrollPane(detailTA);
535     _table = new LogTable(detailTA);
536     setView(_currentView, _table);
537     _table.setFont(new Font(_fontName, Font.PLAIN, _fontSize));
538     _logTableScrollPane = new JScrollPane(_table);
539 
540     if (_trackTableScrollPane) {
541       _logTableScrollPane.getVerticalScrollBar().addAdjustmentListener(
542           new TrackingAdjustmentListener()
543       );
544     }
545 
546 
547     // Configure the SplitPane between the LogTable & DetailTextArea
548     //
549 
550     JSplitPane tableViewerSplitPane = new JSplitPane();
551     tableViewerSplitPane.setOneTouchExpandable(true);
552     tableViewerSplitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
553     tableViewerSplitPane.setLeftComponent(_logTableScrollPane);
554     tableViewerSplitPane.setRightComponent(detailTAScrollPane);
555     // Make sure to do this last..
556     //tableViewerSplitPane.setDividerLocation(1.0); Doesn't work
557     //the same under 1.2.x & 1.3
558     // "350" is a magic number that provides the correct default
559     // behaviour under 1.2.x & 1.3.  For example, bumping this
560     // number to 400, causes the pane to be completely open in 1.2.x
561     // and closed in 1.3
562     tableViewerSplitPane.setDividerLocation(350);
563 
564     //
565     // Configure the CategoryExplorer
566     //
567 
568     _categoryExplorerTree = new CategoryExplorerTree();
569 
570     _table.getFilteredLogTableModel().setLogRecordFilter(createLogRecordFilter());
571 
572     JScrollPane categoryExplorerTreeScrollPane =
573         new JScrollPane(_categoryExplorerTree);
574     categoryExplorerTreeScrollPane.setPreferredSize(new Dimension(130, 400));
575 
576     // Load most recently used file list
577     _mruFileManager = new MRUFileManager();
578 
579     //
580     // Configure the SplitPane between the CategoryExplorer & (LogTable/Detail)
581     //
582 
583     JSplitPane splitPane = new JSplitPane();
584     splitPane.setOneTouchExpandable(true);
585     splitPane.setRightComponent(tableViewerSplitPane);
586     splitPane.setLeftComponent(categoryExplorerTreeScrollPane);
587     // Do this last.
588     splitPane.setDividerLocation(130);
589     //
590     // Add the MenuBar, StatusArea, CategoryExplorer|LogTable to the
591     // LogMonitorFrame.
592     //
593     _logMonitorFrame.getRootPane().setJMenuBar(createMenuBar());
594     _logMonitorFrame.getContentPane().add(splitPane, BorderLayout.CENTER);
595     _logMonitorFrame.getContentPane().add(createToolBar(),
596         BorderLayout.NORTH);
597     _logMonitorFrame.getContentPane().add(createStatusArea(),
598         BorderLayout.SOUTH);
599 
600     makeLogTableListenToCategoryExplorer();
601     addTableModelProperties();
602 
603     //
604     // Configure ConfigurationManager
605     //
606     _configurationManager = new ConfigurationManager(this, _table);
607 
608   }
609 
610   protected LogRecordFilter createLogRecordFilter() {
611     LogRecordFilter result = new LogRecordFilter() {
612       public boolean passes(LogRecord record) {
613         CategoryPath path = new CategoryPath(record.getCategory());
614         return
615             getMenuItem(record.getLevel()).isSelected() &&
616             _categoryExplorerTree.getExplorerModel().isCategoryPathActive(path);
617       }
618     };
619     return result;
620   }
621 
622   // Added in version 1.2 - Creates a new filter that sorts records based on
623   // an NDC string passed in by the user.
624   protected LogRecordFilter createNDCLogRecordFilter(String text) {
625     _NDCTextFilter = text;
626     LogRecordFilter result = new LogRecordFilter() {
627       public boolean passes(LogRecord record) {
628         String NDC = record.getNDC();
629         CategoryPath path = new CategoryPath(record.getCategory());
630         if (NDC == null || _NDCTextFilter == null) {
631           return false;
632         } else if (NDC.toLowerCase().indexOf(_NDCTextFilter.toLowerCase()) == -1) {
633           return false;
634         } else {
635           return getMenuItem(record.getLevel()).isSelected() &&
636               _categoryExplorerTree.getExplorerModel().isCategoryPathActive(path);
637         }
638       }
639     };
640 
641     return result;
642   }
643 
644 
645   protected void updateStatusLabel() {
646     _statusLabel.setText(getRecordsDisplayedMessage());
647   }
648 
649   protected String getRecordsDisplayedMessage() {
650     FilteredLogTableModel model = _table.getFilteredLogTableModel();
651     return getStatusText(model.getRowCount(), model.getTotalRowCount());
652   }
653 
654   protected void addTableModelProperties() {
655     final FilteredLogTableModel model = _table.getFilteredLogTableModel();
656 
657     addDisplayedProperty(new Object() {
658       public String toString() {
659         return getRecordsDisplayedMessage();
660       }
661     });
662     addDisplayedProperty(new Object() {
663       public String toString() {
664         return "Maximum number of displayed LogRecords: "
665             + model._maxNumberOfLogRecords;
666       }
667     });
668   }
669 
670   protected String getStatusText(int displayedRows, int totalRows) {
671     StringBuffer result = new StringBuffer();
672     result.append("Displaying: ");
673     result.append(displayedRows);
674     result.append(" records out of a total of: ");
675     result.append(totalRows);
676     result.append(" records.");
677     return result.toString();
678   }
679 
680   protected void makeLogTableListenToCategoryExplorer() {
681     ActionListener listener = new ActionListener() {
682       public void actionPerformed(ActionEvent e) {
683         _table.getFilteredLogTableModel().refresh();
684         updateStatusLabel();
685       }
686     };
687     _categoryExplorerTree.getExplorerModel().addActionListener(listener);
688   }
689 
690   protected JPanel createStatusArea() {
691     JPanel statusArea = new JPanel();
692     JLabel status =
693         new JLabel("No log records to display.");
694     _statusLabel = status;
695     status.setHorizontalAlignment(JLabel.LEFT);
696 
697     statusArea.setBorder(BorderFactory.createEtchedBorder());
698     statusArea.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
699     statusArea.add(status);
700 
701     return (statusArea);
702   }
703 
704   protected JTextArea createDetailTextArea() {
705     JTextArea detailTA = new JTextArea();
706     detailTA.setFont(new Font("Monospaced", Font.PLAIN, 14));
707     detailTA.setTabSize(3);
708     detailTA.setLineWrap(true);
709     detailTA.setWrapStyleWord(false);
710     return (detailTA);
711   }
712 
713   protected JMenuBar createMenuBar() {
714     JMenuBar menuBar = new JMenuBar();
715     menuBar.add(createFileMenu());
716     menuBar.add(createEditMenu());
717     menuBar.add(createLogLevelMenu());
718     menuBar.add(createViewMenu());
719     menuBar.add(createConfigureMenu());
720     menuBar.add(createHelpMenu());
721 
722     return (menuBar);
723   }
724 
725   protected JMenu createLogLevelMenu() {
726     JMenu result = new JMenu("Log Level");
727     result.setMnemonic('l');
728     Iterator levels = getLogLevels();
729     while (levels.hasNext()) {
730       result.add(getMenuItem((LogLevel) levels.next()));
731     }
732 
733     result.addSeparator();
734     result.add(createAllLogLevelsMenuItem());
735     result.add(createNoLogLevelsMenuItem());
736     result.addSeparator();
737     result.add(createLogLevelColorMenu());
738     result.add(createResetLogLevelColorMenuItem());
739 
740     return result;
741   }
742 
743   protected JMenuItem createAllLogLevelsMenuItem() {
744     JMenuItem result = new JMenuItem("Show all LogLevels");
745     result.setMnemonic('s');
746     result.addActionListener(new ActionListener() {
747       public void actionPerformed(ActionEvent e) {
748         selectAllLogLevels(true);
749         _table.getFilteredLogTableModel().refresh();
750         updateStatusLabel();
751       }
752     });
753     return result;
754   }
755 
756   protected JMenuItem createNoLogLevelsMenuItem() {
757     JMenuItem result = new JMenuItem("Hide all LogLevels");
758     result.setMnemonic('h');
759     result.addActionListener(new ActionListener() {
760       public void actionPerformed(ActionEvent e) {
761         selectAllLogLevels(false);
762         _table.getFilteredLogTableModel().refresh();
763         updateStatusLabel();
764       }
765     });
766     return result;
767   }
768 
769   protected JMenu createLogLevelColorMenu() {
770     JMenu colorMenu = new JMenu("Configure LogLevel Colors");
771     colorMenu.setMnemonic('c');
772     Iterator levels = getLogLevels();
773     while (levels.hasNext()) {
774       colorMenu.add(createSubMenuItem((LogLevel) levels.next()));
775     }
776 
777     return colorMenu;
778   }
779 
780   protected JMenuItem createResetLogLevelColorMenuItem() {
781     JMenuItem result = new JMenuItem("Reset LogLevel Colors");
782     result.setMnemonic('r');
783     result.addActionListener(new ActionListener() {
784       public void actionPerformed(ActionEvent e) {
785         // reset the level colors in the map
786         LogLevel.resetLogLevelColorMap();
787 
788         // refresh the table
789         _table.getFilteredLogTableModel().refresh();
790       }
791     });
792     return result;
793   }
794 
795   protected void selectAllLogLevels(boolean selected) {
796     Iterator levels = getLogLevels();
797     while (levels.hasNext()) {
798       getMenuItem((LogLevel) levels.next()).setSelected(selected);
799     }
800   }
801 
802   protected JCheckBoxMenuItem getMenuItem(LogLevel level) {
803     JCheckBoxMenuItem result = (JCheckBoxMenuItem) (_logLevelMenuItems.get(level));
804     if (result == null) {
805       result = createMenuItem(level);
806       _logLevelMenuItems.put(level, result);
807     }
808     return result;
809   }
810 
811   protected JMenuItem createSubMenuItem(LogLevel level) {
812     final JMenuItem result = new JMenuItem(level.toString());
813     final LogLevel logLevel = level;
814     result.setMnemonic(level.toString().charAt(0));
815     result.addActionListener(new ActionListener() {
816       public void actionPerformed(ActionEvent e) {
817         showLogLevelColorChangeDialog(result, logLevel);
818       }
819     });
820 
821     return result;
822 
823   }
824 
825   protected void showLogLevelColorChangeDialog(JMenuItem result, LogLevel level) {
826     JMenuItem menuItem = result;
827     Color newColor = JColorChooser.showDialog(
828         _logMonitorFrame,
829         "Choose LogLevel Color",
830         result.getForeground());
831 
832     if (newColor != null) {
833       // set the color for the record
834       level.setLogLevelColorMap(level, newColor);
835       _table.getFilteredLogTableModel().refresh();
836     }
837 
838   }
839 
840   protected JCheckBoxMenuItem createMenuItem(LogLevel level) {
841     JCheckBoxMenuItem result = new JCheckBoxMenuItem(level.toString());
842     result.setSelected(true);
843     result.setMnemonic(level.toString().charAt(0));
844     result.addActionListener(new ActionListener() {
845       public void actionPerformed(ActionEvent e) {
846         _table.getFilteredLogTableModel().refresh();
847         updateStatusLabel();
848       }
849     });
850     return result;
851   }
852 
853   // view menu
854   protected JMenu createViewMenu() {
855     JMenu result = new JMenu("View");
856     result.setMnemonic('v');
857     Iterator columns = getLogTableColumns();
858     while (columns.hasNext()) {
859       result.add(getLogTableColumnMenuItem((LogTableColumn) columns.next()));
860     }
861 
862     result.addSeparator();
863     result.add(createAllLogTableColumnsMenuItem());
864     result.add(createNoLogTableColumnsMenuItem());
865     return result;
866   }
867 
868   protected JCheckBoxMenuItem getLogTableColumnMenuItem(LogTableColumn column) {
869     JCheckBoxMenuItem result = (JCheckBoxMenuItem) (_logTableColumnMenuItems.get(column));
870     if (result == null) {
871       result = createLogTableColumnMenuItem(column);
872       _logTableColumnMenuItems.put(column, result);
873     }
874     return result;
875   }
876 
877   protected JCheckBoxMenuItem createLogTableColumnMenuItem(LogTableColumn column) {
878     JCheckBoxMenuItem result = new JCheckBoxMenuItem(column.toString());
879 
880     result.setSelected(true);
881     result.setMnemonic(column.toString().charAt(0));
882     result.addActionListener(new ActionListener() {
883       public void actionPerformed(ActionEvent e) {
884         // update list of columns and reset the view
885         List selectedColumns = updateView();
886         _table.setView(selectedColumns);
887       }
888     });
889     return result;
890   }
891 
892   protected List updateView() {
893     ArrayList updatedList = new ArrayList();
894     Iterator columnIterator = _columns.iterator();
895     while (columnIterator.hasNext()) {
896       LogTableColumn column = (LogTableColumn) columnIterator.next();
897       JCheckBoxMenuItem result = getLogTableColumnMenuItem(column);
898       // check and see if the checkbox is checked
899       if (result.isSelected()) {
900         updatedList.add(column);
901       }
902     }
903 
904     return updatedList;
905   }
906 
907   protected JMenuItem createAllLogTableColumnsMenuItem() {
908     JMenuItem result = new JMenuItem("Show all Columns");
909     result.setMnemonic('s');
910     result.addActionListener(new ActionListener() {
911       public void actionPerformed(ActionEvent e) {
912         selectAllLogTableColumns(true);
913         // update list of columns and reset the view
914         List selectedColumns = updateView();
915         _table.setView(selectedColumns);
916       }
917     });
918     return result;
919   }
920 
921   protected JMenuItem createNoLogTableColumnsMenuItem() {
922     JMenuItem result = new JMenuItem("Hide all Columns");
923     result.setMnemonic('h');
924     result.addActionListener(new ActionListener() {
925       public void actionPerformed(ActionEvent e) {
926         selectAllLogTableColumns(false);
927         // update list of columns and reset the view
928         List selectedColumns = updateView();
929         _table.setView(selectedColumns);
930       }
931     });
932     return result;
933   }
934 
935   protected void selectAllLogTableColumns(boolean selected) {
936     Iterator columns = getLogTableColumns();
937     while (columns.hasNext()) {
938       getLogTableColumnMenuItem((LogTableColumn) columns.next()).setSelected(selected);
939     }
940   }
941 
942   protected JMenu createFileMenu() {
943     JMenu fileMenu = new JMenu("File");
944     fileMenu.setMnemonic('f');
945     JMenuItem exitMI;
946     fileMenu.add(createOpenMI());
947     fileMenu.add(createOpenURLMI());
948     fileMenu.addSeparator();
949     fileMenu.add(createCloseMI());
950     createMRUFileListMI(fileMenu);
951     fileMenu.addSeparator();
952     fileMenu.add(createExitMI());
953     return fileMenu;
954   }
955 
956   /**
957    * Menu item added to allow log files to be opened with
958    * the LF5 GUI.
959    */
960   protected JMenuItem createOpenMI() {
961     JMenuItem result = new JMenuItem("Open...");
962     result.setMnemonic('o');
963     result.addActionListener(new ActionListener() {
964       public void actionPerformed(ActionEvent e) {
965         requestOpen();
966       }
967     });
968     return result;
969   }
970 
971   /**
972    * Menu item added to allow log files loaded from a URL
973    * to be opened by the LF5 GUI.
974    */
975   protected JMenuItem createOpenURLMI() {
976     JMenuItem result = new JMenuItem("Open URL...");
977     result.setMnemonic('u');
978     result.addActionListener(new ActionListener() {
979       public void actionPerformed(ActionEvent e) {
980         requestOpenURL();
981       }
982     });
983     return result;
984   }
985 
986   protected JMenuItem createCloseMI() {
987     JMenuItem result = new JMenuItem("Close");
988     result.setMnemonic('c');
989     result.setAccelerator(KeyStroke.getKeyStroke("control Q"));
990     result.addActionListener(new ActionListener() {
991       public void actionPerformed(ActionEvent e) {
992         requestClose();
993       }
994     });
995     return result;
996   }
997 
998   /**
999    * Creates a Most Recently Used file list to be
1000    * displayed in the File menu
1001    */
1002   protected void createMRUFileListMI(JMenu menu) {
1003 
1004     String[] files = _mruFileManager.getMRUFileList();
1005 
1006     if (files != null) {
1007       menu.addSeparator();
1008       for (int i = 0; i < files.length; i++) {
1009         JMenuItem result = new JMenuItem((i + 1) + " " + files[i]);
1010         result.setMnemonic(i + 1);
1011         result.addActionListener(new ActionListener() {
1012           public void actionPerformed(ActionEvent e) {
1013             requestOpenMRU(e);
1014           }
1015         });
1016         menu.add(result);
1017       }
1018     }
1019   }
1020 
1021   protected JMenuItem createExitMI() {
1022     JMenuItem result = new JMenuItem("Exit");
1023     result.setMnemonic('x');
1024     result.addActionListener(new ActionListener() {
1025       public void actionPerformed(ActionEvent e) {
1026         requestExit();
1027       }
1028     });
1029     return result;
1030   }
1031 
1032   protected JMenu createConfigureMenu() {
1033     JMenu configureMenu = new JMenu("Configure");
1034     configureMenu.setMnemonic('c');
1035     configureMenu.add(createConfigureSave());
1036     configureMenu.add(createConfigureReset());
1037     configureMenu.add(createConfigureMaxRecords());
1038 
1039     return configureMenu;
1040   }
1041 
1042   protected JMenuItem createConfigureSave() {
1043     JMenuItem result = new JMenuItem("Save");
1044     result.setMnemonic('s');
1045     result.addActionListener(new ActionListener() {
1046       public void actionPerformed(ActionEvent e) {
1047         saveConfiguration();
1048       }
1049     });
1050 
1051     return result;
1052   }
1053 
1054   protected JMenuItem createConfigureReset() {
1055     JMenuItem result = new JMenuItem("Reset");
1056     result.setMnemonic('r');
1057     result.addActionListener(new ActionListener() {
1058       public void actionPerformed(ActionEvent e) {
1059         resetConfiguration();
1060       }
1061     });
1062 
1063     return result;
1064   }
1065 
1066   protected JMenuItem createConfigureMaxRecords() {
1067     JMenuItem result = new JMenuItem("Set Max Number of Records");
1068     result.setMnemonic('m');
1069     result.addActionListener(new ActionListener() {
1070       public void actionPerformed(ActionEvent e) {
1071         setMaxRecordConfiguration();
1072       }
1073     });
1074 
1075     return result;
1076   }
1077 
1078 
1079   protected void saveConfiguration() {
1080     _configurationManager.save();
1081   }
1082 
1083   protected void resetConfiguration() {
1084     _configurationManager.reset();
1085   }
1086 
1087   protected void setMaxRecordConfiguration() {
1088     LogFactor5InputDialog inputDialog = new LogFactor5InputDialog(
1089         getBaseFrame(), "Set Max Number of Records", "", 10);
1090 
1091     String temp = inputDialog.getText();
1092 
1093     if (temp != null) {
1094       try {
1095         setMaxNumberOfLogRecords(Integer.parseInt(temp));
1096       } catch (NumberFormatException e) {
1097         LogFactor5ErrorDialog error = new LogFactor5ErrorDialog(
1098             getBaseFrame(),
1099             "'" + temp + "' is an invalid parameter.\nPlease try again.");
1100         setMaxRecordConfiguration();
1101       }
1102     }
1103   }
1104 
1105 
1106   protected JMenu createHelpMenu() {
1107     JMenu helpMenu = new JMenu("Help");
1108     helpMenu.setMnemonic('h');
1109     helpMenu.add(createHelpProperties());
1110     return helpMenu;
1111   }
1112 
1113   protected JMenuItem createHelpProperties() {
1114     final String title = "LogFactor5 Properties";
1115     final JMenuItem result = new JMenuItem(title);
1116     result.setMnemonic('l');
1117     result.addActionListener(new ActionListener() {
1118       public void actionPerformed(ActionEvent e) {
1119         showPropertiesDialog(title);
1120       }
1121     });
1122     return result;
1123   }
1124 
1125   protected void showPropertiesDialog(String title) {
1126     JOptionPane.showMessageDialog(
1127         _logMonitorFrame,
1128         _displayedLogBrokerProperties.toArray(),
1129         title,
1130         JOptionPane.PLAIN_MESSAGE
1131     );
1132   }
1133 
1134   protected JMenu createEditMenu() {
1135     JMenu editMenu = new JMenu("Edit");
1136     editMenu.setMnemonic('e');
1137     editMenu.add(createEditFindMI());
1138     editMenu.add(createEditFindNextMI());
1139     editMenu.addSeparator();
1140     editMenu.add(createEditSortNDCMI());
1141     editMenu.add(createEditRestoreAllNDCMI());
1142     return editMenu;
1143   }
1144 
1145   protected JMenuItem createEditFindNextMI() {
1146     JMenuItem editFindNextMI = new JMenuItem("Find Next");
1147     editFindNextMI.setMnemonic('n');
1148     editFindNextMI.setAccelerator(KeyStroke.getKeyStroke("F3"));
1149     editFindNextMI.addActionListener(new ActionListener() {
1150       public void actionPerformed(ActionEvent e) {
1151         findSearchText();
1152       }
1153     });
1154     return editFindNextMI;
1155   }
1156 
1157   protected JMenuItem createEditFindMI() {
1158     JMenuItem editFindMI = new JMenuItem("Find");
1159     editFindMI.setMnemonic('f');
1160     editFindMI.setAccelerator(KeyStroke.getKeyStroke("control F"));
1161 
1162     editFindMI.addActionListener(
1163         new ActionListener() {
1164           public void actionPerformed(ActionEvent e) {
1165             String inputValue =
1166                 JOptionPane.showInputDialog(
1167                     _logMonitorFrame,
1168                     "Find text: ",
1169                     "Search Record Messages",
1170                     JOptionPane.QUESTION_MESSAGE
1171                 );
1172             setSearchText(inputValue);
1173             findSearchText();
1174           }
1175         }
1176 
1177     );
1178     return editFindMI;
1179   }
1180 
1181   // Added version 1.2 - Allows users to Sort Log Records by an
1182   // NDC text filter. A new LogRecordFilter was created to
1183   // sort the records.
1184   protected JMenuItem createEditSortNDCMI() {
1185     JMenuItem editSortNDCMI = new JMenuItem("Sort by NDC");
1186     editSortNDCMI.setMnemonic('s');
1187     editSortNDCMI.addActionListener(
1188         new ActionListener() {
1189           public void actionPerformed(ActionEvent e) {
1190             String inputValue =
1191                 JOptionPane.showInputDialog(
1192                     _logMonitorFrame,
1193                     "Sort by this NDC: ",
1194                     "Sort Log Records by NDC",
1195                     JOptionPane.QUESTION_MESSAGE
1196                 );
1197             setNDCTextFilter(inputValue);
1198             sortByNDC();
1199             _table.getFilteredLogTableModel().refresh();
1200             updateStatusLabel();
1201           }
1202         }
1203 
1204     );
1205     return editSortNDCMI;
1206   }
1207 
1208   // Added in version 1.2 - Resets the LogRecordFilter back to default
1209   // filter.
1210   protected JMenuItem createEditRestoreAllNDCMI() {
1211     JMenuItem editRestoreAllNDCMI = new JMenuItem("Restore all NDCs");
1212     editRestoreAllNDCMI.setMnemonic('r');
1213     editRestoreAllNDCMI.addActionListener(
1214         new ActionListener() {
1215           public void actionPerformed(ActionEvent e) {
1216             _table.getFilteredLogTableModel().setLogRecordFilter(createLogRecordFilter());
1217             // reset the text filter
1218             setNDCTextFilter("");
1219             _table.getFilteredLogTableModel().refresh();
1220             updateStatusLabel();
1221           }
1222         }
1223     );
1224     return editRestoreAllNDCMI;
1225   }
1226 
1227   protected JToolBar createToolBar() {
1228     JToolBar tb = new JToolBar();
1229     tb.putClientProperty("JToolBar.isRollover", Boolean.TRUE);
1230     JComboBox fontCombo = new JComboBox();
1231     JComboBox fontSizeCombo = new JComboBox();
1232     _fontSizeCombo = fontSizeCombo;
1233 
1234     ClassLoader cl = this.getClass().getClassLoader();
1235     if(cl == null) {
1236         cl = ClassLoader.getSystemClassLoader();
1237     }
1238     URL newIconURL = cl.getResource("org/apache/log4j/lf5/viewer/" +
1239         "images/channelexplorer_new.gif");
1240 
1241     ImageIcon newIcon = null;
1242 
1243     if (newIconURL != null) {
1244       newIcon = new ImageIcon(newIconURL);
1245     }
1246 
1247     JButton newButton = new JButton("Clear Log Table");
1248 
1249     if (newIcon != null) {
1250       newButton.setIcon(newIcon);
1251     }
1252 
1253     newButton.setToolTipText("Clear Log Table.");
1254     //newButton.setBorder(BorderFactory.createEtchedBorder());
1255 
1256     newButton.addActionListener(
1257         new ActionListener() {
1258           public void actionPerformed(ActionEvent e) {
1259             _table.clearLogRecords();
1260             _categoryExplorerTree.getExplorerModel().resetAllNodeCounts();
1261             updateStatusLabel();
1262             clearDetailTextArea();
1263             LogRecord.resetSequenceNumber();
1264           }
1265         }
1266     );
1267 
1268     Toolkit tk = Toolkit.getDefaultToolkit();
1269     // This will actually grab all the fonts
1270 
1271     String[] fonts;
1272 
1273     if (_loadSystemFonts) {
1274       fonts = GraphicsEnvironment.
1275           getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
1276     } else {
1277       fonts = tk.getFontList();
1278     }
1279 
1280     for (int j = 0; j < fonts.length; j++) {
1281       fontCombo.addItem(fonts[j]);
1282     }
1283 
1284     fontCombo.setSelectedItem(_fontName);
1285 
1286     fontCombo.addActionListener(
1287 
1288         new ActionListener() {
1289           public void actionPerformed(ActionEvent e) {
1290             JComboBox box = (JComboBox) e.getSource();
1291             String font = (String) box.getSelectedItem();
1292             _table.setFont(new Font(font, Font.PLAIN, _fontSize));
1293             _fontName = font;
1294           }
1295         }
1296     );
1297 
1298     fontSizeCombo.addItem("8");
1299     fontSizeCombo.addItem("9");
1300     fontSizeCombo.addItem("10");
1301     fontSizeCombo.addItem("12");
1302     fontSizeCombo.addItem("14");
1303     fontSizeCombo.addItem("16");
1304     fontSizeCombo.addItem("18");
1305     fontSizeCombo.addItem("24");
1306 
1307     fontSizeCombo.setSelectedItem(String.valueOf(_fontSize));
1308     fontSizeCombo.addActionListener(
1309         new ActionListener() {
1310           public void actionPerformed(ActionEvent e) {
1311             JComboBox box = (JComboBox) e.getSource();
1312             String size = (String) box.getSelectedItem();
1313             int s = Integer.valueOf(size).intValue();
1314 
1315             setFontSizeSilently(s);
1316             refreshDetailTextArea();
1317             _fontSize = s;
1318           }
1319         }
1320     );
1321 
1322     tb.add(new JLabel(" Font: "));
1323     tb.add(fontCombo);
1324     tb.add(fontSizeCombo);
1325     tb.addSeparator();
1326     tb.addSeparator();
1327     tb.add(newButton);
1328 
1329     newButton.setAlignmentY(0.5f);
1330     newButton.setAlignmentX(0.5f);
1331 
1332     fontCombo.setMaximumSize(fontCombo.getPreferredSize());
1333     fontSizeCombo.setMaximumSize(
1334         fontSizeCombo.getPreferredSize());
1335 
1336     return (tb);
1337   }
1338 
1339 //    protected void setView(String viewString, LogTable table) {
1340 //        if (STANDARD_VIEW.equals(viewString)) {
1341 //            table.setStandardView();
1342 //        } else if (COMPACT_VIEW.equals(viewString)) {
1343 //            table.setCompactView();
1344 //        } else if (DETAILED_VIEW.equals(viewString)) {
1345 //            table.setDetailedView();
1346 //        } else {
1347 //            String message = viewString + "does not match a supported view.";
1348 //            throw new IllegalArgumentException(message);
1349 //        }
1350 //        _currentView = viewString;
1351 //    }
1352 
1353   protected void setView(String viewString, LogTable table) {
1354     if (DETAILED_VIEW.equals(viewString)) {
1355       table.setDetailedView();
1356     } else {
1357       String message = viewString + "does not match a supported view.";
1358       throw new IllegalArgumentException(message);
1359     }
1360     _currentView = viewString;
1361   }
1362 
1363   protected JComboBox createLogLevelCombo() {
1364     JComboBox result = new JComboBox();
1365     Iterator levels = getLogLevels();
1366     while (levels.hasNext()) {
1367       result.addItem(levels.next());
1368     }
1369     result.setSelectedItem(_leastSevereDisplayedLogLevel);
1370 
1371     result.addActionListener(new ActionListener() {
1372       public void actionPerformed(ActionEvent e) {
1373         JComboBox box = (JComboBox) e.getSource();
1374         LogLevel level = (LogLevel) box.getSelectedItem();
1375         setLeastSevereDisplayedLogLevel(level);
1376       }
1377     });
1378     result.setMaximumSize(result.getPreferredSize());
1379     return result;
1380   }
1381 
1382   protected void setLeastSevereDisplayedLogLevel(LogLevel level) {
1383     if (level == null || _leastSevereDisplayedLogLevel == level) {
1384       return; // nothing to do
1385     }
1386     _leastSevereDisplayedLogLevel = level;
1387     _table.getFilteredLogTableModel().refresh();
1388     updateStatusLabel();
1389   }
1390 
1391   /**
1392    * Ensures that the Table's ScrollPane Viewport will "track" with updates
1393    * to the Table.  When the vertical scroll bar is at its bottom anchor
1394    * and tracking is enabled then viewport will stay at the bottom most
1395    * point of the component.  The purpose of this feature is to allow
1396    * a developer to watch the table as messages arrive and not have to
1397    * scroll after each new message arrives.  When the vertical scroll bar
1398    * is at any other location, then no tracking will happen.
1399    * @deprecated tracking is now done automatically.
1400    */
1401   protected void trackTableScrollPane() {
1402     // do nothing
1403   }
1404 
1405   protected void centerFrame(JFrame frame) {
1406     Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1407     Dimension comp = frame.getSize();
1408 
1409     frame.setLocation(((screen.width - comp.width) / 2),
1410         ((screen.height - comp.height) / 2));
1411 
1412   }
1413 
1414   /**
1415    * Uses a JFileChooser to select a file to opened with the
1416    * LF5 GUI.
1417    */
1418   protected void requestOpen() {
1419     JFileChooser chooser;
1420 
1421     if (_fileLocation == null) {
1422       chooser = new JFileChooser();
1423     } else {
1424       chooser = new JFileChooser(_fileLocation);
1425     }
1426 
1427     int returnVal = chooser.showOpenDialog(_logMonitorFrame);
1428     if (returnVal == JFileChooser.APPROVE_OPTION) {
1429       File f = chooser.getSelectedFile();
1430       if (loadLogFile(f)) {
1431         _fileLocation = chooser.getSelectedFile();
1432         _mruFileManager.set(f);
1433         updateMRUList();
1434       }
1435     }
1436   }
1437 
1438   /**
1439    * Uses a Dialog box to accept a URL to a file to be opened
1440    * with the LF5 GUI.
1441    */
1442   protected void requestOpenURL() {
1443     LogFactor5InputDialog inputDialog = new LogFactor5InputDialog(
1444         getBaseFrame(), "Open URL", "URL:");
1445     String temp = inputDialog.getText();
1446 
1447     if (temp != null) {
1448       if (temp.indexOf("://") == -1) {
1449         temp = "http://" + temp;
1450       }
1451 
1452       try {
1453         URL url = new URL(temp);
1454         if (loadLogFile(url)) {
1455           _mruFileManager.set(url);
1456           updateMRUList();
1457         }
1458       } catch (MalformedURLException e) {
1459         LogFactor5ErrorDialog error = new LogFactor5ErrorDialog(
1460             getBaseFrame(), "Error reading URL.");
1461       }
1462     }
1463   }
1464 
1465   /**
1466    * Removes old file list and creates a new file list
1467    * with the updated MRU list.
1468    */
1469   protected void updateMRUList() {
1470     JMenu menu = _logMonitorFrame.getJMenuBar().getMenu(0);
1471     menu.removeAll();
1472     menu.add(createOpenMI());
1473     menu.add(createOpenURLMI());
1474     menu.addSeparator();
1475     menu.add(createCloseMI());
1476     createMRUFileListMI(menu);
1477     menu.addSeparator();
1478     menu.add(createExitMI());
1479   }
1480 
1481   protected void requestClose() {
1482     setCallSystemExitOnClose(false);
1483     closeAfterConfirm();
1484   }
1485 
1486   /**
1487    * Opens a file in the MRU list.
1488    */
1489   protected void requestOpenMRU(ActionEvent e) {
1490     String file = e.getActionCommand();
1491     StringTokenizer st = new StringTokenizer(file);
1492     String num = st.nextToken().trim();
1493     file = st.nextToken("\n");
1494 
1495     try {
1496       int index = Integer.parseInt(num) - 1;
1497 
1498       InputStream in = _mruFileManager.getInputStream(index);
1499       LogFileParser lfp = new LogFileParser(in);
1500       lfp.parse(this);
1501 
1502       _mruFileManager.moveToTop(index);
1503       updateMRUList();
1504 
1505     } catch (Exception me) {
1506       LogFactor5ErrorDialog error = new LogFactor5ErrorDialog(
1507           getBaseFrame(), "Unable to load file " + file);
1508     }
1509 
1510   }
1511 
1512   protected void requestExit() {
1513     _mruFileManager.save();
1514     setCallSystemExitOnClose(true);
1515     closeAfterConfirm();
1516   }
1517 
1518   protected void closeAfterConfirm() {
1519     StringBuffer message = new StringBuffer();
1520 
1521     if (_callSystemExitOnClose == false) {
1522       message.append("Are you sure you want to close the logging ");
1523       message.append("console?\n");
1524       message.append("(Note: This will not shut down the Virtual Machine,\n");
1525       message.append("or the Swing event thread.)");
1526     } else {
1527       message.append("Are you sure you want to exit?\n");
1528       message.append("This will shut down the Virtual Machine.\n");
1529     }
1530 
1531     String title =
1532         "Are you sure you want to dispose of the Logging Console?";
1533 
1534     if (_callSystemExitOnClose == true) {
1535       title = "Are you sure you want to exit?";
1536     }
1537     int value = JOptionPane.showConfirmDialog(
1538         _logMonitorFrame,
1539         message.toString(),
1540         title,
1541         JOptionPane.OK_CANCEL_OPTION,
1542         JOptionPane.QUESTION_MESSAGE,
1543         null
1544     );
1545 
1546     if (value == JOptionPane.OK_OPTION) {
1547       dispose();
1548     }
1549   }
1550 
1551   protected Iterator getLogLevels() {
1552     return _levels.iterator();
1553   }
1554 
1555   protected Iterator getLogTableColumns() {
1556     return _columns.iterator();
1557   }
1558 
1559   /**
1560    * Loads and parses a log file.
1561    */
1562   protected boolean loadLogFile(File file) {
1563     boolean ok = false;
1564     try {
1565       LogFileParser lfp = new LogFileParser(file);
1566       lfp.parse(this);
1567       ok = true;
1568     } catch (IOException e) {
1569       LogFactor5ErrorDialog error = new LogFactor5ErrorDialog(
1570           getBaseFrame(), "Error reading " + file.getName());
1571     }
1572 
1573     return ok;
1574   }
1575 
1576   /**
1577    * Loads a parses a log file running on a server.
1578    */
1579   protected boolean loadLogFile(URL url) {
1580     boolean ok = false;
1581     try {
1582       LogFileParser lfp = new LogFileParser(url.openStream());
1583       lfp.parse(this);
1584       ok = true;
1585     } catch (IOException e) {
1586       LogFactor5ErrorDialog error = new LogFactor5ErrorDialog(
1587           getBaseFrame(), "Error reading URL:" + url.getFile());
1588     }
1589     return ok;
1590   }
1591   //--------------------------------------------------------------------------
1592   //   Private Methods:
1593   //--------------------------------------------------------------------------
1594 
1595   //--------------------------------------------------------------------------
1596   //   Nested Top-Level Classes or Interfaces:
1597   //--------------------------------------------------------------------------
1598 
1599   class LogBrokerMonitorWindowAdaptor extends WindowAdapter {
1600     protected LogBrokerMonitor _monitor;
1601 
1602     public LogBrokerMonitorWindowAdaptor(LogBrokerMonitor monitor) {
1603       _monitor = monitor;
1604     }
1605 
1606     public void windowClosing(WindowEvent ev) {
1607       _monitor.requestClose();
1608     }
1609   }
1610 }
1611 
1612