| /* |
| * ============================================================================ |
| * The Apache Software License, Version 1.1 |
| * ============================================================================ |
| * |
| * Copyright (C) 1999 The Apache Software Foundation. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without modifica- |
| * tion, are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, if any, must |
| * include the following acknowledgment: "This product includes software |
| * developed by the Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowledgment may appear in the software itself, if |
| * and wherever such third-party acknowledgments normally appear. |
| * |
| * 4. The names "log4j" and "Apache Software Foundation" must not be used to |
| * endorse or promote products derived from this software without prior |
| * written permission. For written permission, please contact |
| * apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache", nor may |
| * "Apache" appear in their name, without prior written permission of the |
| * Apache Software Foundation. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, |
| * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
| * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, |
| * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- |
| * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
| * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
| * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * This software consists of voluntary contributions made by many individuals |
| * on behalf of the Apache Software Foundation. For more information on the |
| * Apache Software Foundation, please see <http://www.apache.org/>. |
| * |
| */ |
| |
| package org.apache.log4j.chainsaw; |
| |
| import org.apache.log4j.Level; |
| import org.apache.log4j.LogManager; |
| import org.apache.log4j.Priority; |
| import org.apache.log4j.UtilLoggingLevel; |
| import org.apache.log4j.chainsaw.icons.ChainsawIcons; |
| import org.apache.log4j.chainsaw.prefs.LoadSettingsEvent; |
| import org.apache.log4j.chainsaw.prefs.SaveSettingsEvent; |
| import org.apache.log4j.chainsaw.prefs.SettingsListener; |
| import org.apache.log4j.chainsaw.prefs.SettingsManager; |
| import org.apache.log4j.helpers.LogLog; |
| import org.apache.log4j.helpers.OptionConverter; |
| import org.apache.log4j.net.SocketNodeEventListener; |
| import org.apache.log4j.net.SocketReceiver; |
| import org.apache.log4j.plugins.PluginRegistry; |
| import org.apache.log4j.plugins.Receiver; |
| |
| import java.awt.BorderLayout; |
| import java.awt.Component; |
| import java.awt.Dimension; |
| import java.awt.Event; |
| import java.awt.FlowLayout; |
| import java.awt.Font; |
| import java.awt.Point; |
| import java.awt.Toolkit; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| import java.awt.event.ComponentEvent; |
| import java.awt.event.ComponentListener; |
| import java.awt.event.InputEvent; |
| import java.awt.event.KeyEvent; |
| import java.awt.event.KeyListener; |
| import java.awt.event.MouseAdapter; |
| import java.awt.event.MouseEvent; |
| import java.awt.event.MouseMotionAdapter; |
| import java.awt.event.WindowAdapter; |
| import java.awt.event.WindowEvent; |
| |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| |
| import java.io.BufferedInputStream; |
| import java.io.BufferedOutputStream; |
| import java.io.EOFException; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutputStream; |
| import java.io.Serializable; |
| |
| import java.net.URL; |
| |
| import java.text.NumberFormat; |
| |
| import java.util.ArrayList; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.StringTokenizer; |
| import java.util.Vector; |
| |
| import javax.swing.AbstractAction; |
| import javax.swing.Action; |
| import javax.swing.BorderFactory; |
| import javax.swing.ButtonGroup; |
| import javax.swing.ImageIcon; |
| import javax.swing.JCheckBox; |
| import javax.swing.JCheckBoxMenuItem; |
| import javax.swing.JComboBox; |
| import javax.swing.JComponent; |
| import javax.swing.JDialog; |
| import javax.swing.JEditorPane; |
| import javax.swing.JFrame; |
| import javax.swing.JLabel; |
| import javax.swing.JMenu; |
| import javax.swing.JMenuItem; |
| import javax.swing.JOptionPane; |
| import javax.swing.JPanel; |
| import javax.swing.JPopupMenu; |
| import javax.swing.JRadioButtonMenuItem; |
| import javax.swing.JScrollPane; |
| import javax.swing.JSeparator; |
| import javax.swing.JSplitPane; |
| import javax.swing.JTable; |
| import javax.swing.JTextField; |
| import javax.swing.JToolBar; |
| import javax.swing.JWindow; |
| import javax.swing.KeyStroke; |
| import javax.swing.ListSelectionModel; |
| import javax.swing.SwingUtilities; |
| import javax.swing.UIManager; |
| import javax.swing.event.ChangeEvent; |
| import javax.swing.event.ChangeListener; |
| import javax.swing.event.DocumentEvent; |
| import javax.swing.event.DocumentListener; |
| import javax.swing.event.EventListenerList; |
| import javax.swing.event.ListSelectionEvent; |
| import javax.swing.event.ListSelectionListener; |
| import javax.swing.event.TableColumnModelEvent; |
| import javax.swing.event.TableColumnModelListener; |
| import javax.swing.table.AbstractTableModel; |
| import javax.swing.table.TableColumn; |
| import javax.swing.table.TableColumnModel; |
| import javax.swing.table.TableModel; |
| |
| |
| /** |
| * The main entry point for Chainsaw, this class represents the first frame |
| * that is used to display a Welcome panel, and any other panels that |
| * are generated because Logging Events are streamed via a Receiver, or other |
| * mechanism. |
| * |
| * If a system property 'chainsaw.usecyclicbuffer' is set to 'true', each panel will use a cyclic |
| * buffer for displaying events and once events reach the buffer limit, the oldest events |
| * are removed from the table. |
| * |
| * If the property is not provided, there is no limit on the table's buffer size. |
| * |
| * If 'chainsaw.usecyclicbuffer' is set to 'true' and a system |
| * property 'chainsaw.cyclicbuffersize' is set to some integer value, that value will |
| * be used as the buffer size - if the buffersize is not provided, a default |
| * size of 500 is used. |
| * |
| * |
| * @author Scott Deboy <sdeboy@apache.org> |
| * @author Paul Smith <psmith@apache.org> |
| * |
| */ |
| public class LogUI extends JFrame implements ChainsawViewer, SettingsListener { |
| private static final String CONFIG_FILE_TO_USE = "config.file"; |
| private static final String USE_CYCLIC_BUFFER_PROP_NAME = |
| "chainsaw.usecyclicbuffer"; |
| private static final String CYCLIC_BUFFER_SIZE_PROP_NAME = |
| "chainsaw.cyclicbuffersize"; |
| private static final String MAIN_WINDOW_HEIGHT = "main.window.height"; |
| private static final String MAIN_WINDOW_WIDTH = "main.window.width"; |
| private static final String MAIN_WINDOW_Y = "main.window.y"; |
| private static final String MAIN_WINDOW_X = "main.window.x"; |
| private static final String TABLE_COLUMN_ORDER = "table.columns.order"; |
| private static final String TABLE_COLUMN_WIDTHS = "table.columns.widths"; |
| private static final String LOOK_AND_FEEL = "LookAndFeel"; |
| private static final String STATUS_BAR = "StatusBar"; |
| private static final String COLUMNS_EXTENSION = ".columns"; |
| private static ChainsawSplash splash; |
| private URL configURLToUse; |
| private boolean noReceiversDefined; |
| private ReceiversPanel receiversPanel; |
| ChainsawTabbedPane tabbedPane; |
| private JToolBar toolbar; |
| private ChainsawStatusBar statusBar; |
| private final Map tableModelMap = new HashMap(); |
| private final Map tableMap = new HashMap(); |
| final List pausedList = new Vector(); |
| private final List filterableColumns = new ArrayList(); |
| private final Map entryMap = new HashMap(); |
| private final Map panelMap = new HashMap(); |
| private final Map scrollMap = new HashMap(); |
| private final Map levelMap = new HashMap(); |
| ChainsawAppenderHandler handler; |
| private ChainsawToolBarAndMenus tbms; |
| private ChainsawAbout aboutBox; |
| private final SettingsManager sm = SettingsManager.getInstance(); |
| private String lookAndFeelClassName; |
| private final NoReceiversWarningPanel noReceiversWarningPanel = |
| new NoReceiversWarningPanel(); |
| |
| /** |
| * Set to true, if and only if the GUI has completed |
| * it's full initialization. Any logging events |
| * that come in must wait until this is true, and |
| * if it is false, should wait on the initializationLock |
| * object until notified. |
| */ |
| private boolean isGUIFullyInitialized = false; |
| private Object initializationLock = new Object(); |
| |
| /** |
| * The shutdownAction is called when the user requests to exit |
| * Chainsaw, and by default this exits the VM, but |
| * a developer may replace this action with something that better suits |
| * their needs |
| */ |
| private Action shutdownAction = |
| new AbstractAction() { |
| public void actionPerformed(ActionEvent e) { |
| System.exit(0); |
| } |
| }; |
| |
| /** |
| * Clients can register a ShutdownListener to be notified |
| * when the user has requested Chainsaw to exit. |
| */ |
| private EventListenerList shutdownListenerList = new EventListenerList(); |
| |
| /** |
| * Constructor which builds up all the visual elements of the frame |
| * including the Menu bar |
| */ |
| public LogUI() { |
| super("Chainsaw v2 - Log Viewer"); |
| |
| if (ChainsawIcons.WINDOW_ICON != null) { |
| setIconImage(new ImageIcon(ChainsawIcons.WINDOW_ICON).getImage()); |
| } |
| } |
| |
| private static final void showSplash() { |
| splash = new ChainsawSplash(); |
| |
| Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); |
| splash.setLocation( |
| (screenSize.width / 2) - (splash.getWidth() / 2), |
| (screenSize.height / 2) - (splash.getHeight() / 2)); |
| |
| splash.setVisible(true); |
| } |
| |
| private static final void removeSplash() { |
| if (splash != null) { |
| splash.setVisible(false); |
| splash.dispose(); |
| } |
| } |
| |
| /** |
| * Registers a ShutdownListener with this calss so that |
| * it can be notified when the user has requested |
| * that Chainsaw exit. |
| * |
| * @param l |
| */ |
| public void addShutdownListener(ShutdownListener l) { |
| shutdownListenerList.add(ShutdownListener.class, l); |
| } |
| |
| /** |
| * Removes the registered ShutdownListener so |
| * that the listener will not be notified on a shutdown. |
| * |
| * @param l |
| */ |
| public void removeShutdownListener(ShutdownListener l) { |
| shutdownListenerList.remove(ShutdownListener.class, l); |
| } |
| |
| /** |
| * Starts Chainsaw by attaching a new instance to the Log4J |
| * main root Logger via a ChainsawAppender, and activates itself |
| * @param args |
| */ |
| public static void main(String[] args) { |
| showSplash(); |
| |
| LogUI logUI = new LogUI(); |
| |
| logUI.handler = new ChainsawAppenderHandler(); |
| logUI.handler.addEventBatchListener(logUI.new NewTabEventBatchReceiver()); |
| LogManager.getRootLogger().addAppender(logUI.handler); |
| logUI.activateViewer(); |
| } |
| |
| public void activateViewer(ChainsawAppender appender) { |
| handler = new ChainsawAppenderHandler(appender); |
| handler.addEventBatchListener(new NewTabEventBatchReceiver()); |
| activateViewer(); |
| } |
| |
| /** |
| * Initialises the menu's and toolbars, but does not actually |
| * create any of the main panel components. |
| * |
| */ |
| private void initGUI() { |
| statusBar = new ChainsawStatusBar(); |
| receiversPanel = new ReceiversPanel(this); |
| tbms = new ChainsawToolBarAndMenus(this); |
| toolbar = tbms.getToolbar(); |
| setJMenuBar(tbms.getMenubar()); |
| tabbedPane = new ChainsawTabbedPane(); |
| tabbedPane.addChangeListener(tbms.getPanelListener()); |
| } |
| |
| /** |
| * Given the load event, configures the size/location of the main window |
| * etc etc. |
| */ |
| public void loadSettings(LoadSettingsEvent event) { |
| if (event.asBoolean(LogUI.STATUS_BAR)) { |
| addStatusBar(); |
| } else { |
| removeStatusBar(); |
| } |
| |
| setLocation( |
| event.asInt(LogUI.MAIN_WINDOW_X), event.asInt(LogUI.MAIN_WINDOW_Y)); |
| setSize( |
| event.asInt(LogUI.MAIN_WINDOW_WIDTH), |
| event.asInt(LogUI.MAIN_WINDOW_HEIGHT)); |
| |
| tbms.stateChange(); |
| } |
| |
| /** |
| * Ensures the location/size of the main window is stored with the settings |
| */ |
| public void saveSettings(SaveSettingsEvent event) { |
| event.saveSetting(LogUI.MAIN_WINDOW_X, (int) getLocation().getX()); |
| event.saveSetting(LogUI.MAIN_WINDOW_Y, (int) getLocation().getY()); |
| |
| event.saveSetting(LogUI.MAIN_WINDOW_WIDTH, getWidth()); |
| event.saveSetting(LogUI.MAIN_WINDOW_HEIGHT, getHeight()); |
| |
| if (lookAndFeelClassName != null) { |
| event.saveSetting(LogUI.LOOK_AND_FEEL, lookAndFeelClassName); |
| } |
| |
| event.saveSetting( |
| LogUI.STATUS_BAR, isStatusBarVisible() ? Boolean.TRUE : Boolean.FALSE); |
| |
| if (configURLToUse != null) { |
| event.saveSetting(LogUI.CONFIG_FILE_TO_USE, configURLToUse.toString()); |
| } |
| } |
| |
| /** |
| * Activates itself as a viewer by configuring Size, and location of |
| * itself, and configures the default Tabbed Pane elements with the correct |
| * layout, table columns, and sets itself viewable. |
| */ |
| public void activateViewer() { |
| sm.configure( |
| new SettingsListener() { |
| public void loadSettings(LoadSettingsEvent event) { |
| lookAndFeelClassName = event.getSetting(LogUI.LOOK_AND_FEEL); |
| |
| if (lookAndFeelClassName != null) { |
| applyLookAndFeel(lookAndFeelClassName); |
| } |
| } |
| |
| public void saveSettings(SaveSettingsEvent event) { |
| //required because of SettingsListener interface..not used during load |
| } |
| }); |
| |
| sm.configure( |
| new SettingsListener() { |
| public void loadSettings(LoadSettingsEvent event) { |
| String configFile = event.getSetting(LogUI.CONFIG_FILE_TO_USE); |
| |
| //if both a config file are defined and a log4j.configuration property are set, |
| //don't use configFile's configuration |
| if ( |
| (configFile != null) && !configFile.trim().equals("") |
| && (System.getProperty("log4j.configuration") == null)) { |
| try { |
| URL url = new URL(configFile); |
| OptionConverter.selectAndConfigure( |
| url, null, LogManager.getLoggerRepository()); |
| |
| if (LogUI.this.getStatusBar() != null) { |
| LogUI.this.getStatusBar().setMessage( |
| "Configured Log4j using remembered URL :: " + url); |
| } |
| |
| LogUI.this.configURLToUse = url; |
| } catch (Exception e) { |
| LogLog.error("error occurred initializing log4j", e); |
| } |
| } |
| } |
| |
| public void saveSettings(SaveSettingsEvent event) { |
| //required because of SettingsListener interface..not used during load |
| } |
| }); |
| |
| /** |
| * This listener sets up the NoReciversWarningPanel and |
| * loads saves the configs/logfiles |
| */ |
| sm.addSettingsListener( |
| new SettingsListener() { |
| public void loadSettings(LoadSettingsEvent event) { |
| int size = event.asInt("SavedConfigs.Size"); |
| Object[] configs = new Object[size]; |
| |
| for (int i = 0; i < size; i++) { |
| configs[i] = event.getSetting("SavedConfigs." + i); |
| } |
| |
| noReceiversWarningPanel.getModel().setRememberedConfigs(configs); |
| } |
| |
| public void saveSettings(SaveSettingsEvent event) { |
| Object[] configs = |
| noReceiversWarningPanel.getModel().getRememberedConfigs(); |
| event.saveSetting("SavedConfigs.Size", configs.length); |
| |
| for (int i = 0; i < configs.length; i++) { |
| event.saveSetting("SavedConfigs." + i, configs[i].toString()); |
| } |
| } |
| }); |
| |
| if ( |
| PluginRegistry.getPlugins( |
| LogManager.getLoggerRepository(), Receiver.class).size() == 0) { |
| noReceiversDefined = true; |
| } |
| |
| initGUI(); |
| |
| /** |
| * Get all the SocketReceivers and configure a new SocketNodeEventListener |
| * so we can get notified of new Sockets |
| */ |
| List list = |
| PluginRegistry.getPlugins( |
| LogManager.getLoggerRepository(), SocketReceiver.class); |
| final SocketNodeEventListener socketListener = |
| new SocketNodeEventListener() { |
| public void socketOpened(String remoteInfo) { |
| statusBar.remoteConnectionReceived(remoteInfo); |
| } |
| |
| public void socketClosedEvent(Exception e) { |
| statusBar.setMessage("Collection lost! :: " + e.getMessage()); |
| } |
| }; |
| |
| for (Iterator iter = list.iterator(); iter.hasNext();) { |
| SocketReceiver item = (SocketReceiver) iter.next(); |
| LogLog.debug("Adding listener for " + item.getName()); |
| item.addSocketNodeEventListener(socketListener); |
| } |
| |
| List utilList = UtilLoggingLevel.getAllPossibleLevels(); |
| |
| // TODO: Replace the array list creating with the standard way of retreiving the Level set. (TBD) |
| Priority[] priorities = |
| new Level[] { Level.FATAL, Level.ERROR, Level.WARN, Level.INFO, Level.DEBUG }; |
| List priorityLevels = new ArrayList(); |
| |
| for (int i = 0; i < priorities.length; i++) { |
| priorityLevels.add(priorities[i].toString()); |
| } |
| |
| List utilLevels = new ArrayList(); |
| |
| for (Iterator iterator = utilLevels.iterator(); iterator.hasNext();) { |
| utilLevels.add(iterator.next().toString()); |
| } |
| |
| levelMap.put(ChainsawConstants.UTIL_LOGGING_EVENT_TYPE, utilLevels); |
| levelMap.put(ChainsawConstants.LOG4J_EVENT_TYPE, priorityLevels); |
| |
| filterableColumns.add(ChainsawConstants.LEVEL_COL_NAME); |
| filterableColumns.add(ChainsawConstants.LOGGER_COL_NAME); |
| filterableColumns.add(ChainsawConstants.THREAD_COL_NAME); |
| filterableColumns.add(ChainsawConstants.NDC_COL_NAME); |
| filterableColumns.add(ChainsawConstants.MDC_COL_NAME); |
| filterableColumns.add(ChainsawConstants.CLASS_COL_NAME); |
| filterableColumns.add(ChainsawConstants.METHOD_COL_NAME); |
| filterableColumns.add(ChainsawConstants.FILE_COL_NAME); |
| filterableColumns.add(ChainsawConstants.NONE_COL_NAME); |
| |
| JPanel panePanel = new JPanel(); |
| panePanel.setLayout(new BorderLayout(2, 2)); |
| |
| getContentPane().setLayout(new BorderLayout()); |
| |
| tabbedPane.addChangeListener(tbms); |
| tabbedPane.addChangeListener( |
| new ChangeListener() { |
| //received a statechange event - selection changed - remove icon from selected index |
| public void stateChanged(ChangeEvent e) { |
| if (tabbedPane.getSelectedComponent() instanceof ChainsawTabbedPane) { |
| if (tabbedPane.getSelectedIndex() > -1) { |
| tabbedPane.setIconAt(tabbedPane.getSelectedIndex(), null); |
| } |
| } |
| } |
| }); |
| |
| KeyStroke ksRight = |
| KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, Event.CTRL_MASK); |
| KeyStroke ksLeft = |
| KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, Event.CTRL_MASK); |
| |
| tabbedPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( |
| ksRight, "MoveRight"); |
| tabbedPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( |
| ksLeft, "MoveLeft"); |
| |
| Action moveRight = |
| new AbstractAction() { |
| public void actionPerformed(ActionEvent e) { |
| int temp = tabbedPane.getSelectedIndex(); |
| ++temp; |
| |
| if (temp != tabbedPane.getTabCount()) { |
| tabbedPane.setSelectedTab(temp); |
| } |
| } |
| }; |
| |
| Action moveLeft = |
| new AbstractAction() { |
| public void actionPerformed(ActionEvent e) { |
| int temp = tabbedPane.getSelectedIndex(); |
| --temp; |
| |
| if (temp > -1) { |
| tabbedPane.setSelectedTab(temp); |
| } |
| } |
| }; |
| |
| tabbedPane.getActionMap().put("MoveRight", moveRight); |
| tabbedPane.getActionMap().put("MoveLeft", moveLeft); |
| |
| /** |
| * We listen for double clicks, and auto-undock currently |
| * selected Tab if the mouse event location matches the currently selected |
| * tab |
| */ |
| tabbedPane.addMouseListener( |
| new MouseAdapter() { |
| public void mouseClicked(MouseEvent e) { |
| super.mouseClicked(e); |
| |
| if ( |
| (e.getClickCount() > 1) |
| && ((e.getModifiers() & InputEvent.BUTTON1_MASK) > 0)) { |
| int tabIndex = tabbedPane.getSelectedIndex(); |
| |
| if ( |
| (tabIndex != -1) && (tabIndex == tabbedPane.getSelectedIndex())) { |
| LogPanel logPanel = getCurrentLogPanel(); |
| |
| if (logPanel != null) { |
| logPanel.undock(); |
| } |
| } |
| } |
| } |
| }); |
| |
| addWelcomePanel(); |
| panePanel.add(tabbedPane); |
| |
| getContentPane().add(toolbar, BorderLayout.NORTH); |
| getContentPane().add(panePanel, BorderLayout.CENTER); |
| getContentPane().add(statusBar, BorderLayout.SOUTH); |
| |
| addWindowListener( |
| new WindowAdapter() { |
| public void windowClosing(WindowEvent event) { |
| exit(); |
| } |
| }); |
| |
| pack(); |
| |
| sm.addSettingsListener(this); |
| sm.addSettingsListener(tbms); |
| sm.loadSettings(); |
| |
| setVisible(true); |
| |
| removeSplash(); |
| |
| synchronized (initializationLock) { |
| isGUIFullyInitialized = true; |
| initializationLock.notifyAll(); |
| } |
| |
| if (noReceiversDefined) { |
| showNoReceiversWarningPanel(); |
| } |
| } |
| |
| /** |
| * Displays a warning dialog about having no Receivers defined |
| * and allows the user to choose some options for configuration |
| */ |
| private void showNoReceiversWarningPanel() { |
| SwingUtilities.invokeLater( |
| new Runnable() { |
| public void run() { |
| final JDialog dialog = new JDialog(LogUI.this, true); |
| dialog.setTitle("Warning: You have no Receivers defined..."); |
| dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); |
| |
| dialog.setResizable(false); |
| |
| noReceiversWarningPanel.setOkActionListener( |
| new ActionListener() { |
| public void actionPerformed(ActionEvent e) { |
| dialog.setVisible(false); |
| } |
| }); |
| |
| dialog.getContentPane().add(noReceiversWarningPanel); |
| |
| dialog.pack(); |
| |
| Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); |
| dialog.setLocation( |
| (screenSize.width / 2) - (dialog.getWidth() / 2), |
| (screenSize.height / 2) - (dialog.getHeight() / 2)); |
| dialog.show(); |
| |
| dialog.dispose(); |
| |
| if (noReceiversWarningPanel.getModel().isManualMode()) { |
| toggleReceiversPanel(); |
| } else if (noReceiversWarningPanel.getModel().isSimpleSocketMode()) { |
| int port = noReceiversWarningPanel.getModel().getSimplePort(); |
| SocketReceiver simpleSocketReceiver = new SocketReceiver(port); |
| simpleSocketReceiver.setName("Simple Socket Receiver"); |
| PluginRegistry.startPlugin(simpleSocketReceiver); |
| receiversPanel.updateReceiverTreeInDispatchThread(); |
| getStatusBar().setMessage( |
| "Simple Socket Receiver created, started, and listening on port " |
| + port); |
| } else if (noReceiversWarningPanel.getModel().isLoadConfig()) { |
| final URL url = |
| noReceiversWarningPanel.getModel().getConfigToLoad(); |
| |
| if (url != null) { |
| LogLog.debug("Initialiazing Log4j with " + url.toExternalForm()); |
| |
| new Thread( |
| new Runnable() { |
| public void run() { |
| try { |
| OptionConverter.selectAndConfigure( |
| url, null, LogManager.getLoggerRepository()); |
| } catch (Exception e) { |
| LogLog.error("Error initializing Log4j", e); |
| } |
| |
| receiversPanel.updateReceiverTreeInDispatchThread(); |
| } |
| }).start(); |
| } |
| } |
| } |
| }); |
| } |
| |
| /** |
| * Exits the application, ensuring Settings are saved. |
| * |
| */ |
| void exit() { |
| // TODO Ask the user if they want to save the settings via a dialog. |
| sm.saveSettings(); |
| |
| int tabCount = tabbedPane.getTabCount(); |
| |
| for (int i = 0; i < tabCount; i++) { |
| Component c = tabbedPane.getComponentAt(i); |
| |
| if (c instanceof LogPanel) { |
| ((LogPanel) c).saveSettings(); |
| } |
| } |
| |
| shutdown(); |
| } |
| |
| void addWelcomePanel() { |
| tabbedPane.addANewTab( |
| "Welcome", WelcomePanel.getInstance(), new ImageIcon( |
| ChainsawIcons.ABOUT), "Welcome/Help"); |
| } |
| |
| void removeWelcomePanel() { |
| tabbedPane.remove( |
| tabbedPane.getComponentAt(tabbedPane.indexOfTab("Welcome"))); |
| } |
| |
| void toggleReceiversPanel() { |
| SwingUtilities.invokeLater( |
| new Runnable() { |
| public void run() { |
| if (getContentPane().isAncestorOf(receiversPanel)) { |
| getContentPane().remove(receiversPanel); |
| } else { |
| getContentPane().add(receiversPanel, BorderLayout.EAST); |
| } |
| |
| getContentPane().invalidate(); |
| getContentPane().validate(); |
| |
| tbms.stateChange(); |
| } |
| }); |
| } |
| |
| boolean isReceiverPanelVisible() { |
| return getContentPane().isAncestorOf(receiversPanel); |
| } |
| |
| ChainsawStatusBar getStatusBar() { |
| return statusBar; |
| } |
| |
| void showAboutBox() { |
| if (aboutBox == null) { |
| aboutBox = new ChainsawAbout(this); |
| } |
| |
| aboutBox.setVisible(true); |
| } |
| |
| Map getPanels() { |
| Map m = new HashMap(); |
| Set panelSet = panelMap.entrySet(); |
| Iterator iter = panelSet.iterator(); |
| |
| while (iter.hasNext()) { |
| Map.Entry entry = (Map.Entry) iter.next(); |
| m.put( |
| entry.getKey(), |
| Boolean.valueOf(((DockablePanel) entry.getValue()).isDocked())); |
| } |
| |
| return m; |
| } |
| |
| void displayPanel(String panelName, boolean display) { |
| Object o = panelMap.get(panelName); |
| |
| if (o instanceof LogPanel) { |
| LogPanel p = (LogPanel) o; |
| |
| int index = tabbedPane.indexOfTab(panelName); |
| |
| if ((index == -1) && display) { |
| tabbedPane.addTab(panelName, p); |
| } |
| |
| if ((index > -1) && !display) { |
| tabbedPane.removeTabAt(index); |
| } |
| } |
| } |
| |
| /** |
| * Shutsdown by ensuring the Appender gets a chance to close. |
| */ |
| private void shutdown() { |
| JWindow progress = new JWindow(); |
| final ProgressPanel panel = new ProgressPanel(1, 3, "Shutting down"); |
| progress.getContentPane().add(panel); |
| progress.pack(); |
| |
| Point p = new Point(getLocation()); |
| p.move((int) getSize().getWidth() >> 1, (int) getSize().getHeight() >> 1); |
| progress.setLocation(p); |
| progress.setVisible(true); |
| |
| Runnable runnable = |
| new Runnable() { |
| public void run() { |
| try { |
| int progress = 1; |
| final int delay = 25; |
| |
| handler.close(); |
| panel.setProgress(progress++); |
| |
| Thread.sleep(delay); |
| |
| PluginRegistry.stopAllPlugins(); |
| panel.setProgress(progress++); |
| |
| Thread.sleep(delay); |
| |
| panel.setProgress(progress++); |
| Thread.sleep(delay); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| |
| fireShutdownEvent(); |
| performShutdownAction(); |
| } |
| }; |
| |
| new Thread(runnable).start(); |
| } |
| |
| /** |
| * Ensures all the registered ShutdownListeners are notified. |
| */ |
| private void fireShutdownEvent() { |
| ShutdownListener[] listeners = |
| (ShutdownListener[]) shutdownListenerList.getListeners( |
| ShutdownListener.class); |
| |
| for (int i = 0; i < listeners.length; i++) { |
| listeners[i].shuttingDown(); |
| } |
| } |
| |
| /** |
| * Configures LogUI's with an action to execute when the user |
| * requests to exit the application, the default action |
| * is to exit the VM. |
| * This Action is called AFTER all the ShutdownListeners have been notified |
| * |
| * @param shutdownAction |
| */ |
| public final void setShutdownAction(Action shutdownAction) { |
| this.shutdownAction = shutdownAction; |
| } |
| |
| /** |
| * Using the current thread, calls the registed Shutdown action's |
| * actionPerformed(...) method. |
| * |
| */ |
| private void performShutdownAction() { |
| LogLog.debug("Calling the shutdown Action. Goodbye!"); |
| shutdownAction.actionPerformed( |
| new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "Shutting Down")); |
| } |
| |
| /** |
| * Returns the currently selected LogPanel, if there is one, otherwise null |
| * @return |
| */ |
| LogPanel getCurrentLogPanel() { |
| Component selectedTab = tabbedPane.getSelectedComponent(); |
| |
| if (selectedTab instanceof LogPanel) { |
| return (LogPanel) selectedTab; |
| } else { |
| // System.out.println(selectedTab); |
| } |
| |
| return null; |
| } |
| |
| void removeStatusBar() { |
| SwingUtilities.invokeLater( |
| new Runnable() { |
| public void run() { |
| getContentPane().remove(statusBar); |
| getContentPane().validate(); |
| getContentPane().repaint(); |
| } |
| }); |
| } |
| |
| boolean isStatusBarVisible() { |
| return getContentPane().isAncestorOf(statusBar); |
| } |
| |
| void addStatusBar() { |
| removeStatusBar(); |
| SwingUtilities.invokeLater( |
| new Runnable() { |
| public void run() { |
| getContentPane().add(statusBar, BorderLayout.SOUTH); |
| getContentPane().validate(); |
| getContentPane().repaint(); |
| } |
| }); |
| } |
| |
| public String getActiveTabName() { |
| int index = tabbedPane.getSelectedIndex(); |
| |
| if (index == -1) { |
| return null; |
| } else { |
| return tabbedPane.getTitleAt(index); |
| } |
| } |
| |
| /** |
| * Formats the individual elements of an LoggingEvent by ensuring that |
| * there are no null bits, replacing them with EMPTY_STRING |
| * @param v |
| * @return |
| */ |
| private Vector formatFields(Vector v) { |
| for (int i = 0; i < v.size(); i++) { |
| if (v.get(i) == null) { |
| v.set(i, ChainsawConstants.EMPTY_STRING); |
| } |
| } |
| |
| return v; |
| } |
| |
| /** |
| * Regurgitates a DisplayFilter for a specific machine identifier |
| * by deserializing the settings from a file. |
| * DisplayFilter serializes tool tip fields and enabled flag. |
| * @param ident |
| * @return |
| */ |
| private DisplayFilter loadDisplayFilter(String ident) { |
| DisplayFilter d = null; |
| ObjectInputStream s = null; |
| File f = |
| new File( |
| SettingsManager.getInstance().getSettingsDirectory() + File.separator |
| + ident + ChainsawConstants.SETTINGS_EXTENSION); |
| |
| if (f.exists()) { |
| try { |
| s = new ObjectInputStream( |
| new BufferedInputStream(new FileInputStream(f))); |
| d = (DisplayFilter) s.readObject(); |
| } catch (IOException ioe) { |
| ioe.printStackTrace(); |
| } catch (ClassNotFoundException cnfe) { |
| cnfe.printStackTrace(); |
| } finally { |
| if (s != null) { |
| try { |
| s.close(); |
| } catch (IOException ioe) { |
| ioe.printStackTrace(); |
| } |
| } |
| } |
| } |
| |
| if (d == null) { |
| d = new DisplayFilter(ident); |
| } |
| |
| return d; |
| } |
| |
| /** |
| * Modify the saved Look And Feel - does not update the currently used Look And Feel |
| * @param string The FQN of the LookAndFeel |
| */ |
| public void setLookAndFeel(String lookAndFeelClassName) { |
| this.lookAndFeelClassName = lookAndFeelClassName; |
| JOptionPane.showMessageDialog( |
| getContentPane(), |
| "Restart application for the new Look and Feel to take effect.", |
| "Look and Feel Updated", JOptionPane.INFORMATION_MESSAGE); |
| } |
| |
| /** |
| * Changes the currently used Look And Feel of the App |
| * @param string The FQN of the LookANdFeel |
| */ |
| private void applyLookAndFeel(String lookAndFeelClassName) { |
| LogLog.debug("Setting L&F -> " + lookAndFeelClassName); |
| |
| try { |
| UIManager.setLookAndFeel(lookAndFeelClassName); |
| SwingUtilities.updateComponentTreeUI(this); |
| } catch (Exception e) { |
| LogLog.error("Failed to change L&F", e); |
| } |
| } |
| |
| /** |
| * Causes the Welcome Panel to become visible, and shows the URL |
| * specified as it's contents |
| * @param url for content to show |
| */ |
| void showHelp(URL url) { |
| removeWelcomePanel(); |
| addWelcomePanel(); |
| |
| // TODO ensure the Welcome Panel is the selected tab |
| WelcomePanel.getInstance().setURL(url); |
| } |
| |
| /** |
| * @return |
| */ |
| public boolean isLogTreePanelVisible() { |
| if (getCurrentLogPanel() == null) { |
| return false; |
| } |
| |
| return getCurrentLogPanel().isLogTreePanelVisible(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.log4j.chainsaw.EventBatchListener#getInterestedIdentifier() |
| */ |
| public String getInterestedIdentifier() { |
| // this instance is interested in ALL event batches, as we determine how to route things |
| return null; |
| } |
| |
| /** |
| * This class handles the recption of the Event batches |
| * and creates new LogPanels if the identifier is not in use |
| * otherwise it ignores the event batch. |
| * @author Paul Smith <psmith@apache.org> |
| * |
| */ |
| private class NewTabEventBatchReceiver implements EventBatchListener { |
| public void receiveEventBatch( |
| final String ident, final List eventBatchEntrys) { |
| if (eventBatchEntrys.size() == 0) { |
| return; |
| } |
| |
| EventContainer tableModel; |
| JSortTable table; |
| ScrollToBottom scrollToBottom; |
| HashMap map = null; |
| |
| if (!isGUIFullyInitialized) { |
| synchronized (initializationLock) { |
| while (!isGUIFullyInitialized) { |
| System.out.println( |
| "Wanting to add a row, but GUI not initialized, waiting..."); |
| |
| /** |
| * Lets wait 1 seconds and recheck. |
| */ |
| try { |
| initializationLock.wait(1000); |
| } catch (InterruptedException e) { |
| } |
| } |
| } |
| } |
| |
| if (tableModelMap.containsKey(ident)) { |
| /** |
| * we ignore this since we assume the LogPanel has been registered itself a listener |
| * and will receive it's own event batches directly |
| */ |
| } else { |
| final String eventType = |
| ((ChainsawEventBatchEntry) eventBatchEntrys.get(0)).getEventType(); |
| final LogPanel thisPanel = new LogPanel(ident, eventType); |
| |
| /** |
| * Let the new LogPanel receive this batch |
| */ |
| thisPanel.receiveEventBatch(ident, eventBatchEntrys); |
| |
| /** |
| * Now add the panel as a batch listener so it can handle it's own batchs |
| */ |
| handler.addEventBatchListener(thisPanel); |
| |
| SwingUtilities.invokeLater( |
| new Runnable() { |
| public void run() { |
| tabbedPane.addANewTab( |
| ident, thisPanel, new ImageIcon(ChainsawIcons.TOOL_TIP)); |
| } |
| }); |
| |
| sm.configure(thisPanel); |
| |
| String msg = "added tab " + ident; |
| LogLog.debug(msg); |
| statusBar.setMessage(msg); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.log4j.chainsaw.EventBatchListener#getInterestedIdentifier() |
| */ |
| public String getInterestedIdentifier() { |
| // we are interested in all batches so we can detect new identifiers |
| return null; |
| } |
| } |
| |
| /** |
| * LogPanel encapsulates all the necessary bits and pieces of a |
| * floating window of Events coming from a specific Location. |
| * |
| * This is where most of the Swing components are constructed and laid out. |
| */ |
| class LogPanel extends DockablePanel implements SettingsListener, |
| EventBatchListener { |
| final ColorFilter colorFilter = new ColorFilter(); |
| final DisplayFilter displayFilter; |
| final EventContainer tableModel; |
| final JEditorPane detail; |
| final JSplitPane lowerPanel; |
| final DetailPaneUpdater detailPaneUpdater; |
| final JPanel upperPanel; |
| final JPanel eventsAndStatusPanel; |
| final JFrame f; |
| final DockablePanel externalPanel; |
| final Action dockingAction; |
| final JSortTable table; |
| private String profileName = null; |
| boolean isDocked = true; |
| String identifier; |
| final Map columnDisplayMap = new HashMap(); |
| final Map colorDisplayMap = new HashMap(); |
| final Set loggerSet = new HashSet(); |
| final ColorDisplaySelector colorDisplaySelector; |
| Set MDCSet = new HashSet(); |
| Set NDCSet = new HashSet(); |
| Set threadSet = new HashSet(); |
| Set classSet = new HashSet(); |
| Set methodSet = new HashSet(); |
| Set fileSet = new HashSet(); |
| Set levelSet = new HashSet(); |
| ScrollToBottom scrollToBottom; |
| private final LogPanelLoggerTreeModel logTreeModel = |
| new LogPanelLoggerTreeModel(); |
| |
| //used for consistency - stays empty - used to allow none set in the colordisplay selector and right click |
| Set noneSet = new HashSet(); |
| Point currentPoint; |
| private final JSplitPane nameTreeAndMainPanelSplit; |
| private final LoggerNameTreePanel logTreePanel; |
| |
| public LogPanel(final String ident, String eventType) { |
| identifier = ident; |
| |
| Map map = new HashMap(); |
| entryMap.put(ident, map); |
| |
| int bufferSize = 500; |
| |
| //if buffer size not provided, set default buffer size to 500 (only used if usecyclicbuffer true) |
| if (System.getProperty(CYCLIC_BUFFER_SIZE_PROP_NAME) != null) { |
| bufferSize = |
| Integer.valueOf(System.getProperty(CYCLIC_BUFFER_SIZE_PROP_NAME)) |
| .intValue(); |
| } |
| |
| tableModel = |
| new ChainsawCyclicBufferTableModel( |
| Boolean.valueOf(System.getProperty(USE_CYCLIC_BUFFER_PROP_NAME)) |
| .booleanValue(), bufferSize); |
| |
| table = new JSortTable(tableModel); |
| table.getColumnModel().addColumnModelListener( |
| new ChainsawTableColumnModelListener(table)); |
| |
| table.setAutoCreateColumnsFromModel(false); |
| |
| table.setRowHeight(20); |
| table.setShowGrid(false); |
| |
| scrollToBottom = new ScrollToBottom(true); |
| |
| // ========================================== |
| tableModel.addLoggerNameListener(logTreeModel); |
| logTreePanel = new LoggerNameTreePanel(logTreeModel); |
| |
| levelSet = new HashSet((List) levelMap.get(eventType)); |
| |
| map.put(ChainsawConstants.LEVEL_COL_NAME, levelSet); |
| |
| map.put(ChainsawConstants.LOGGER_COL_NAME, loggerSet); |
| map.put(ChainsawConstants.THREAD_COL_NAME, threadSet); |
| map.put(ChainsawConstants.NDC_COL_NAME, NDCSet); |
| map.put(ChainsawConstants.MDC_COL_NAME, MDCSet); |
| map.put(ChainsawConstants.CLASS_COL_NAME, classSet); |
| map.put(ChainsawConstants.METHOD_COL_NAME, methodSet); |
| map.put(ChainsawConstants.FILE_COL_NAME, fileSet); |
| map.put(ChainsawConstants.NONE_COL_NAME, noneSet); |
| |
| setLayout(new BorderLayout()); |
| displayFilter = loadDisplayFilter(ident); |
| tableModel.setDisplayFilter(displayFilter); |
| displayFilter.addFilterChangedListener(tableModel); |
| |
| scrollMap.put(ident, scrollToBottom); |
| |
| TableColorizingRenderer renderer = new TableColorizingRenderer(); |
| sm.addSettingsListener(renderer); |
| sm.configure(renderer); |
| |
| renderer.setColorFilter(colorFilter); |
| |
| table.setDefaultRenderer(Object.class, renderer); |
| |
| //if the color filter changes, trigger the tablemodel update |
| colorFilter.addFilterChangedListener( |
| new FilterChangedListener() { |
| public void filterChanged() { |
| if (tableModel instanceof AbstractTableModel) { |
| ((AbstractTableModel) tableModel).fireTableDataChanged(); |
| } |
| } |
| }); |
| |
| final DetailFieldSelector detailFieldSelector = |
| new DetailFieldSelector( |
| ident, new Vector(ChainsawColumns.getColumnsNames()), displayFilter); |
| |
| final ColumnSelector columnSelector = |
| new ColumnSelector( |
| ident, new Vector(ChainsawColumns.getColumnsNames()), table, |
| displayFilter); |
| table.getColumnModel().addColumnModelListener(columnSelector); |
| columnSelector.setIconImage(getIconImage()); |
| detailFieldSelector.setIconImage(getIconImage()); |
| |
| JMenu menuColumnDisplayFilter = |
| new JMenu("Apply display filter for column"); |
| |
| JMenu menuColumnColorFilter = new JMenu("Apply color filter for column"); |
| |
| ButtonGroup bg = new ButtonGroup(); |
| Iterator iter = filterableColumns.iterator(); |
| |
| while (iter.hasNext()) { |
| final String colName = (String) iter.next(); |
| JRadioButtonMenuItem thisItem = new JRadioButtonMenuItem(colName); |
| thisItem.setFont(thisItem.getFont().deriveFont(Font.PLAIN)); |
| thisItem.addActionListener( |
| new ActionListener() { |
| public void actionPerformed(ActionEvent evt) { |
| Vector lastSelected = null; |
| |
| if (table.getSelectedRow() > -1) { |
| lastSelected = tableModel.getRow(table.getSelectedRow()); |
| } |
| |
| colorDisplaySelector.applyColorUpdateForColumn(colName); |
| colorDisplaySelector.applyColorFilters(colName); |
| |
| if (lastSelected != null) { |
| int newIndex = tableModel.getRowIndex(lastSelected); |
| |
| if (newIndex > -1) { |
| table.scrollToRow( |
| newIndex, |
| table.columnAtPoint(table.getVisibleRect().getLocation())); |
| } |
| } |
| } |
| }); |
| bg.add(thisItem); |
| menuColumnColorFilter.add(thisItem); |
| colorDisplayMap.put(colName, thisItem); |
| } |
| |
| ButtonGroup bg2 = new ButtonGroup(); |
| Iterator iter2 = filterableColumns.iterator(); |
| |
| while (iter2.hasNext()) { |
| final String colName = (String) iter2.next(); |
| JRadioButtonMenuItem thisItem = new JRadioButtonMenuItem(colName); |
| thisItem.setFont(thisItem.getFont().deriveFont(Font.PLAIN)); |
| thisItem.addActionListener( |
| new ActionListener() { |
| public void actionPerformed(ActionEvent evt) { |
| Vector lastSelected = null; |
| |
| if (table.getSelectedRow() > -1) { |
| lastSelected = tableModel.getRow(table.getSelectedRow()); |
| } |
| |
| colorDisplaySelector.applyDisplayUpdateForColumn(colName); |
| colorDisplaySelector.applyDisplayFilters(colName); |
| |
| if (lastSelected != null) { |
| int newIndex = tableModel.getRowIndex(lastSelected); |
| |
| if (newIndex > -1) { |
| table.scrollToRow( |
| newIndex, |
| table.columnAtPoint(table.getVisibleRect().getLocation())); |
| } |
| } |
| } |
| }); |
| bg2.add(thisItem); |
| menuColumnDisplayFilter.add(thisItem); |
| columnDisplayMap.put(colName, thisItem); |
| } |
| |
| colorDisplaySelector = |
| new ColorDisplaySelector( |
| ident, map, colorFilter, displayFilter, colorDisplayMap, |
| columnDisplayMap, ChainsawColumns.getColumnsNames(), |
| filterableColumns, (List) levelMap.get(eventType)); |
| colorDisplaySelector.setIconImage( |
| ((ImageIcon) ChainsawIcons.ICON_PREFERENCES).getImage()); |
| |
| table.addMouseMotionListener( |
| new MouseMotionAdapter() { |
| int currentRow = -1; |
| |
| public void mouseMoved(MouseEvent evt) { |
| currentPoint = evt.getPoint(); |
| |
| if (displayFilter.isToolTipsEnabled()) { |
| int row = table.rowAtPoint(evt.getPoint()); |
| |
| if ((row == currentRow) || (row == -1)) { |
| return; |
| } |
| |
| currentRow = row; |
| |
| table.setToolTipText( |
| ((EventContainer) table.getModel()).getDetailText(row)); |
| } else { |
| table.setToolTipText(null); |
| } |
| } |
| }); |
| |
| //HACK - fix the way columns are sized..should be saved off and loaded later |
| table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); |
| |
| detail = new JEditorPane(ChainsawConstants.DETAIL_CONTENT_TYPE, ""); |
| detail.setEditable(false); |
| |
| detailPaneUpdater = |
| new DetailPaneUpdater(this, detail, (EventContainer) tableModel); |
| |
| upperPanel = new JPanel(new BorderLayout()); |
| upperPanel.setBorder(BorderFactory.createEmptyBorder(2, 5, 2, 0)); |
| |
| final JLabel filterLabel = new JLabel("QuickFilter: "); |
| filterLabel.setFont(filterLabel.getFont().deriveFont(Font.BOLD)); |
| |
| JPanel upperLeftPanel = |
| new JPanel(new FlowLayout(FlowLayout.CENTER, 3, 0)); |
| upperLeftPanel.add(filterLabel); |
| |
| final JComboBox customFilterList = |
| new JComboBox(ChainsawColumns.getColumnsNames().toArray()); |
| customFilterList.setFont(customFilterList.getFont().deriveFont(10f)); |
| |
| final JTextField filterText = new JTextField(); |
| |
| filterText.getDocument().addDocumentListener( |
| new DocumentListener() { |
| public void insertUpdate(DocumentEvent e) { |
| setFilter(); |
| } |
| |
| public void removeUpdate(DocumentEvent e) { |
| setFilter(); |
| } |
| |
| public void changedUpdate(DocumentEvent e) { |
| setFilter(); |
| } |
| |
| public void setFilter() { |
| if (filterText.getText().equals("")) { |
| displayFilter.setCustomFilter(null); |
| } else { |
| detailPaneUpdater.setSelectedRow(-1); |
| |
| displayFilter.setCustomFilter( |
| new DisplayFilterEntry( |
| (String) customFilterList.getSelectedItem(), |
| filterText.getText(), ChainsawConstants.GLOBAL_MATCH)); |
| } |
| } |
| }); |
| |
| String evaluator = |
| ExpressionEvaluatorFactory.newInstance().getEvaluatorClassName(); |
| filterText.setToolTipText( |
| "See " + evaluator + " documentation for expression rules"); |
| |
| customFilterList.setMaximumRowCount(15); |
| |
| upperLeftPanel.add(customFilterList); |
| |
| customFilterList.addActionListener( |
| new ActionListener() { |
| public void actionPerformed(ActionEvent evt) { |
| if (!(filterText.getText().equals(""))) { |
| displayFilter.setCustomFilter( |
| new DisplayFilterEntry( |
| (String) customFilterList.getSelectedItem(), |
| filterText.getText(), ChainsawConstants.GLOBAL_MATCH)); |
| } |
| } |
| }); |
| |
| upperPanel.add(filterText, BorderLayout.CENTER); |
| upperPanel.add(upperLeftPanel, BorderLayout.WEST); |
| |
| final JCheckBox override = new JCheckBox(); |
| override.addActionListener( |
| new ActionListener() { |
| public void actionPerformed(ActionEvent evt) { |
| Vector lastSelected = null; |
| |
| if (table.getSelectedRow() > -1) { |
| lastSelected = tableModel.getRow(table.getSelectedRow()); |
| } |
| |
| displayFilter.setCustomFilterOverride(override.isSelected()); |
| |
| if (lastSelected != null) { |
| int newIndex = tableModel.getRowIndex(lastSelected); |
| |
| if (newIndex > -1) { |
| table.scrollToRow( |
| newIndex, |
| table.columnAtPoint(table.getVisibleRect().getLocation())); |
| } |
| } |
| } |
| }); |
| |
| override.setToolTipText( |
| "<html>Unchecked: Apply QuickFilter to displayed rows<br>Checked: Apply QuickFilter to ALL rows (override display filter setting)</html>"); |
| |
| JPanel upperRightPanel = |
| new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0)); |
| upperRightPanel.add(override); |
| |
| upperPanel.add(upperRightPanel, BorderLayout.EAST); |
| |
| eventsAndStatusPanel = new JPanel(); |
| eventsAndStatusPanel.setLayout(new BorderLayout()); |
| |
| final JScrollPane eventsPane = new JScrollPane(table); |
| |
| eventsPane.setPreferredSize(new Dimension(900, 300)); |
| |
| eventsAndStatusPanel.add(eventsPane, BorderLayout.CENTER); |
| |
| final JPanel statusLabelPanel = new JPanel(); |
| statusLabelPanel.setLayout(new BorderLayout()); |
| |
| final JLabel statusPaneLabel = new JLabel(); |
| statusPaneLabel.setFont(statusPaneLabel.getFont().deriveFont(Font.BOLD)); |
| statusLabelPanel.setBorder(BorderFactory.createEtchedBorder()); |
| statusPaneLabel.setHorizontalAlignment(JLabel.LEFT); |
| statusPaneLabel.setVerticalAlignment(JLabel.CENTER); |
| statusLabelPanel.add(statusPaneLabel, BorderLayout.WEST); |
| statusLabelPanel.add(upperPanel, BorderLayout.CENTER); |
| eventsAndStatusPanel.add(statusLabelPanel, BorderLayout.NORTH); |
| |
| final JPanel detailPanel = new JPanel(new BorderLayout()); |
| |
| //set valueisadjusting if holding down a key - don't process setdetail events |
| table.addKeyListener( |
| new KeyListener() { |
| public void keyTyped(KeyEvent e) { |
| } |
| |
| public void keyPressed(KeyEvent e) { |
| synchronized (detail) { |
| table.getSelectionModel().setValueIsAdjusting(true); |
| detail.notify(); |
| } |
| } |
| |
| public void keyReleased(KeyEvent e) { |
| synchronized (detail) { |
| table.getSelectionModel().setValueIsAdjusting(false); |
| detail.notify(); |
| } |
| } |
| }); |
| |
| final JScrollPane detailPane = new JScrollPane(detail); |
| detailPane.setPreferredSize(new Dimension(900, 50)); |
| |
| detailPanel.add(detailPane, BorderLayout.CENTER); |
| |
| lowerPanel = |
| new JSplitPane( |
| JSplitPane.VERTICAL_SPLIT, eventsAndStatusPanel, detailPanel); |
| lowerPanel.setBorder(null); |
| lowerPanel.setDividerLocation(150); |
| lowerPanel.setLastDividerLocation(-1); |
| lowerPanel.setOneTouchExpandable(true); |
| lowerPanel.addPropertyChangeListener( |
| "dividerLocation", |
| new PropertyChangeListener() { |
| public void propertyChange(PropertyChangeEvent evt) { |
| tbms.stateChange(); |
| } |
| }); |
| |
| nameTreeAndMainPanelSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); |
| nameTreeAndMainPanelSplit.add(logTreePanel); |
| nameTreeAndMainPanelSplit.add(lowerPanel); |
| nameTreeAndMainPanelSplit.setOneTouchExpandable(true); |
| nameTreeAndMainPanelSplit.setToolTipText("Still under development...."); |
| nameTreeAndMainPanelSplit.setDividerLocation(160); |
| |
| add(nameTreeAndMainPanelSplit, BorderLayout.CENTER); |
| |
| /** |
| * This listener deals with when the user hides the LogPanel, |
| * by disabling the use of the splitpane |
| */ |
| logTreePanel.addComponentListener( |
| new ComponentListener() { |
| public void componentHidden(ComponentEvent e) { |
| nameTreeAndMainPanelSplit.setEnabled(false); |
| nameTreeAndMainPanelSplit.setOneTouchExpandable(false); |
| tbms.stateChange(); |
| } |
| |
| public void componentMoved(ComponentEvent e) { |
| } |
| |
| public void componentResized(ComponentEvent e) { |
| } |
| |
| public void componentShown(ComponentEvent e) { |
| nameTreeAndMainPanelSplit.setEnabled(true); |
| nameTreeAndMainPanelSplit.setOneTouchExpandable(true); |
| nameTreeAndMainPanelSplit.setDividerLocation(-1); |
| tbms.stateChange(); |
| } |
| }); |
| |
| // add(lowerPanel, BorderLayout.CENTER); |
| table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); |
| |
| table.getSelectionModel().addListSelectionListener( |
| new ListSelectionListener() { |
| public void valueChanged(ListSelectionEvent evt) { |
| if ( |
| ((evt.getFirstIndex() == evt.getLastIndex()) |
| && (evt.getFirstIndex() > 0)) || (evt.getValueIsAdjusting())) { |
| return; |
| } |
| |
| final ListSelectionModel lsm = |
| (ListSelectionModel) evt.getSource(); |
| |
| if (lsm.isSelectionEmpty()) { |
| if (getIdentifier().equals(getActiveTabName())) { |
| statusBar.setNothingSelected(); |
| } |
| |
| if (detail.getDocument().getDefaultRootElement() != null) { |
| detailPaneUpdater.setSelectedRow(-1); |
| } |
| } else { |
| if (table.getSelectedRow() > -1) { |
| int selectedRow = table.getSelectedRow(); |
| |
| if (getIdentifier().equals(getActiveTabName())) { |
| updateStatusBar(); |
| } |
| |
| try { |
| if (tableModel.getRowCount() >= selectedRow) { |
| detailPaneUpdater.setSelectedRow(table.getSelectedRow()); |
| } else { |
| detailPaneUpdater.setSelectedRow(-1); |
| } |
| } catch (Exception e) { |
| e.printStackTrace(); |
| detailPaneUpdater.setSelectedRow(-1); |
| } |
| } |
| } |
| } |
| }); |
| |
| final JMenuItem menuItemToggleDock = new JMenuItem("Undock/dock"); |
| |
| f = new JFrame(ident); |
| f.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); |
| |
| if (ChainsawIcons.UNDOCKED_ICON != null) { |
| f.setIconImage(new ImageIcon(ChainsawIcons.UNDOCKED_ICON).getImage()); |
| } |
| |
| externalPanel = new DockablePanel(); |
| externalPanel.setLayout(new BorderLayout()); |
| f.getContentPane().add(externalPanel); |
| f.getContentPane().add( |
| tbms.createDockwindowToolbar(f, this), BorderLayout.NORTH); |
| |
| dockingAction = |
| new AbstractAction("Undock") { |
| public void actionPerformed(ActionEvent evt) { |
| if (isDocked()) { |
| undock(); |
| } else { |
| dock(); |
| } |
| } |
| }; |
| dockingAction.putValue( |
| Action.SMALL_ICON, new ImageIcon(ChainsawIcons.UNDOCK)); |
| menuItemToggleDock.setAction(dockingAction); |
| f.addWindowListener( |
| new WindowAdapter() { |
| public void windowClosing(WindowEvent e) { |
| dock(); |
| } |
| }); |
| |
| JMenuItem menuItemDisplayFilter = |
| new JMenuItem("Define display and color filters..."); |
| menuItemDisplayFilter.addActionListener( |
| new ActionListener() { |
| public void actionPerformed(ActionEvent evt) { |
| showPreferences(); |
| } |
| }); |
| menuItemDisplayFilter.setIcon(ChainsawIcons.ICON_PREFERENCES); |
| |
| final JCheckBoxMenuItem menuItemToggleToolTips = |
| new JCheckBoxMenuItem( |
| "Show ToolTips", displayFilter.isToolTipsEnabled()); |
| menuItemToggleToolTips.addActionListener( |
| new ActionListener() { |
| public void actionPerformed(ActionEvent evt) { |
| displayFilter.enableToolTips(menuItemToggleToolTips.isSelected()); |
| } |
| }); |
| menuItemToggleToolTips.setIcon(new ImageIcon(ChainsawIcons.TOOL_TIP)); |
| |
| final JMenuItem menuDefineCustomFilter = |
| new JMenuItem("Custom filter from mouse location"); |
| menuDefineCustomFilter.addActionListener( |
| new ActionListener() { |
| public void actionPerformed(ActionEvent evt) { |
| if (currentPoint != null) { |
| int column = table.columnAtPoint(currentPoint); |
| int row = table.rowAtPoint(currentPoint); |
| String colName = table.getColumnName(column); |
| String value = ""; |
| |
| if ( |
| colName.equalsIgnoreCase(ChainsawConstants.TIMESTAMP_COL_NAME)) { |
| JComponent comp = |
| (JComponent) table.getCellRenderer(row, column); |
| |
| if (comp instanceof JLabel) { |
| value = ((JLabel) comp).getText(); |
| } |
| } else { |
| value = table.getValueAt(row, column).toString(); |
| } |
| |
| customFilterList.setSelectedItem(colName); |
| filterText.setText(value); |
| } |
| } |
| }); |
| |
| final JCheckBoxMenuItem menuItemScrollBottom = |
| new JCheckBoxMenuItem("Scroll to bottom", scrollToBottom.isScrolled()); |
| menuItemScrollBottom.addActionListener( |
| new ActionListener() { |
| public void actionPerformed(ActionEvent evt) { |
| scrollToBottom.scroll(menuItemScrollBottom.isSelected()); |
| } |
| }); |
| menuItemScrollBottom.setIcon( |
| new ImageIcon(ChainsawIcons.SCROLL_TO_BOTTOM)); |
| |
| JMenuItem menuItemRemoveColorFilter = |
| new JMenuItem("Remove all color filters"); |
| menuItemRemoveColorFilter.addActionListener( |
| new ActionListener() { |
| public void actionPerformed(ActionEvent evt) { |
| colorDisplaySelector.clearColors(); |
| colorFilter.clear(); |
| } |
| }); |
| |
| JMenuItem menuItemDetailFieldSelector = |
| new JMenuItem("Select tooltip/detail columns..."); |
| menuItemDetailFieldSelector.addActionListener( |
| new ActionListener() { |
| public void actionPerformed(ActionEvent evt) { |
| detailFieldSelector.show(); |
| } |
| }); |
| |
| JMenuItem menuItemColumnSelector = |
| new JMenuItem("Select display columns..."); |
| menuItemColumnSelector.addActionListener( |
| new ActionListener() { |
| public void actionPerformed(ActionEvent evt) { |
| columnSelector.show(); |
| } |
| }); |
| |
| JMenuItem menuItemRemoveDisplayFilter = |
| new JMenuItem("Remove all display filters"); |
| menuItemRemoveDisplayFilter.addActionListener( |
| new ActionListener() { |
| public void actionPerformed(ActionEvent evt) { |
| colorDisplaySelector.clearDisplay(); |
| displayFilter.clear(); |
| } |
| }); |
| |
| final JPopupMenu p = new JPopupMenu(); |
| |
| p.add(menuItemToggleDock); |
| p.add(new JSeparator()); |
| |
| final JCheckBoxMenuItem menuItemToggleDetails = |
| new JCheckBoxMenuItem("Show Detail Pane"); |
| menuItemToggleDetails.addActionListener( |
| new ActionListener() { |
| public void actionPerformed(ActionEvent e) { |
| toggleDetailPanel(); |
| menuItemToggleDetails.getModel().setSelected( |
| isDetailPaneVisible()); |
| } |
| }); |
| lowerPanel.addPropertyChangeListener( |
| "dividerLocation", |
| new PropertyChangeListener() { |
| public void propertyChange(PropertyChangeEvent evt) { |
| menuItemToggleDetails.getModel().setSelected( |
| isDetailPaneVisible()); |
| } |
| }); |
| menuItemToggleDetails.setIcon(new ImageIcon(ChainsawIcons.INFO)); |
| |
| /** |
| * We set this to true first, because the Split pane hasn't been laid |
| * out yet, and isDetailPaneVisible() will therefore return false. |
| */ |
| menuItemToggleDetails.getModel().setSelected(true); |
| menuItemToggleDetails.addChangeListener(tbms); |
| |
| p.add(menuItemToggleDetails); |
| p.add(menuItemToggleToolTips); |
| p.add(menuItemScrollBottom); |
| |
| p.add(new JSeparator()); |
| |
| p.add(menuDefineCustomFilter); |
| p.add(new JSeparator()); |
| |
| p.add(menuItemDisplayFilter); |
| p.add(menuColumnDisplayFilter); |
| p.add(menuColumnColorFilter); |
| p.add(new JSeparator()); |
| |
| JMenu removeSubMenu = new JMenu("Remove"); |
| JMenu selectSubMenu = new JMenu("Select"); |
| |
| selectSubMenu.add(menuItemColumnSelector); |
| selectSubMenu.add(menuItemDetailFieldSelector); |
| |
| removeSubMenu.add(menuItemRemoveColorFilter); |
| removeSubMenu.add(menuItemRemoveDisplayFilter); |
| |
| p.add(selectSubMenu); |
| p.add(removeSubMenu); |
| |
| final PopupListener popupListener = new PopupListener(p); |
| |
| eventsPane.addMouseListener(popupListener); |
| table.addMouseListener(popupListener); |
| detail.addMouseListener(popupListener); |
| |
| tableMap.put(ident, table); |
| tableModelMap.put(ident, tableModel); |
| tabbedPane.add(ident, this); |
| panelMap.put(ident, this); |
| |
| tableModel.addEventCountListener( |
| new EventCountListener() { |
| public void eventCountChanged(int currentCount, int totalCount) { |
| statusPaneLabel.setText( |
| " Events " + currentCount + " of " + totalCount); |
| } |
| }); |
| |
| if (tableModel.isCyclic()) { |
| final ChainsawCyclicBufferTableModel cyclicModel = |
| (ChainsawCyclicBufferTableModel) tableModel; |
| tableModel.addEventCountListener( |
| new EventCountListener() { |
| final NumberFormat formatter = NumberFormat.getPercentInstance(); |
| boolean warning75 = false; |
| boolean warning100 = false; |
| |
| public void eventCountChanged(int currentCount, int totalCount) { |
| double percent = |
| ((double) totalCount) / cyclicModel.getMaxSize(); |
| String msg = null; |
| |
| if ((percent > 0.75) && (percent < 1.0) && !warning75) { |
| msg = |
| "Warning :: " + formatter.format(percent) + " of the '" |
| + getIdentifier() + "' buffer has been used"; |
| warning75 = true; |
| } else if ((percent >= 1.0) && !warning100) { |
| msg = |
| "Warning :: " + formatter.format(percent) + " of the '" |
| + getIdentifier() |
| + "' buffer has been used. Older events are being discarded."; |
| warning100 = true; |
| } |
| |
| if (msg != null) { |
| getStatusBar().setMessage(msg); |
| } |
| } |
| }); |
| } |
| |
| tableModel.addEventCountListener(new TabIconHandler(ident)); |
| f.pack(); |
| } |
| |
| void updateStatusBar() { |
| SwingUtilities.invokeLater( |
| new Runnable() { |
| public void run() { |
| statusBar.setSelectedLine( |
| table.getSelectedRow() + 1, table.getModel().getRowCount()); |
| } |
| }); |
| } |
| |
| void showPreferences() { |
| colorDisplaySelector.show(); |
| } |
| |
| TableModel getModel() { |
| return tableModel; |
| } |
| |
| String getIdentifier() { |
| return identifier; |
| } |
| |
| void clearModel() { |
| tableModel.clearModel(); |
| |
| synchronized (detail) { |
| detailPaneUpdater.setSelectedRow(-1); |
| detail.notify(); |
| } |
| |
| statusBar.setNothingSelected(); |
| } |
| |
| public int getCurrentRow() { |
| return table.getSelectedRow(); |
| } |
| |
| /** |
| * Find from the top |
| * @param text |
| */ |
| void findFromTop(String text) { |
| find(0, text); |
| } |
| |
| /** |
| * Finds the row with the specified text, and ensures it is made visible |
| * @param text |
| */ |
| void find(String text) { |
| find(table.getSelectedRow(), text); |
| } |
| |
| /** |
| * Finds the next row with the specified text, and ensures it is made visible |
| * @param text |
| */ |
| void findNext(String text) { |
| find(table.getSelectedRow() + 1, text); |
| } |
| |
| /** |
| * Finds the row with the specified text starting at the specified row, and ensures it is made visible |
| * @param text |
| */ |
| void find(int row, String text) { |
| final int newRow = tableModel.find(row, text); |
| |
| if (text.length() == 0) { |
| scrollToBottom.bypass(false); |
| } else { |
| scrollToBottom.bypass(true); |
| } |
| |
| table.scrollToRow( |
| newRow, table.columnAtPoint(table.getVisibleRect().getLocation())); |
| } |
| |
| /** |
| * Docks this DockablePanel by hiding the JFrame and placing the |
| * Panel back inside the LogUI window. |
| * |
| */ |
| void dock() { |
| setDocked(true); |
| f.setVisible(false); |
| removeAll(); |
| |
| // add(lowerPanel, BorderLayout.CENTER); |
| add(nameTreeAndMainPanelSplit, BorderLayout.CENTER); |
| panelMap.put(getIdentifier(), LogPanel.this); |
| tabbedPane.addANewTab(getIdentifier(), LogPanel.this, null); |
| externalPanel.setDocked(true); |
| dockingAction.putValue(Action.NAME, "Undock"); |
| dockingAction.putValue(Action.SMALL_ICON, ChainsawIcons.ICON_UNDOCK); |
| } |
| |
| /** |
| * Undocks this DockablePanel by removing the panel from the LogUI |
| * window and placing it inside it's own JFrame. |
| * |
| */ |
| void undock() { |
| setDocked(false); |
| externalPanel.removeAll(); |
| externalPanel.add(nameTreeAndMainPanelSplit, BorderLayout.CENTER); |
| tabbedPane.remove(LogPanel.this); |
| externalPanel.setDocked(false); |
| panelMap.put(getIdentifier(), externalPanel); |
| f.setSize(getSize()); |
| |
| f.setLocation(getBounds().x, getBounds().y); |
| |
| f.setVisible(true); |
| dockingAction.putValue(Action.NAME, "Dock"); |
| dockingAction.putValue(Action.SMALL_ICON, ChainsawIcons.ICON_DOCK); |
| } |
| |
| /** |
| * returns true if the DetailPane is viewable, that is |
| * if the SplitPane's properties are such that the lower pane |
| * would be seen, and so returns a boolean expression |
| * of the SplitPanes divider location |
| * @return |
| */ |
| boolean isDetailPaneVisible() { |
| double currentLoc = lowerPanel.getDividerLocation(); |
| double max = lowerPanel.getMaximumDividerLocation(); |
| |
| return currentLoc < max; |
| } |
| |
| /** |
| * Shows or hides the Detail Pane depending on the Last |
| * known position |
| * |
| */ |
| void toggleDetailPanel() { |
| int currentPosition = lowerPanel.getDividerLocation(); |
| int lastPosition = lowerPanel.getLastDividerLocation(); |
| |
| if (lastPosition == -1) { |
| lowerPanel.setDividerLocation(1.0d); |
| lowerPanel.setLastDividerLocation(currentPosition); |
| } else { |
| lowerPanel.setDividerLocation(lowerPanel.getLastDividerLocation()); |
| lowerPanel.setLastDividerLocation(currentPosition); |
| } |
| } |
| |
| void toggleLogTreePanel() { |
| LogLog.debug( |
| "Toggling logPanel, currently isVisible=" + logTreePanel.isVisible()); |
| logTreePanel.setVisible(!logTreePanel.isVisible()); |
| LogLog.debug( |
| "Toggling logPanel, now isVisible=" + logTreePanel.isVisible()); |
| } |
| |
| public void saveSettings() { |
| saveColumnSettings(identifier, table.getColumnModel()); |
| colorDisplaySelector.save(); |
| displayFilter.save(); |
| } |
| |
| public void saveSettings(SaveSettingsEvent event) { |
| //not used..save of columns performed via tablecolumnmodellistener event callback |
| } |
| |
| void saveColumnSettings(String ident, TableColumnModel model) { |
| ObjectOutputStream o = null; |
| |
| try { |
| File f = |
| new File( |
| SettingsManager.getInstance().getSettingsDirectory() |
| + File.separator + ident + COLUMNS_EXTENSION); |
| o = new ObjectOutputStream( |
| new BufferedOutputStream(new FileOutputStream(f))); |
| |
| Enumeration e = model.getColumns(); |
| |
| while (e.hasMoreElements()) { |
| TableColumn c = (TableColumn) e.nextElement(); |
| o.writeObject( |
| new TableColumnData( |
| (String) c.getHeaderValue(), c.getModelIndex(), c.getWidth())); |
| } |
| |
| o.flush(); |
| } catch (FileNotFoundException fnfe) { |
| fnfe.printStackTrace(); |
| } catch (IOException ioe) { |
| ioe.printStackTrace(); |
| } finally { |
| try { |
| if (o != null) { |
| o.close(); |
| } |
| } catch (IOException ioe) { |
| ioe.printStackTrace(); |
| } |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.log4j.chainsaw.prefs.Profileable#loadSettings(org.apache.log4j.chainsaw.prefs.LoadSettingsEvent) |
| */ |
| public void loadSettings(LoadSettingsEvent event) { |
| File f = |
| new File( |
| SettingsManager.getInstance().getSettingsDirectory() |
| + File.separator + identifier + COLUMNS_EXTENSION); |
| |
| if (f.exists()) { |
| loadColumnSettings(identifier, table.getColumnModel()); |
| } else { |
| loadDefaultColumnSettings(event); |
| } |
| } |
| |
| void loadDefaultColumnSettings(LoadSettingsEvent event) { |
| String columnOrder = event.getSetting(TABLE_COLUMN_ORDER); |
| |
| TableColumnModel columnModel = table.getColumnModel(); |
| |
| Map columnNameMap = new HashMap(); |
| |
| for (int i = 0; i < columnModel.getColumnCount(); i++) { |
| columnNameMap.put(table.getColumnName(i), columnModel.getColumn(i)); |
| } |
| |
| int index = 0; |
| StringTokenizer tok = new StringTokenizer(columnOrder, ","); |
| List sortedColumnList = new ArrayList(); |
| |
| //remove all columns from the table that exist in the model |
| //and add in the correct order to a new arraylist |
| //(may be a subset of possible columns) |
| while (tok.hasMoreElements()) { |
| String element = (String) tok.nextElement(); |
| TableColumn column = (TableColumn) columnNameMap.get(element); |
| |
| if (column != null) { |
| System.out.println( |
| "Moving column " + element + " from index " |
| + column.getModelIndex() + " to index " + index++); |
| sortedColumnList.add(column); |
| table.removeColumn(column); |
| } |
| } |
| |
| //re-add columns to the table in the order provided from the list |
| for (Iterator iter = sortedColumnList.iterator(); iter.hasNext();) { |
| TableColumn element = (TableColumn) iter.next(); |
| table.addColumn(element); |
| } |
| |
| // TODO Rest of the load settings |
| String columnWidths = event.getSetting(TABLE_COLUMN_WIDTHS); |
| |
| // System.out.println("Column widths=" + columnWidths); |
| tok = new StringTokenizer(columnWidths, ","); |
| index = 0; |
| |
| while (tok.hasMoreElements()) { |
| String element = (String) tok.nextElement(); |
| |
| try { |
| int width = Integer.parseInt(element); |
| |
| if (index > (columnModel.getColumnCount() - 1)) { |
| System.out.println( |
| "loadsettings - failed attempt to set width for index " + index |
| + ", width " + element); |
| } else { |
| columnModel.getColumn(index).setPreferredWidth(width); |
| } |
| |
| index++; |
| } catch (NumberFormatException e) { |
| LogLog.error("Error decoding a Table width", e); |
| } |
| } |
| |
| SwingUtilities.invokeLater( |
| new Runnable() { |
| public void run() { |
| repaint(); |
| } |
| }); |
| } |
| |
| void loadColumnSettings(String ident, TableColumnModel model) { |
| File f = |
| new File( |
| SettingsManager.getInstance().getSettingsDirectory() |
| + File.separator + ident + COLUMNS_EXTENSION); |
| |
| if (f.exists()) { |
| ArrayList newColumns = new ArrayList(); |
| |
| TableColumnData temp = null; |
| ObjectInputStream s = null; |
| |
| try { |
| s = new ObjectInputStream( |
| new BufferedInputStream(new FileInputStream(f))); |
| |
| while (true) { |
| temp = (TableColumnData) s.readObject(); |
| |
| TableColumn tc = new TableColumn(temp.getIndex(), temp.getWidth()); |
| tc.setHeaderValue(temp.getColName()); |
| newColumns.add(tc); |
| } |
| } catch (EOFException eof) { //end of file - ignore.. |
| }catch (IOException ioe) { |
| ioe.printStackTrace(); |
| } catch (ClassNotFoundException cnfe) { |
| cnfe.printStackTrace(); |
| } finally { |
| if (s != null) { |
| try { |
| s.close(); |
| } catch (IOException ioe) { |
| ioe.printStackTrace(); |
| } |
| } |
| } |
| |
| //only remove columns and add serialized columns if |
| //at least one column was read from the file |
| if (newColumns.size() > 0) { |
| //remove columns from model - will be re-added in the correct order |
| for (int i = model.getColumnCount() - 1; i > -1; i--) { |
| model.removeColumn(model.getColumn(i)); |
| } |
| |
| for (Iterator iter = newColumns.iterator(); iter.hasNext();) { |
| model.addColumn((TableColumn) iter.next()); |
| } |
| } |
| } |
| } |
| |
| /** |
| * @return |
| */ |
| public boolean isLogTreePanelVisible() { |
| return logTreePanel.isVisible(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.log4j.chainsaw.EventBatchListener#getInterestedIdentifier() |
| */ |
| public String getInterestedIdentifier() { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.log4j.chainsaw.EventBatchListener#receiveEventBatch(java.lang.String, java.util.List) |
| */ |
| public void receiveEventBatch(String identifier, List eventBatchEntrys) { |
| table.getSelectionModel().setValueIsAdjusting(true); |
| |
| boolean rowAdded = false; |
| Vector lastSelected = null; |
| |
| if (table.getSelectedRow() > -1) { |
| lastSelected = tableModel.getRow(table.getSelectedRow()); |
| } |
| |
| for (Iterator iter = eventBatchEntrys.iterator(); iter.hasNext();) { |
| ChainsawEventBatchEntry entry = (ChainsawEventBatchEntry) iter.next(); |
| |
| Vector v = formatFields(entry.getEventVector()); |
| |
| final String eventType = entry.getEventType(); |
| String level = |
| v.get( |
| ChainsawColumns.getColumnsNames().indexOf( |
| ChainsawConstants.LEVEL_COL_NAME)).toString(); |
| |
| //add the level to the appropriate list if it didn't previously exist |
| if (!((List) levelMap.get(eventType)).contains(level)) { |
| ((List) levelMap.get(eventType)).add(level); |
| } |
| |
| Map map = (HashMap) entryMap.get(getIdentifier()); |
| |
| //also add it to the unique values list |
| ((Set) map.get(ChainsawConstants.LEVEL_COL_NAME)).add(level); |
| |
| Object loggerName = |
| v.get( |
| ChainsawColumns.getColumnsNames().indexOf( |
| ChainsawConstants.LOGGER_COL_NAME)); |
| ((Set) map.get(ChainsawConstants.LOGGER_COL_NAME)).add(loggerName); |
| |
| /** |
| * EventContainer is a LoggerNameModel imp, use that for notifing |
| */ |
| tableModel.addLoggerName(loggerName.toString()); |
| |
| ((Set) map.get(ChainsawConstants.THREAD_COL_NAME)).add( |
| v.get( |
| ChainsawColumns.getColumnsNames().indexOf( |
| ChainsawConstants.THREAD_COL_NAME))); |
| ((Set) map.get(ChainsawConstants.NDC_COL_NAME)).add( |
| v.get( |
| ChainsawColumns.getColumnsNames().indexOf( |
| ChainsawConstants.NDC_COL_NAME))); |
| ((Set) map.get(ChainsawConstants.MDC_COL_NAME)).add( |
| v.get( |
| ChainsawColumns.getColumnsNames().indexOf( |
| ChainsawConstants.MDC_COL_NAME))); |
| ((Set) map.get(ChainsawConstants.CLASS_COL_NAME)).add( |
| v.get( |
| ChainsawColumns.getColumnsNames().indexOf( |
| ChainsawConstants.CLASS_COL_NAME))); |
| ((Set) map.get(ChainsawConstants.METHOD_COL_NAME)).add( |
| v.get( |
| ChainsawColumns.getColumnsNames().indexOf( |
| ChainsawConstants.METHOD_COL_NAME))); |
| ((Set) map.get(ChainsawConstants.FILE_COL_NAME)).add( |
| v.get( |
| ChainsawColumns.getColumnsNames().indexOf( |
| ChainsawConstants.FILE_COL_NAME))); |
| |
| boolean isCurrentRowAdded = tableModel.isAddRow(v, true); |
| rowAdded = rowAdded ? true : isCurrentRowAdded; |
| statusBar.receivedEvent(); |
| } |
| |
| table.getSelectionModel().setValueIsAdjusting(false); |
| |
| //tell the model to notify the count listeners |
| tableModel.notifyCountListeners(); |
| |
| if (rowAdded) { |
| tableModel.sort(); |
| |
| if (scrollToBottom.isScrolled() && !scrollToBottom.isBypassed()) { |
| table.scrollToBottom( |
| table.columnAtPoint(table.getVisibleRect().getLocation())); |
| } else { |
| if (lastSelected != null) { |
| table.scrollToRow( |
| tableModel.getRowIndex(lastSelected), |
| table.columnAtPoint(table.getVisibleRect().getLocation())); |
| } |
| } |
| } |
| } |
| } |
| |
| class TableColumnData implements Serializable { |
| static final long serialVersionUID = 5350440293110513986L; |
| private String colName; |
| private int index; |
| private int width; |
| |
| public TableColumnData(String colName, int index, int width) { |
| this.colName = colName; |
| this.index = index; |
| this.width = width; |
| } |
| |
| public String getColName() { |
| return colName; |
| } |
| |
| public int getIndex() { |
| return index; |
| } |
| |
| public int getWidth() { |
| return width; |
| } |
| |
| private void readObject(java.io.ObjectInputStream in) |
| throws IOException, ClassNotFoundException { |
| colName = (String) in.readObject(); |
| index = in.readInt(); |
| width = in.readInt(); |
| } |
| |
| private void writeObject(java.io.ObjectOutputStream out) |
| throws IOException { |
| out.writeObject(colName); |
| out.writeInt(index); |
| out.writeInt(width); |
| } |
| } |
| |
| /** |
| * Thread that periodically checks if the selected row has changed, and if |
| * it was, updates the Detail Panel with the detailed Logging information |
| */ |
| class DetailPaneUpdater { |
| private int selectedRow = -1; |
| private int lastRow = -1; |
| private final JEditorPane pane; |
| private final EventContainer model; |
| private final LogPanel panel; |
| |
| public DetailPaneUpdater( |
| LogPanel panel, JEditorPane pane, EventContainer model) { |
| this.pane = pane; |
| this.model = model; |
| this.panel = panel; |
| } |
| |
| public void setSelectedRow(int row) { |
| if (row == -1) { |
| lastRow = 0; |
| } |
| |
| selectedRow = row; |
| updateDetailPane(); |
| } |
| |
| private void updateDetailPane() { |
| String text = null; |
| |
| if (selectedRow != lastRow) { |
| if (selectedRow == -1) { |
| text = "Nothing selected"; |
| } else { |
| // TODO refactor to use a single getEvent(row) call, and use a Formatter interface for pluggable formatting |
| text = model.getDetailText(selectedRow); |
| } |
| |
| if (!((text != null) && !text.equals(""))) { |
| text = "Nothing selected"; |
| } |
| |
| lastRow = selectedRow; |
| |
| final String text2 = text; |
| SwingUtilities.invokeLater( |
| new Runnable() { |
| public void run() { |
| pane.setText(text2); |
| } |
| }); |
| SwingUtilities.invokeLater( |
| new Runnable() { |
| public void run() { |
| pane.setCaretPosition(0); |
| } |
| }); |
| } |
| } |
| } |
| |
| class TabIconHandler implements EventCountListener { |
| private final String ident; |
| private int lastCount; |
| private int currentCount; |
| |
| //the tabIconHandler is associated with a new tab, and a new tab always |
| //has new events |
| private boolean hasNewEvents = true; |
| ImageIcon NEW_EVENTS = new ImageIcon(ChainsawIcons.TOOL_TIP); |
| ImageIcon HAS_EVENTS = new ImageIcon(ChainsawIcons.INFO); |
| |
| public TabIconHandler(final String ident) { |
| this.ident = ident; |
| |
| new Thread( |
| new Runnable() { |
| public void run() { |
| while (true) { |
| //if this tab is active, remove the icon |
| if ( |
| (tabbedPane.getSelectedIndex() > -1) |
| && (tabbedPane.getSelectedIndex() == tabbedPane.indexOfTab( |
| ident))) { |
| tabbedPane.setIconAt(tabbedPane.indexOfTab(ident), null); |
| |
| //reset fields so no icon will display |
| lastCount = currentCount; |
| hasNewEvents = false; |
| } else { |
| //don't process undocked tabs |
| if (tabbedPane.indexOfTab(ident) > -1) { |
| //if the tab is not active and the counts don't match, set the new events icon |
| if (lastCount != currentCount) { |
| tabbedPane.setIconAt( |
| tabbedPane.indexOfTab(ident), NEW_EVENTS); |
| lastCount = currentCount; |
| hasNewEvents = true; |
| } else { |
| if (hasNewEvents) { |
| tabbedPane.setIconAt( |
| tabbedPane.indexOfTab(ident), HAS_EVENTS); |
| } |
| } |
| } |
| } |
| |
| try { |
| Thread.sleep(handler.getQueueInterval() + 1000); |
| } catch (InterruptedException ie) { |
| } |
| } |
| } |
| }).start(); |
| } |
| |
| public void eventCountChanged(int currentCount, int totalCount) { |
| this.currentCount = currentCount; |
| } |
| } |
| |
| class ScrollToBottom extends Thread { |
| boolean scrollToBottom; |
| boolean bypassed; |
| |
| public ScrollToBottom(boolean scrollToBottom) { |
| this.scrollToBottom = scrollToBottom; |
| } |
| |
| public void scroll(boolean scrollToBottom) { |
| this.scrollToBottom = scrollToBottom; |
| } |
| |
| public boolean isScrolled() { |
| return scrollToBottom; |
| } |
| |
| public void bypass(boolean bypassed) { |
| this.bypassed = bypassed; |
| } |
| |
| public boolean isBypassed() { |
| return bypassed; |
| } |
| } |
| |
| //if columnmoved or columnremoved callback received, re-apply table's sort index based |
| //sort column name |
| class ChainsawTableColumnModelListener implements TableColumnModelListener { |
| private JSortTable table; |
| |
| public ChainsawTableColumnModelListener(JSortTable table) { |
| this.table = table; |
| } |
| |
| public void columnAdded(TableColumnModelEvent e) { |
| } |
| |
| public void columnRemoved(TableColumnModelEvent e) { |
| table.updateSortedColumn(); |
| } |
| |
| public void columnMoved(TableColumnModelEvent e) { |
| table.updateSortedColumn(); |
| } |
| |
| public void columnMarginChanged(ChangeEvent e) { |
| } |
| |
| public void columnSelectionChanged(ListSelectionEvent e) { |
| } |
| } |
| } |