001: package net.sourceforge.squirrel_sql.client.gui;
002:
003: /*
004: * Copyright (C) 2002-2006 Colin Bell
005: * colbell@users.sourceforge.net
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: */
021: import java.awt.BorderLayout;
022: import java.awt.Container;
023: import java.awt.event.*;
024: import java.io.BufferedReader;
025: import java.io.File;
026: import java.io.FileReader;
027: import java.io.FilenameFilter;
028:
029: import javax.swing.*;
030:
031: import net.sourceforge.squirrel_sql.client.IApplication;
032: import net.sourceforge.squirrel_sql.client.preferences.SquirrelPreferences;
033: import net.sourceforge.squirrel_sql.client.util.ApplicationFiles;
034: import net.sourceforge.squirrel_sql.fw.gui.CursorChanger;
035: import net.sourceforge.squirrel_sql.fw.gui.DirectoryListComboBox;
036: import net.sourceforge.squirrel_sql.fw.gui.GUIUtils;
037: import net.sourceforge.squirrel_sql.fw.gui.TextPopupMenu;
038: import net.sourceforge.squirrel_sql.fw.gui.ToolBar;
039: import net.sourceforge.squirrel_sql.fw.util.StringManager;
040: import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;
041: import net.sourceforge.squirrel_sql.fw.util.Utilities;
042: import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
043: import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;
044:
045: /**
046: * This sheet shows the SQuirreL log files.
047: *
048: * @author <A HREF="mailto:colbell@users.sourceforge.net">Colin Bell</A>
049: */
050: public class ViewLogsSheet extends BaseInternalFrame {
051: /** Internationalized strings for this class. */
052: private static final StringManager s_stringMgr = StringManagerFactory
053: .getStringManager(ViewLogsSheet.class);
054:
055: /** Logger for this class. */
056: private static final ILogger s_log = LoggerController
057: .createLogger(ViewLogsSheet.class);
058:
059: /** Singleton instance of this class. */
060: private static ViewLogsSheet s_instance;
061:
062: /** Application API. */
063: private final IApplication _app;
064:
065: /** Preferences */
066: private final SquirrelPreferences _prefs;
067:
068: /** Combo box containing all the log files. */
069: private final LogsComboBox _logDirCmb = new LogsComboBox();
070:
071: /** Text area containing the log contents. */
072: private final JTextArea _logContentsTxt = new JTextArea(20, 50);
073:
074: /** Button that refreshes the log contents. */
075: private final JButton _refreshBtn = new JButton(s_stringMgr
076: .getString("ViewLogsSheet.refresh"));
077:
078: private final JCheckBox _errorChkbox = new JCheckBox("Errors");
079: private final JCheckBox _debugChkbox = new JCheckBox("Debug");
080: private final JCheckBox _infoChkbox = new JCheckBox("Info");
081:
082: /** Directory containing the log files. */
083: private final File _logDir;
084:
085: /** If <TT>true</TT> user is closing this window. */
086: private boolean _closing = false;
087:
088: /** If <TT>true</TT> log is being refreshed. */
089: private boolean _refreshing = false;
090:
091: /**
092: * Ctor specifying the application API.
093: *
094: * @param app Application API.
095: *
096: * @throws IllegalArgumentException
097: * Thrown if a <TT>null</TT> <TT>IApplication passed.
098: */
099: private ViewLogsSheet(IApplication app) {
100: super (s_stringMgr.getString("ViewLogsSheet.title"), true, true,
101: true, true);
102: if (app == null) {
103: throw new IllegalArgumentException("IApplication == null");
104: }
105:
106: _app = app;
107: _prefs = _app.getSquirrelPreferences();
108: _logDir = new ApplicationFiles().getExecutionLogFile()
109: .getParentFile();
110: createUserInterface();
111: }
112:
113: /**
114: * Show this window
115: *
116: * @param app Application API.
117: *
118: * @throws IllegalArgumentException
119: * Thrown if a <TT>null</TT> <TT>IApplication</TT> object passed.
120: */
121: public static synchronized void showSheet(IApplication app) {
122: if (s_instance == null) {
123: s_instance = new ViewLogsSheet(app);
124: app.getMainFrame().addInternalFrame(s_instance, true, null);
125: GUIUtils.centerWithinDesktop(s_instance);
126: }
127:
128: final boolean wasVisible = s_instance.isVisible();
129: if (!wasVisible) {
130: s_instance.setVisible(true);
131: }
132: s_instance.moveToFront();
133: if (!wasVisible && !s_instance._refreshing) {
134: s_instance.startRefreshingLog();
135: }
136: }
137:
138: public void dispose() {
139: // Stop refresh if it is running.
140: _closing = true;
141:
142: synchronized (getClass()) {
143: s_instance = null;
144: }
145: super .dispose();
146: }
147:
148: /**
149: * Close this sheet.
150: */
151: private void performClose() {
152: dispose();
153: }
154:
155: /**
156: * Start a thread to refrsh the log.
157: */
158: private synchronized void startRefreshingLog() {
159: if (!_refreshing) {
160: _app.getThreadPool().addTask(new Refresher());
161: }
162: }
163:
164: /**
165: * Enables the log combo box and refresh button using the Swing event
166: * thread.
167: */
168: private void enableComponents(final boolean enabled) {
169: GUIUtils.processOnSwingEventThread(new Runnable() {
170: public void run() {
171: _refreshBtn.setEnabled(enabled);
172: _logDirCmb.setEnabled(enabled);
173: }
174: });
175: }
176:
177: /**
178: * Refresh the log.
179: */
180: private void refreshLog() {
181: enableComponents(false);
182: CursorChanger cursorChg = new CursorChanger(this );
183: cursorChg.show();
184: try {
185: try {
186: SwingUtilities.invokeAndWait(new Runnable() {
187: public void run() {
188: _logContentsTxt.setText("");
189: }
190: });
191: } catch (Exception ex) {
192: // i18n[ViewLogsSheet.error.clearlogcontents=Error clearing the log contents]
193: s_log
194: .error(
195: s_stringMgr
196: .getString("ViewLogsSheet.error.clearlogcontents"),
197: ex);
198: }
199: final File logFile = (File) _logDirCmb.getSelectedItem();
200: if (logFile != null) {
201: try {
202: if (logFile.exists() && logFile.canRead()) {
203: final BufferedReader rdr = new BufferedReader(
204: new FileReader(logFile));
205: try {
206: String line = null;
207: StringBuffer chunk = new StringBuffer(16384);
208: while ((line = rdr.readLine()) != null) {
209: if (_closing) {
210: return;
211: }
212:
213: if (chunk.length() > 16000) {
214: final String finalLine = chunk
215: .toString();
216: SwingUtilities
217: .invokeAndWait(new Runnable() {
218: public void run() {
219: if (!_closing) {
220: _logContentsTxt
221: .append(finalLine);
222: }
223: }
224: });
225: chunk = new StringBuffer(16384);
226: } else {
227: if (shouldAppendLineToChunk(line)) {
228: chunk.append(line).append('\n');
229: }
230: }
231: }
232:
233: if (_closing) {
234: return;
235: }
236:
237: final String finalLine = chunk.toString();
238: SwingUtilities
239: .invokeAndWait(new Runnable() {
240: public void run() {
241: if (!_closing) {
242: _logContentsTxt
243: .append(finalLine);
244: }
245: }
246: });
247: } finally {
248: rdr.close();
249: }
250: }
251: } catch (Exception ex) {
252: // i18n[ViewLogsSheet.error.processinglogfile=Error occured processing log file]
253: final String msg = s_stringMgr
254: .getString("ViewLogsSheet.error.processinglogfile");
255: s_log.error(msg, ex);
256: }
257: } else {
258: // i18n[ViewLogsSheet.info.nulllogfile=Null log file name]
259: s_log.debug(s_stringMgr
260: .getString("ViewLogsSheet.info.nulllogfile"));
261: }
262:
263: if (_closing) {
264: return;
265: }
266:
267: // Position to the start of the last line in log.
268: try {
269: int pos = Math.max(0, _logContentsTxt.getText()
270: .length() - 1);
271: int line = _logContentsTxt.getLineOfOffset(pos);
272: final int finalpos = _logContentsTxt
273: .getLineStartOffset(line);
274: SwingUtilities.invokeAndWait(new Runnable() {
275: public void run() {
276: _logContentsTxt.setCaretPosition(finalpos);
277: }
278: });
279: } catch (Exception ex) {
280: // i18n[ViewLogsSheet.error.setcaret=Error positioning caret in log text component]
281: s_log.error(s_stringMgr
282: .getString("ViewLogsSheet.error.setcaret"), ex);
283: }
284: } finally {
285: enableComponents(true);
286: _refreshing = false;
287: cursorChg.restore();
288: }
289: }
290:
291: /**
292: 199828 [Foo Thread] message
293: * @param line
294: * @return
295: */
296: private boolean shouldAppendLineToChunk(String line) {
297: boolean result = false;
298: if (line == null || line.length() == 0) {
299: return false;
300: }
301: if (_errorChkbox.isSelected() && _debugChkbox.isSelected()
302: && _infoChkbox.isSelected()) {
303: return true;
304: }
305: int threadNameEndIdx = line.indexOf("]");
306: if (threadNameEndIdx > -1) {
307: char levelChar = line.charAt(threadNameEndIdx + 2);
308: if (_errorChkbox.isSelected() && levelChar == 'E') {
309: result = true;
310: }
311: if (_debugChkbox.isSelected() && levelChar == 'D') {
312: result = true;
313: }
314: if (_infoChkbox.isSelected() && levelChar == 'I') {
315: result = true;
316: }
317: if (levelChar != 'E' && levelChar != 'D'
318: && levelChar != 'I') {
319: result = true;
320: }
321: } else {
322: result = true;
323: }
324: return result;
325: }
326:
327: /**
328: * Create user interface.
329: */
330: private void createUserInterface() {
331: setDefaultCloseOperation(DISPOSE_ON_CLOSE);
332: GUIUtils.makeToolWindow(this , true);
333: Container contentPane = getContentPane();
334: contentPane.setLayout(new BorderLayout());
335: contentPane.add(createToolBar(), BorderLayout.NORTH);
336: contentPane.add(createMainPanel(), BorderLayout.CENTER);
337: contentPane.add(createButtonsPanel(), BorderLayout.SOUTH);
338: pack();
339:
340: AbstractAction closeAction = new AbstractAction() {
341: public void actionPerformed(ActionEvent actionEvent) {
342: performClose();
343: }
344: };
345: KeyStroke escapeStroke = KeyStroke.getKeyStroke(
346: KeyEvent.VK_ESCAPE, 0);
347: getRootPane().getInputMap(
348: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
349: escapeStroke, "CloseAction");
350: getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
351: .put(escapeStroke, "CloseAction");
352: getRootPane().getInputMap(JComponent.WHEN_FOCUSED).put(
353: escapeStroke, "CloseAction");
354: getRootPane().getActionMap().put("CloseAction", closeAction);
355:
356: }
357:
358: private ToolBar createToolBar() {
359: final ToolBar tb = new ToolBar();
360: tb.setUseRolloverButtons(true);
361: tb.setFloatable(false);
362:
363: final Object[] args = { getTitle(), _logDir.getAbsolutePath() };
364: final String lblTitle = s_stringMgr.getString(
365: "ViewLogsSheet.storedin", args);
366: final JLabel lbl = new JLabel(lblTitle);
367: lbl.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0));
368: tb.add(lbl);
369:
370: return tb;
371: }
372:
373: /**
374: * Create the main panel containing the log details and selector.
375: */
376: private JPanel createMainPanel() {
377: //_logContentsTxt.setEditable(false);
378: final TextPopupMenu pop = new TextPopupMenu();
379: pop.setTextComponent(_logContentsTxt);
380: _logContentsTxt.addMouseListener(new MouseAdapter() {
381: public void mousePressed(MouseEvent evt) {
382: if (evt.isPopupTrigger()) {
383: pop
384: .show(evt.getComponent(), evt.getX(), evt
385: .getY());
386: }
387: }
388:
389: public void mouseReleased(MouseEvent evt) {
390: if (evt.isPopupTrigger()) {
391: pop
392: .show(evt.getComponent(), evt.getX(), evt
393: .getY());
394: }
395: }
396: });
397:
398: File appLogFile = new ApplicationFiles().getExecutionLogFile();
399: _logDirCmb.load(appLogFile.getParentFile());
400:
401: if (_logDirCmb.getModel().getSize() > 0) {
402: _logDirCmb.setSelectedItem(appLogFile.getName());
403: }
404:
405: // Done after the set of the selected item above so that we control
406: // when the initial build is done. We want to make sure that under all
407: // versions of the JDK that the window is shown before the (possibly
408: // lengthy) refresh starts.
409: _logDirCmb.addActionListener(new ChangeLogListener());
410:
411: final JPanel pnl = new JPanel(new BorderLayout());
412: pnl.add(_logDirCmb, BorderLayout.NORTH);
413: _logContentsTxt.setBorder(BorderFactory.createEmptyBorder(0, 1,
414: 0, 0));
415: pnl.add(new JScrollPane(_logContentsTxt), BorderLayout.CENTER);
416:
417: return pnl;
418: }
419:
420: /**
421: * Create panel at bottom containing the buttons.
422: */
423: private JPanel createButtonsPanel() {
424: JPanel pnl = new JPanel();
425:
426: pnl.add(_refreshBtn);
427: _refreshBtn.addActionListener(new ActionListener() {
428: public void actionPerformed(ActionEvent evt) {
429: startRefreshingLog();
430: }
431: });
432:
433: JButton closeBtn = new JButton(s_stringMgr
434: .getString("ViewLogsSheet.close"));
435: closeBtn.addActionListener(new ActionListener() {
436: public void actionPerformed(ActionEvent evt) {
437: performClose();
438: }
439: });
440: pnl.add(closeBtn);
441:
442: _errorChkbox.setSelected(_prefs.getShowErrorLogMessages());
443: _errorChkbox.addActionListener(new ActionListener() {
444: public void actionPerformed(ActionEvent e) {
445: _prefs.setShowErrorLogMessages(_errorChkbox
446: .isSelected());
447: }
448: });
449: _infoChkbox.setSelected(_prefs.getShowInfoLogMessages());
450: _infoChkbox.addActionListener(new ActionListener() {
451: public void actionPerformed(ActionEvent e) {
452: _prefs.setShowInfoLogMessages(_infoChkbox.isSelected());
453: }
454: });
455: _debugChkbox.setSelected(_prefs.getShowDebugLogMessage());
456: _debugChkbox.addActionListener(new ActionListener() {
457: public void actionPerformed(ActionEvent e) {
458: _prefs.setShowDebugLogMessages(_debugChkbox
459: .isSelected());
460: }
461: });
462: pnl.add(_errorChkbox);
463: pnl.add(_infoChkbox);
464: pnl.add(_debugChkbox);
465:
466: GUIUtils.setJButtonSizesTheSame(new JButton[] { closeBtn,
467: _refreshBtn });
468: getRootPane().setDefaultButton(closeBtn);
469:
470: return pnl;
471: }
472:
473: private final class ChangeLogListener implements ActionListener {
474: public void actionPerformed(ActionEvent evt) {
475: ViewLogsSheet.this .startRefreshingLog();
476: }
477: }
478:
479: private final class Refresher implements Runnable {
480: public void run() {
481: ViewLogsSheet.this .refreshLog();
482: }
483: }
484:
485: private static final class LogsComboBox extends
486: DirectoryListComboBox {
487: private File _dir;
488:
489: public void load(File dir, FilenameFilter filter) {
490: _dir = dir;
491: super .load(dir, filter);
492: }
493:
494: public void addItem(Object anObject) {
495: super .addItem(new LogFile(_dir, anObject.toString()));
496: }
497: }
498:
499: private static final class LogFile extends File {
500: private final String _stringRep;
501:
502: LogFile(File dir, String name) {
503: super (dir, name);
504: StringBuffer buf = new StringBuffer();
505: buf.append(getName()).append(" (").append(
506: Utilities.formatSize(length())).append(")");
507: _stringRep = buf.toString();
508: }
509:
510: public String toString() {
511: return _stringRep;
512: }
513: }
514: }
|