Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.log4j.chainsaw; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Frame; import java.awt.Point; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.io.IOException; import java.lang.Thread.UncaughtExceptionHandler; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.security.AllPermission; import java.security.CodeSource; import java.security.PermissionCollection; import java.security.Permissions; import java.security.Policy; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JEditorPane; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JToolBar; import javax.swing.JWindow; import javax.swing.KeyStroke; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.ToolTipManager; import javax.swing.UIManager; import javax.swing.WindowConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.EventListenerList; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; import org.apache.log4j.Appender; import org.apache.log4j.AppenderSkeleton; import org.apache.log4j.Level; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.apache.log4j.LoggerRepositoryExImpl; import org.apache.log4j.chainsaw.color.RuleColorizer; import org.apache.log4j.chainsaw.dnd.FileDnDTarget; import org.apache.log4j.chainsaw.help.HelpManager; import org.apache.log4j.chainsaw.help.Tutorial; import org.apache.log4j.chainsaw.helper.SwingHelper; import org.apache.log4j.chainsaw.icons.ChainsawIcons; import org.apache.log4j.chainsaw.icons.LineIconFactory; import org.apache.log4j.chainsaw.messages.MessageCenter; import org.apache.log4j.chainsaw.osx.OSXIntegration; import org.apache.log4j.chainsaw.plugins.PluginClassLoaderFactory; import org.apache.log4j.chainsaw.prefs.LoadSettingsEvent; import org.apache.log4j.chainsaw.prefs.MRUFileListPreferenceSaver; 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.chainsaw.receivers.ReceiversHelper; import org.apache.log4j.chainsaw.receivers.ReceiversPanel; import org.apache.log4j.chainsaw.vfs.VFSLogFilePatternReceiver; import org.apache.log4j.net.SocketNodeEventListener; import org.apache.log4j.plugins.Plugin; import org.apache.log4j.plugins.PluginEvent; import org.apache.log4j.plugins.PluginListener; import org.apache.log4j.plugins.PluginRegistry; import org.apache.log4j.plugins.Receiver; import org.apache.log4j.rewrite.PropertyRewritePolicy; import org.apache.log4j.rewrite.RewriteAppender; import org.apache.log4j.rule.ExpressionRule; import org.apache.log4j.rule.Rule; import org.apache.log4j.spi.Decoder; import org.apache.log4j.spi.LoggerRepository; import org.apache.log4j.spi.LoggerRepositoryEx; import org.apache.log4j.spi.LoggingEvent; import org.apache.log4j.spi.RepositorySelector; import org.apache.log4j.xml.DOMConfigurator; import org.apache.log4j.xml.XMLDecoder; /** * 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. * * NOTE: Some of Chainsaw's application initialization should be performed prior * to activating receivers and the logging framework used to perform self-logging. * * DELAY as much as possible the logging framework initialization process, * currently initialized by the creation of a ChainsawAppenderHandler. * * @author Scott Deboy <sdeboy@apache.org> * @author Paul Smith <psmith@apache.org> * */ public class LogUI extends JFrame implements ChainsawViewer, SettingsListener { 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 ChainsawSplash splash; private static final double DEFAULT_MAIN_RECEIVER_SPLIT_LOCATION = 0.85d; private final JFrame preferencesFrame = new JFrame(); private boolean noReceiversDefined; private ReceiversPanel receiversPanel; private ChainsawTabbedPane tabbedPane; private JToolBar toolbar; private ChainsawStatusBar statusBar; private ApplicationPreferenceModel applicationPreferenceModel; private ApplicationPreferenceModelPanel applicationPreferenceModelPanel; private final Map tableModelMap = new HashMap(); private final Map tableMap = new HashMap(); private final List filterableColumns = new ArrayList(); private final Map panelMap = new HashMap(); ChainsawAppenderHandler handler; private ChainsawToolBarAndMenus tbms; private ChainsawAbout aboutBox; private final SettingsManager sm = SettingsManager.getInstance(); private final JFrame tutorialFrame = new JFrame("Chainsaw Tutorial"); private JSplitPane mainReceiverSplitPane; private double lastMainReceiverSplitLocation = DEFAULT_MAIN_RECEIVER_SPLIT_LOCATION; private final List identifierPanels = new ArrayList(); private int dividerSize; private int cyclicBufferSize; private static Logger logger; private static String configurationURLAppArg; /** * 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 = null; /** * Clients can register a ShutdownListener to be notified when the user has * requested Chainsaw to exit. */ private EventListenerList shutdownListenerList = new EventListenerList(); private WelcomePanel welcomePanel; private static final Object repositorySelectorGuard = new Object(); private static final LoggerRepositoryExImpl repositoryExImpl = new LoggerRepositoryExImpl( LogManager.getLoggerRepository()); private PluginRegistry pluginRegistry; //map of tab names to rulecolorizers private Map allColorizers = new HashMap(); private ReceiverConfigurationPanel receiverConfigurationPanel = new ReceiverConfigurationPanel(); /** * Constructor which builds up all the visual elements of the frame including * the Menu bar */ public LogUI() { super("Chainsaw"); setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); if (ChainsawIcons.WINDOW_ICON != null) { setIconImage(new ImageIcon(ChainsawIcons.WINDOW_ICON).getImage()); } } private static final void showSplash(Frame owner) { splash = new ChainsawSplash(owner); SwingHelper.centerOnScreen(splash); 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) { if (args.length > 0) { configurationURLAppArg = args[0]; } if (OSXIntegration.IS_OSX) { System.setProperty("apple.laf.useScreenMenuBar", "true"); } LogManager.setRepositorySelector(new RepositorySelector() { public LoggerRepository getLoggerRepository() { return repositoryExImpl; } }, repositorySelectorGuard); final ApplicationPreferenceModel model = new ApplicationPreferenceModel(); SettingsManager.getInstance().configure(new ApplicationPreferenceModelSaver(model)); //if a configuration URL param was provided, set the configuration URL field to null if (configurationURLAppArg != null) { model.setBypassConfigurationURL(configurationURLAppArg); } EventQueue.invokeLater(new Runnable() { public void run() { String lookAndFeelClassName = model.getLookAndFeelClassName(); if (lookAndFeelClassName == null || lookAndFeelClassName.trim().equals("")) { String osName = System.getProperty("os.name"); if (osName.toLowerCase(Locale.ENGLISH).startsWith("mac")) { //no need to assign look and feel } else if (osName.toLowerCase(Locale.ENGLISH).startsWith("windows")) { lookAndFeelClassName = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel"; model.setLookAndFeelClassName(lookAndFeelClassName); } else if (osName.toLowerCase(Locale.ENGLISH).startsWith("linux")) { lookAndFeelClassName = "com.sun.java.swing.plaf.gtk.GTKLookAndFeel"; model.setLookAndFeelClassName(lookAndFeelClassName); } } if (lookAndFeelClassName != null && !(lookAndFeelClassName.trim().equals(""))) { loadLookAndFeelUsingPluginClassLoader(lookAndFeelClassName); } createChainsawGUI(model, null); } }); } /** * Creates, activates, and then shows the Chainsaw GUI, optionally showing * the splash screen, and using the passed shutdown action when the user * requests to exit the application (if null, then Chainsaw will exit the vm) * * @param model * @param newShutdownAction * DOCUMENT ME! */ public static void createChainsawGUI(ApplicationPreferenceModel model, Action newShutdownAction) { if (model.isOkToRemoveSecurityManager()) { MessageCenter.getInstance() .addMessage("User has authorised removal of Java Security Manager via preferences"); System.setSecurityManager(null); // this SHOULD set the Policy/Permission stuff for any // code loaded from our custom classloader. // crossing fingers... Policy.setPolicy(new Policy() { public void refresh() { } public PermissionCollection getPermissions(CodeSource codesource) { Permissions perms = new Permissions(); perms.add(new AllPermission()); return (perms); } }); } final LogUI logUI = new LogUI(); logUI.applicationPreferenceModel = model; if (model.isShowSplash()) { showSplash(logUI); } logUI.cyclicBufferSize = model.getCyclicBufferSize(); logUI.pluginRegistry = repositoryExImpl.getPluginRegistry(); logUI.handler = new ChainsawAppenderHandler(); logUI.handler.addEventBatchListener(logUI.new NewTabEventBatchReceiver()); /** * TODO until we work out how JoranConfigurator might be able to have * configurable class loader, if at all. For now we temporarily replace the * TCCL so that Plugins that need access to resources in * the Plugins directory can find them (this is particularly * important for the Web start version of Chainsaw */ //configuration initialized here logUI.ensureChainsawAppenderHandlerAdded(); logger = LogManager.getLogger(LogUI.class); //set hostname, application and group properties which will cause Chainsaw and other apache-generated //logging events to route (by default) to a tab named 'chainsaw-log' PropertyRewritePolicy policy = new PropertyRewritePolicy(); policy.setProperties("hostname=chainsaw,application=log,group=chainsaw"); RewriteAppender rewriteAppender = new RewriteAppender(); rewriteAppender.setRewritePolicy(policy); Enumeration appenders = Logger.getLogger("org.apache").getAllAppenders(); if (!appenders.hasMoreElements()) { appenders = Logger.getRootLogger().getAllAppenders(); } while (appenders.hasMoreElements()) { Appender nextAppender = (Appender) appenders.nextElement(); rewriteAppender.addAppender(nextAppender); } Logger.getLogger("org.apache").removeAllAppenders(); Logger.getLogger("org.apache").addAppender(rewriteAppender); Logger.getLogger("org.apache").setAdditivity(false); //commons-vfs uses httpclient for http filesystem support, route this to the chainsaw-log tab as well appenders = Logger.getLogger("httpclient").getAllAppenders(); if (!appenders.hasMoreElements()) { appenders = Logger.getRootLogger().getAllAppenders(); } while (appenders.hasMoreElements()) { Appender nextAppender = (Appender) appenders.nextElement(); rewriteAppender.addAppender(nextAppender); } Logger.getLogger("httpclient").removeAllAppenders(); Logger.getLogger("httpclient").addAppender(rewriteAppender); Logger.getLogger("httpclient").setAdditivity(false); //set the commons.vfs.cache logger to info, since it can contain password information Logger.getLogger("org.apache.commons.vfs.cache").setLevel(Level.INFO); Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() { public void uncaughtException(Thread t, Throwable e) { e.printStackTrace(); logger.error("Uncaught exception in thread " + t, e); } }); String config = configurationURLAppArg; if (config != null) { logger.info("Command-line configuration arg provided (overriding auto-configuration URL) - using: " + config); } else { config = model.getConfigurationURL(); } if (config != null && (!config.trim().equals(""))) { config = config.trim(); try { URL configURL = new URL(config); logger.info("Using '" + config + "' for auto-configuration"); logUI.loadConfigurationUsingPluginClassLoader(configURL); } catch (MalformedURLException e) { logger.error("Initial configuration - failed to convert config string to url", e); } catch (IOException e) { logger.error("Unable to access auto-configuration URL: " + config); } } //register a listener to load the configuration when it changes (avoid having to restart Chainsaw when applying a new configuration) //this doesn't remove receivers from receivers panel, it just triggers DOMConfigurator.configure. model.addPropertyChangeListener("configurationURL", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { String newConfiguration = evt.getNewValue().toString(); if (newConfiguration != null && !(newConfiguration.trim().equals(""))) { newConfiguration = newConfiguration.trim(); try { logger.info("loading updated configuration: " + newConfiguration); URL newConfigurationURL = new URL(newConfiguration); File file = new File(newConfigurationURL.toURI()); if (file.exists()) { logUI.loadConfigurationUsingPluginClassLoader(newConfigurationURL); } else { logger.info("Updated configuration but file does not exist"); } } catch (MalformedURLException e) { logger.error("Updated configuration - failed to convert config string to URL", e); } catch (URISyntaxException e) { logger.error("Updated configuration - failed to convert config string to URL", e); } } } }); LogManager.getRootLogger().setLevel(Level.TRACE); EventQueue.invokeLater(new Runnable() { public void run() { logUI.activateViewer(); } }); logger.info("SecurityManager is now: " + System.getSecurityManager()); if (newShutdownAction != null) { logUI.setShutdownAction(newShutdownAction); } else { logUI.setShutdownAction(new AbstractAction() { public void actionPerformed(ActionEvent e) { System.exit(0); } }); } } /** * Allow Chainsaw v2 to be ran in-process (configured as a ChainsawAppender) * NOTE: Closing Chainsaw will NOT stop the application generating the events. * @param appender * */ public void activateViewer(ChainsawAppender appender) { if (OSXIntegration.IS_OSX) { System.setProperty("apple.laf.useScreenMenuBar", "true"); } LogManager.setRepositorySelector(new RepositorySelector() { public LoggerRepository getLoggerRepository() { return repositoryExImpl; } }, repositorySelectorGuard); //if Chainsaw is launched as an appender, ensure the root logger level is TRACE LogManager.getRootLogger().setLevel(Level.TRACE); final ApplicationPreferenceModel model = new ApplicationPreferenceModel(); SettingsManager.getInstance().configure(new ApplicationPreferenceModelSaver(model)); cyclicBufferSize = model.getCyclicBufferSize(); handler = new ChainsawAppenderHandler(appender); handler.addEventBatchListener(new NewTabEventBatchReceiver()); logger = LogManager.getLogger(LogUI.class); setShutdownAction(new AbstractAction() { public void actionPerformed(ActionEvent e) { } }); applicationPreferenceModel = new ApplicationPreferenceModel(); SettingsManager.getInstance().configure(new ApplicationPreferenceModelSaver(model)); EventQueue.invokeLater(new Runnable() { public void run() { loadLookAndFeelUsingPluginClassLoader(model.getLookAndFeelClassName()); createChainsawGUI(model, null); getApplicationPreferenceModel().apply(model); activateViewer(); } }); } /** * Initialises the menu's and toolbars, but does not actually create any of * the main panel components. * */ private void initGUI() { setupHelpSystem(); statusBar = new ChainsawStatusBar(this); setupReceiverPanel(); setToolBarAndMenus(new ChainsawToolBarAndMenus(this)); toolbar = getToolBarAndMenus().getToolbar(); setJMenuBar(getToolBarAndMenus().getMenubar()); setTabbedPane(new ChainsawTabbedPane()); getSettingsManager().addSettingsListener(getTabbedPane()); getSettingsManager().configure(getTabbedPane()); /** * This adds Drag & Drop capability to Chainsaw */ FileDnDTarget dnDTarget = new FileDnDTarget(tabbedPane); dnDTarget.addPropertyChangeListener("fileList", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { final List fileList = (List) evt.getNewValue(); Thread thread = new Thread(new Runnable() { public void run() { logger.debug("Loading files: " + fileList); for (Iterator iter = fileList.iterator(); iter.hasNext();) { File file = (File) iter.next(); final Decoder decoder = new XMLDecoder(); try { getStatusBar().setMessage("Loading " + file.getAbsolutePath() + "..."); FileLoadAction.importURL(handler, decoder, file.getName(), file.toURI().toURL()); } catch (Exception e) { String errorMsg = "Failed to import a file"; logger.error(errorMsg, e); getStatusBar().setMessage(errorMsg); } } } }); thread.setPriority(Thread.MIN_PRIORITY); thread.start(); } }); applicationPreferenceModelPanel = new ApplicationPreferenceModelPanel(applicationPreferenceModel); applicationPreferenceModelPanel.setOkCancelActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { preferencesFrame.setVisible(false); } }); KeyStroke escape = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false); Action closeAction = new AbstractAction() { public void actionPerformed(ActionEvent e) { preferencesFrame.setVisible(false); } }; preferencesFrame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escape, "ESCAPE"); preferencesFrame.getRootPane().getActionMap().put("ESCAPE", closeAction); OSXIntegration.init(this); } private void initPlugins(PluginRegistry pluginRegistry) { pluginRegistry.addPluginListener(new PluginListener() { public void pluginStarted(PluginEvent e) { if (e.getPlugin() instanceof JComponent) { JComponent plugin = (JComponent) e.getPlugin(); getTabbedPane().addANewTab(plugin.getName(), plugin, null, null); getPanelMap().put(plugin.getName(), plugin); } } public void pluginStopped(PluginEvent e) { //TODO remove the plugin from the gui } }); // TODO this should all be in a config file // ChainsawCentral cc = new ChainsawCentral(); // pluginRegistry.addPlugin(cc); // cc.activateOptions(); try { Class pluginClass = Class.forName("org.apache.log4j.chainsaw.zeroconf.ZeroConfPlugin"); Plugin plugin = (Plugin) pluginClass.newInstance(); pluginRegistry.addPlugin(plugin); plugin.activateOptions(); MessageCenter.getInstance().getLogger().info("Looks like ZeroConf is available... WooHoo!"); } catch (Throwable e) { MessageCenter.getInstance().getLogger().error("Doesn't look like ZeroConf is available", e); } } private void setupReceiverPanel() { receiversPanel = new ReceiversPanel(); receiversPanel.addPropertyChangeListener("visible", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { getApplicationPreferenceModel().setReceivers(((Boolean) evt.getNewValue()).booleanValue()); } }); } /** * Initialises the Help system and the WelcomePanel * */ private void setupHelpSystem() { welcomePanel = new WelcomePanel(); JToolBar tb = welcomePanel.getToolbar(); tb.add(new SmallButton(new AbstractAction("Tutorial", new ImageIcon(ChainsawIcons.HELP)) { public void actionPerformed(ActionEvent e) { setupTutorial(); } })); tb.addSeparator(); final Action exampleConfigAction = new AbstractAction("View example Receiver configuration") { public void actionPerformed(ActionEvent e) { HelpManager.getInstance().setHelpURL(ChainsawConstants.EXAMPLE_CONFIG_URL); } }; exampleConfigAction.putValue(Action.SHORT_DESCRIPTION, "Displays an example Log4j configuration file with several Receivers defined."); JButton exampleButton = new SmallButton(exampleConfigAction); tb.add(exampleButton); tb.add(Box.createHorizontalGlue()); /** * Setup a listener on the HelpURL property and automatically change the WelcomePages URL * to it. */ HelpManager.getInstance().addPropertyChangeListener("helpURL", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { URL newURL = (URL) evt.getNewValue(); if (newURL != null) { welcomePanel.setURL(newURL); ensureWelcomePanelVisible(); } } }); } private void ensureWelcomePanelVisible() { // ensure that the Welcome Panel is made visible if (!getTabbedPane().containsWelcomePanel()) { addWelcomePanel(); } getTabbedPane().setSelectedComponent(welcomePanel); } /** * Given the load event, configures the size/location of the main window etc * etc. * * @param event * DOCUMENT ME! */ public void loadSettings(LoadSettingsEvent event) { setLocation(event.asInt(LogUI.MAIN_WINDOW_X), event.asInt(LogUI.MAIN_WINDOW_Y)); int width = event.asInt(LogUI.MAIN_WINDOW_WIDTH); int height = event.asInt(LogUI.MAIN_WINDOW_HEIGHT); if (width == -1 && height == -1) { width = Toolkit.getDefaultToolkit().getScreenSize().width; height = Toolkit.getDefaultToolkit().getScreenSize().height; setSize(width, height); setExtendedState(getExtendedState() | MAXIMIZED_BOTH); } else { setSize(width, height); } getToolBarAndMenus().stateChange(); RuleColorizer colorizer = new RuleColorizer(); colorizer.loadColorSettings(ChainsawConstants.DEFAULT_COLOR_RULE_NAME); allColorizers.put(ChainsawConstants.DEFAULT_COLOR_RULE_NAME, colorizer); if (event.getSetting("SavedConfig.logFormat") != null) { receiverConfigurationPanel.getModel().setLogFormat(event.getSetting("SavedConfig.logFormat")); } } /** * Ensures the location/size of the main window is stored with the settings * * @param event * DOCUMENT ME! */ 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()); RuleColorizer colorizer = (RuleColorizer) allColorizers.get(ChainsawConstants.DEFAULT_COLOR_RULE_NAME); colorizer.saveColorSettings(ChainsawConstants.DEFAULT_COLOR_RULE_NAME); if (receiverConfigurationPanel.getModel().getLogFormat() != null) { event.saveSetting("SavedConfig.logFormat", receiverConfigurationPanel.getModel().getLogFormat()); } } /** * 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() { LoggerRepository repo = LogManager.getLoggerRepository(); if (repo instanceof LoggerRepositoryEx) { this.pluginRegistry = ((LoggerRepositoryEx) repo).getPluginRegistry(); } initGUI(); initPrefModelListeners(); /** * We add a simple appender to the MessageCenter logger * so that each message is displayed in the Status bar */ MessageCenter.getInstance().getLogger().addAppender(new AppenderSkeleton() { protected void append(LoggingEvent event) { getStatusBar().setMessage(event.getMessage().toString()); } public void close() { } public boolean requiresLayout() { return false; } }); initSocketConnectionListener(); if (pluginRegistry.getPlugins(Receiver.class).size() == 0) { noReceiversDefined = true; } getFilterableColumns().add(ChainsawConstants.LEVEL_COL_NAME); getFilterableColumns().add(ChainsawConstants.LOGGER_COL_NAME); getFilterableColumns().add(ChainsawConstants.THREAD_COL_NAME); getFilterableColumns().add(ChainsawConstants.NDC_COL_NAME); getFilterableColumns().add(ChainsawConstants.PROPERTIES_COL_NAME); getFilterableColumns().add(ChainsawConstants.CLASS_COL_NAME); getFilterableColumns().add(ChainsawConstants.METHOD_COL_NAME); getFilterableColumns().add(ChainsawConstants.FILE_COL_NAME); getFilterableColumns().add(ChainsawConstants.NONE_COL_NAME); JPanel panePanel = new JPanel(); panePanel.setLayout(new BorderLayout(2, 2)); getContentPane().setLayout(new BorderLayout()); getTabbedPane().addChangeListener(getToolBarAndMenus()); getTabbedPane().addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { LogPanel thisLogPanel = getCurrentLogPanel(); if (thisLogPanel != null) { thisLogPanel.updateStatusBar(); } } }); KeyStroke ksRight = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); KeyStroke ksLeft = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); KeyStroke ksGotoLine = KeyStroke.getKeyStroke(KeyEvent.VK_G, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); getTabbedPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ksRight, "MoveRight"); getTabbedPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ksLeft, "MoveLeft"); getTabbedPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ksGotoLine, "GotoLine"); Action moveRight = new AbstractAction() { public void actionPerformed(ActionEvent e) { int temp = getTabbedPane().getSelectedIndex(); ++temp; if (temp != getTabbedPane().getTabCount()) { getTabbedPane().setSelectedTab(temp); } } }; Action moveLeft = new AbstractAction() { public void actionPerformed(ActionEvent e) { int temp = getTabbedPane().getSelectedIndex(); --temp; if (temp > -1) { getTabbedPane().setSelectedTab(temp); } } }; Action gotoLine = new AbstractAction() { public void actionPerformed(ActionEvent e) { String inputLine = JOptionPane.showInputDialog(LogUI.this, "Enter the line number to go:", "Goto Line", -1); try { int lineNumber = Integer.parseInt(inputLine); int row = getCurrentLogPanel().setSelectedEvent(lineNumber); if (row == -1) { JOptionPane.showMessageDialog(LogUI.this, "You have entered an invalid line number", "Error", 0); } } catch (NumberFormatException nfe) { JOptionPane.showMessageDialog(LogUI.this, "You have entered an invalid line number", "Error", 0); } } }; getTabbedPane().getActionMap().put("MoveRight", moveRight); getTabbedPane().getActionMap().put("MoveLeft", moveLeft); getTabbedPane().getActionMap().put("GotoLine", gotoLine); /** * We listen for double clicks, and auto-undock currently selected Tab if * the mouse event location matches the currently selected tab */ getTabbedPane().addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { super.mouseClicked(e); if ((e.getClickCount() > 1) && ((e.getModifiers() & InputEvent.BUTTON1_MASK) > 0)) { int tabIndex = getTabbedPane().getSelectedIndex(); if ((tabIndex != -1) && (tabIndex == getTabbedPane().getSelectedIndex())) { LogPanel logPanel = getCurrentLogPanel(); if (logPanel != null) { logPanel.undock(); } } } } }); panePanel.add(getTabbedPane()); addWelcomePanel(); getContentPane().add(toolbar, BorderLayout.NORTH); getContentPane().add(statusBar, BorderLayout.SOUTH); mainReceiverSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, panePanel, receiversPanel); dividerSize = mainReceiverSplitPane.getDividerSize(); mainReceiverSplitPane.setDividerLocation(-1); getContentPane().add(mainReceiverSplitPane, BorderLayout.CENTER); /** * We need to make sure that all the internal GUI components have been added to the * JFrame so that any plugns that get activated during initPlugins(...) method * have access to inject menus */ initPlugins(pluginRegistry); mainReceiverSplitPane.setResizeWeight(1.0); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent event) { exit(); } }); preferencesFrame.setTitle("'Application-wide Preferences"); preferencesFrame.setIconImage(((ImageIcon) ChainsawIcons.ICON_PREFERENCES).getImage()); preferencesFrame.getContentPane().add(applicationPreferenceModelPanel); preferencesFrame.setSize(750, 520); Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); preferencesFrame.setLocation(new Point((screenDimension.width / 2) - (preferencesFrame.getSize().width / 2), (screenDimension.height / 2) - (preferencesFrame.getSize().height / 2))); pack(); final JPopupMenu tabPopup = new JPopupMenu(); final Action hideCurrentTabAction = new AbstractAction("Hide") { public void actionPerformed(ActionEvent e) { Component selectedComp = getTabbedPane().getSelectedComponent(); if (selectedComp instanceof LogPanel) { displayPanel(getCurrentLogPanel().getIdentifier(), false); tbms.stateChange(); } else { getTabbedPane().remove(selectedComp); } } }; final Action hideOtherTabsAction = new AbstractAction("Hide Others") { public void actionPerformed(ActionEvent e) { Component selectedComp = getTabbedPane().getSelectedComponent(); String currentName; if (selectedComp instanceof LogPanel) { currentName = getCurrentLogPanel().getIdentifier(); } else if (selectedComp instanceof WelcomePanel) { currentName = ChainsawTabbedPane.WELCOME_TAB; } else { currentName = ChainsawTabbedPane.ZEROCONF; } int count = getTabbedPane().getTabCount(); int index = 0; for (int i = 0; i < count; i++) { String name = getTabbedPane().getTitleAt(index); if (getPanelMap().keySet().contains(name) && !name.equals(currentName)) { displayPanel(name, false); tbms.stateChange(); } else { index++; } } } }; Action showHiddenTabsAction = new AbstractAction("Show All Hidden") { public void actionPerformed(ActionEvent e) { for (Iterator iter = getPanels().entrySet().iterator(); iter.hasNext();) { Map.Entry entry = (Map.Entry) iter.next(); Boolean docked = (Boolean) entry.getValue(); if (docked.booleanValue()) { String identifier = (String) entry.getKey(); int count = getTabbedPane().getTabCount(); boolean found = false; for (int i = 0; i < count; i++) { String name = getTabbedPane().getTitleAt(i); if (name.equals(identifier)) { found = true; break; } } if (!found) { displayPanel(identifier, true); tbms.stateChange(); } } } } }; tabPopup.add(hideCurrentTabAction); tabPopup.add(hideOtherTabsAction); tabPopup.addSeparator(); tabPopup.add(showHiddenTabsAction); final PopupListener tabPopupListener = new PopupListener(tabPopup); getTabbedPane().addMouseListener(tabPopupListener); this.handler.addPropertyChangeListener("dataRate", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { double dataRate = ((Double) evt.getNewValue()).doubleValue(); statusBar.setDataRate(dataRate); } }); getSettingsManager().addSettingsListener(this); getSettingsManager().addSettingsListener(MRUFileListPreferenceSaver.getInstance()); getSettingsManager().addSettingsListener(receiversPanel); try { //if an uncaught exception is thrown, allow the UI to continue to load getSettingsManager().loadSettings(); } catch (Exception e) { e.printStackTrace(); } //app preferences have already been loaded (and configuration url possibly set to blank if being overridden) //but we need a listener so the settings will be saved on exit (added after loadsettings was called) getSettingsManager().addSettingsListener(new ApplicationPreferenceModelSaver(applicationPreferenceModel)); setVisible(true); if (applicationPreferenceModel.isReceivers()) { showReceiverPanel(); } else { hideReceiverPanel(); } removeSplash(); synchronized (initializationLock) { isGUIFullyInitialized = true; initializationLock.notifyAll(); } if (noReceiversDefined && applicationPreferenceModel.isShowNoReceiverWarning()) { SwingHelper.invokeOnEDT(new Runnable() { public void run() { showReceiverConfigurationPanel(); } }); } Container container = tutorialFrame.getContentPane(); final JEditorPane tutorialArea = new JEditorPane(); tutorialArea.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5)); tutorialArea.setEditable(false); container.setLayout(new BorderLayout()); try { tutorialArea.setPage(ChainsawConstants.TUTORIAL_URL); JTextComponentFormatter.applySystemFontAndSize(tutorialArea); container.add(new JScrollPane(tutorialArea), BorderLayout.CENTER); } catch (Exception e) { MessageCenter.getInstance().getLogger().error("Error occurred loading the Tutorial", e); } tutorialFrame.setIconImage(new ImageIcon(ChainsawIcons.HELP).getImage()); tutorialFrame.setSize(new Dimension(640, 480)); final Action startTutorial = new AbstractAction("Start Tutorial", new ImageIcon(ChainsawIcons.ICON_RESUME_RECEIVER)) { public void actionPerformed(ActionEvent e) { if (JOptionPane.showConfirmDialog(null, "This will start 3 \"Generator\" receivers for use in the Tutorial. Is that ok?", "Confirm", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { new Thread(new Tutorial()).start(); putValue("TutorialStarted", Boolean.TRUE); } else { putValue("TutorialStarted", Boolean.FALSE); } } }; final Action stopTutorial = new AbstractAction("Stop Tutorial", new ImageIcon(ChainsawIcons.ICON_STOP_RECEIVER)) { public void actionPerformed(ActionEvent e) { if (JOptionPane.showConfirmDialog(null, "This will stop all of the \"Generator\" receivers used in the Tutorial, but leave any other Receiver untouched. Is that ok?", "Confirm", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { new Thread(new Runnable() { public void run() { LoggerRepository repo = LogManager.getLoggerRepository(); if (repo instanceof LoggerRepositoryEx) { PluginRegistry pluginRegistry = ((LoggerRepositoryEx) repo).getPluginRegistry(); List list = pluginRegistry.getPlugins(Generator.class); for (Iterator iter = list.iterator(); iter.hasNext();) { Plugin plugin = (Plugin) iter.next(); pluginRegistry.stopPlugin(plugin.getName()); } } } }).start(); setEnabled(false); startTutorial.putValue("TutorialStarted", Boolean.FALSE); } } }; stopTutorial.putValue(Action.SHORT_DESCRIPTION, "Removes all of the Tutorials Generator Receivers, leaving all other Receivers untouched"); startTutorial.putValue(Action.SHORT_DESCRIPTION, "Begins the Tutorial, starting up some Generator Receivers so you can see Chainsaw in action"); stopTutorial.setEnabled(false); final SmallToggleButton startButton = new SmallToggleButton(startTutorial); PropertyChangeListener pcl = new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { stopTutorial.setEnabled(((Boolean) startTutorial.getValue("TutorialStarted")).equals(Boolean.TRUE)); startButton.setSelected(stopTutorial.isEnabled()); } }; startTutorial.addPropertyChangeListener(pcl); stopTutorial.addPropertyChangeListener(pcl); pluginRegistry.addPluginListener(new PluginListener() { public void pluginStarted(PluginEvent e) { } public void pluginStopped(PluginEvent e) { List list = pluginRegistry.getPlugins(Generator.class); if (list.size() == 0) { startTutorial.putValue("TutorialStarted", Boolean.FALSE); } } }); final SmallButton stopButton = new SmallButton(stopTutorial); final JToolBar tutorialToolbar = new JToolBar(); tutorialToolbar.setFloatable(false); tutorialToolbar.add(startButton); tutorialToolbar.add(stopButton); container.add(tutorialToolbar, BorderLayout.NORTH); tutorialArea.addHyperlinkListener(new HyperlinkListener() { public void hyperlinkUpdate(HyperlinkEvent e) { if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { if (e.getDescription().equals("StartTutorial")) { startTutorial.actionPerformed(null); } else if (e.getDescription().equals("StopTutorial")) { stopTutorial.actionPerformed(null); } else { try { tutorialArea.setPage(e.getURL()); } catch (IOException e1) { MessageCenter.getInstance().getLogger() .error("Failed to change the URL for the Tutorial", e1); } } } } }); /** * loads the saved tab settings and if there are hidden tabs, * hide those tabs out of currently loaded tabs.. */ if (!getTabbedPane().tabSetting.isWelcome()) { displayPanel(ChainsawTabbedPane.WELCOME_TAB, false); } if (!getTabbedPane().tabSetting.isZeroconf()) { displayPanel(ChainsawTabbedPane.ZEROCONF, false); } tbms.stateChange(); } /** * Display the log tree pane, using the last known divider location */ private void showReceiverPanel() { mainReceiverSplitPane.setDividerSize(dividerSize); mainReceiverSplitPane.setDividerLocation(lastMainReceiverSplitLocation); receiversPanel.setVisible(true); mainReceiverSplitPane.repaint(); } /** * Hide the log tree pane, holding the current divider location for later use */ private void hideReceiverPanel() { //subtract one to make sizes match int currentSize = mainReceiverSplitPane.getWidth() - mainReceiverSplitPane.getDividerSize(); if (mainReceiverSplitPane.getDividerLocation() > -1) { if (!(((mainReceiverSplitPane.getDividerLocation() + 1) == currentSize) || ((mainReceiverSplitPane.getDividerLocation() - 1) == 0))) { lastMainReceiverSplitLocation = ((double) mainReceiverSplitPane.getDividerLocation() / currentSize); } } mainReceiverSplitPane.setDividerSize(0); receiversPanel.setVisible(false); mainReceiverSplitPane.repaint(); } private void initSocketConnectionListener() { final SocketNodeEventListener socketListener = new SocketNodeEventListener() { public void socketOpened(String remoteInfo) { statusBar.remoteConnectionReceived(remoteInfo); } public void socketClosedEvent(Exception e) { MessageCenter.getInstance().getLogger().info("Connection lost! :: " + e.getMessage()); } }; PluginListener pluginListener = new PluginListener() { public void pluginStarted(PluginEvent e) { MessageCenter.getInstance().getLogger().info(e.getPlugin().getName() + " started!"); Method method = getAddListenerMethod(e.getPlugin()); if (method != null) { try { method.invoke(e.getPlugin(), new Object[] { socketListener }); } catch (Exception ex) { MessageCenter.getInstance().getLogger().error("Failed to add a SocketNodeEventListener", ex); } } } Method getRemoveListenerMethod(Plugin p) { try { return p.getClass().getMethod("removeSocketNodeEventListener", new Class[] { SocketNodeEventListener.class }); } catch (Exception e) { return null; } } Method getAddListenerMethod(Plugin p) { try { return p.getClass().getMethod("addSocketNodeEventListener", new Class[] { SocketNodeEventListener.class }); } catch (Exception e) { return null; } } public void pluginStopped(PluginEvent e) { Method method = getRemoveListenerMethod(e.getPlugin()); if (method != null) { try { method.invoke(e.getPlugin(), new Object[] { socketListener }); } catch (Exception ex) { MessageCenter.getInstance().getLogger().error("Failed to remove SocketNodeEventListener", ex); } } MessageCenter.getInstance().getLogger().info(e.getPlugin().getName() + " stopped!"); } }; pluginRegistry.addPluginListener(pluginListener); } private void initPrefModelListeners() { applicationPreferenceModel.addPropertyChangeListener("identifierExpression", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { handler.setIdentifierExpression(evt.getNewValue().toString()); } }); handler.setIdentifierExpression(applicationPreferenceModel.getIdentifierExpression()); applicationPreferenceModel.addPropertyChangeListener("toolTipDisplayMillis", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { ToolTipManager.sharedInstance().setDismissDelay(((Integer) evt.getNewValue()).intValue()); } }); ToolTipManager.sharedInstance().setDismissDelay(applicationPreferenceModel.getToolTipDisplayMillis()); applicationPreferenceModel.addPropertyChangeListener("responsiveness", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { int value = ((Integer) evt.getNewValue()).intValue(); handler.setQueueInterval((value * 1000) - 750); } }); handler.setQueueInterval((applicationPreferenceModel.getResponsiveness() * 1000) - 750); applicationPreferenceModel.addPropertyChangeListener("tabPlacement", new PropertyChangeListener() { public void propertyChange(final PropertyChangeEvent evt) { SwingUtilities.invokeLater(new Runnable() { public void run() { int placement = ((Integer) evt.getNewValue()).intValue(); switch (placement) { case SwingConstants.TOP: case SwingConstants.BOTTOM: tabbedPane.setTabPlacement(placement); break; default: break; } } }); } }); applicationPreferenceModel.addPropertyChangeListener("statusBar", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { boolean value = ((Boolean) evt.getNewValue()).booleanValue(); setStatusBarVisible(value); } }); setStatusBarVisible(applicationPreferenceModel.isStatusBar()); applicationPreferenceModel.addPropertyChangeListener("receivers", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { boolean value = ((Boolean) evt.getNewValue()).booleanValue(); if (value) { showReceiverPanel(); } else { hideReceiverPanel(); } } }); // if (applicationPreferenceModel.isReceivers()) { // showReceiverPanel(); // } else { // hideReceiverPanel(); // } applicationPreferenceModel.addPropertyChangeListener("toolbar", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { boolean value = ((Boolean) evt.getNewValue()).booleanValue(); toolbar.setVisible(value); } }); toolbar.setVisible(applicationPreferenceModel.isToolbar()); } /** * Displays a dialog which will provide options for selecting a configuration */ private void showReceiverConfigurationPanel() { SwingUtilities.invokeLater(new Runnable() { public void run() { final JDialog dialog = new JDialog(LogUI.this, true); dialog.setTitle("Load events into Chainsaw"); dialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); dialog.setResizable(false); receiverConfigurationPanel.setCompletionActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { dialog.setVisible(false); if (receiverConfigurationPanel.getModel().isCancelled()) { return; } applicationPreferenceModel .setShowNoReceiverWarning(!receiverConfigurationPanel.isDontWarnMeAgain()); //remove existing plugins List plugins = pluginRegistry.getPlugins(); for (Iterator iter = plugins.iterator(); iter.hasNext();) { Plugin plugin = (Plugin) iter.next(); //don't stop ZeroConfPlugin if it is registered if (!plugin.getName().toLowerCase(Locale.ENGLISH).contains("zeroconf")) { pluginRegistry.stopPlugin(plugin.getName()); } } URL configURL = null; if (receiverConfigurationPanel.getModel().isNetworkReceiverMode()) { int port = receiverConfigurationPanel.getModel().getNetworkReceiverPort(); try { Class receiverClass = receiverConfigurationPanel.getModel() .getNetworkReceiverClass(); Receiver networkReceiver = (Receiver) receiverClass.newInstance(); networkReceiver.setName(receiverClass.getSimpleName() + "-" + port); Method portMethod = networkReceiver.getClass().getMethod("setPort", new Class[] { int.class }); portMethod.invoke(networkReceiver, new Object[] { new Integer(port) }); networkReceiver.setThreshold(Level.TRACE); pluginRegistry.addPlugin(networkReceiver); networkReceiver.activateOptions(); receiversPanel.updateReceiverTreeInDispatchThread(); } catch (Exception e3) { MessageCenter.getInstance().getLogger().error("Error creating Receiver", e3); MessageCenter.getInstance().getLogger() .info("An error occurred creating your Receiver"); } } else if (receiverConfigurationPanel.getModel().isLog4jConfig()) { File log4jConfigFile = receiverConfigurationPanel.getModel().getLog4jConfigFile(); if (log4jConfigFile != null) { try { Map entries = LogFilePatternLayoutBuilder .getAppenderConfiguration(log4jConfigFile); for (Iterator iter = entries.entrySet().iterator(); iter.hasNext();) { try { Map.Entry entry = (Map.Entry) iter.next(); String name = (String) entry.getKey(); Map values = (Map) entry.getValue(); //values: conversion, file String conversionPattern = values.get("conversion").toString(); File file = new File(values.get("file").toString()); URL fileURL = file.toURI().toURL(); String timestampFormat = LogFilePatternLayoutBuilder .getTimeStampFormat(conversionPattern); String receiverPattern = LogFilePatternLayoutBuilder .getLogFormatFromPatternLayout(conversionPattern); VFSLogFilePatternReceiver fileReceiver = new VFSLogFilePatternReceiver(); fileReceiver.setName(name); fileReceiver.setAutoReconnect(true); fileReceiver.setContainer(LogUI.this); fileReceiver.setAppendNonMatches(true); fileReceiver.setFileURL(fileURL.toURI().toString()); fileReceiver.setTailing(true); fileReceiver.setLogFormat(receiverPattern); fileReceiver.setTimestampFormat(timestampFormat); fileReceiver.setThreshold(Level.TRACE); pluginRegistry.addPlugin(fileReceiver); fileReceiver.activateOptions(); receiversPanel.updateReceiverTreeInDispatchThread(); } catch (URISyntaxException e1) { e1.printStackTrace(); } } } catch (IOException e1) { e1.printStackTrace(); } } } else if (receiverConfigurationPanel.getModel().isLoadConfig()) { configURL = receiverConfigurationPanel.getModel().getConfigToLoad(); } else if (receiverConfigurationPanel.getModel().isLogFileReceiverConfig()) { try { URL fileURL = receiverConfigurationPanel.getModel().getLogFileURL(); if (fileURL != null) { VFSLogFilePatternReceiver fileReceiver = new VFSLogFilePatternReceiver(); fileReceiver.setName(fileURL.getFile()); fileReceiver.setAutoReconnect(true); fileReceiver.setContainer(LogUI.this); fileReceiver.setAppendNonMatches(true); fileReceiver.setFileURL(fileURL.toURI().toString()); fileReceiver.setTailing(true); if (receiverConfigurationPanel.getModel().isPatternLayoutLogFormat()) { fileReceiver.setLogFormat( LogFilePatternLayoutBuilder.getLogFormatFromPatternLayout( receiverConfigurationPanel.getModel().getLogFormat())); } else { fileReceiver .setLogFormat(receiverConfigurationPanel.getModel().getLogFormat()); } fileReceiver.setTimestampFormat( receiverConfigurationPanel.getModel().getLogFormatTimestampFormat()); fileReceiver.setThreshold(Level.TRACE); pluginRegistry.addPlugin(fileReceiver); fileReceiver.activateOptions(); receiversPanel.updateReceiverTreeInDispatchThread(); } } catch (Exception e2) { MessageCenter.getInstance().getLogger().error("Error creating Receiver", e2); MessageCenter.getInstance().getLogger() .info("An error occurred creating your Receiver"); } } if (configURL == null && receiverConfigurationPanel.isDontWarnMeAgain()) { //use the saved config file as the config URL if defined if (receiverConfigurationPanel.getModel().getSaveConfigFile() != null) { try { configURL = receiverConfigurationPanel.getModel().getSaveConfigFile().toURI() .toURL(); } catch (MalformedURLException e1) { e1.printStackTrace(); } } else { //no saved config defined but don't warn me is checked - use default config configURL = receiverConfigurationPanel.getModel().getDefaultConfigFileURL(); } } if (configURL != null) { MessageCenter.getInstance().getLogger() .debug("Initialiazing Log4j with " + configURL.toExternalForm()); final URL finalURL = configURL; new Thread(new Runnable() { public void run() { if (receiverConfigurationPanel.isDontWarnMeAgain()) { applicationPreferenceModel.setConfigurationURL(finalURL.toExternalForm()); } else { try { if (new File(finalURL.toURI()).exists()) { loadConfigurationUsingPluginClassLoader(finalURL); } } catch (URISyntaxException e) { //ignore } } receiversPanel.updateReceiverTreeInDispatchThread(); } }).start(); } File saveConfigFile = receiverConfigurationPanel.getModel().getSaveConfigFile(); if (saveConfigFile != null) { ReceiversHelper.getInstance().saveReceiverConfiguration(saveConfigFile); } } }); receiverConfigurationPanel.setDialog(dialog); dialog.getContentPane().add(receiverConfigurationPanel); dialog.pack(); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); dialog.setLocation((screenSize.width / 2) - (dialog.getWidth() / 2), (screenSize.height / 2) - (dialog.getHeight() / 2)); dialog.setVisible(true); } }); } /** * Exits the application, ensuring Settings are saved. * */ public boolean exit() { getSettingsManager().saveSettings(); return shutdown(); } void addWelcomePanel() { getTabbedPane().insertTab(ChainsawTabbedPane.WELCOME_TAB, new ImageIcon(ChainsawIcons.ABOUT), welcomePanel, "Welcome/Help", 0); getTabbedPane().setSelectedComponent(welcomePanel); getPanelMap().put(ChainsawTabbedPane.WELCOME_TAB, welcomePanel); } void removeWelcomePanel() { EventQueue.invokeLater(new Runnable() { public void run() { if (getTabbedPane().containsWelcomePanel()) { getTabbedPane().remove(getTabbedPane() .getComponentAt(getTabbedPane().indexOfTab(ChainsawTabbedPane.WELCOME_TAB))); } } }); } ChainsawStatusBar getStatusBar() { return statusBar; } public void showApplicationPreferences() { applicationPreferenceModelPanel.updateModel(); preferencesFrame.setVisible(true); } public void showReceiverConfiguration() { showReceiverConfigurationPanel(); } public void showAboutBox() { if (aboutBox == null) { aboutBox = new ChainsawAbout(this); } aboutBox.setVisible(true); } Map getPanels() { Map m = new HashMap(); Set panelSet = getPanelMap().entrySet(); Iterator iter = panelSet.iterator(); while (iter.hasNext()) { Map.Entry entry = (Map.Entry) iter.next(); Object o = entry.getValue(); boolean valueToSend; if (o instanceof LogPanel) { valueToSend = ((DockablePanel) entry.getValue()).isDocked(); } else { valueToSend = true; } m.put(entry.getKey(), new Boolean(valueToSend)); } return m; } void displayPanel(String panelName, boolean display) { Component p = (Component) getPanelMap().get(panelName); int index = getTabbedPane().indexOfTab(panelName); if ((index == -1) && display) { getTabbedPane().addTab(panelName, p); } if ((index > -1) && !display) { getTabbedPane().removeTabAt(index); } } /** * Shutsdown by ensuring the Appender gets a chance to close. */ public boolean shutdown() { if (getApplicationPreferenceModel().isConfirmExit()) { if (JOptionPane.showConfirmDialog(LogUI.this, "Are you sure you want to exit Chainsaw?", "Confirm Exit", JOptionPane.YES_NO_OPTION, JOptionPane.INFORMATION_MESSAGE) != JOptionPane.YES_OPTION) { return false; } } final JWindow progressWindow = new JWindow(); final ProgressPanel panel = new ProgressPanel(1, 3, "Shutting down"); progressWindow.getContentPane().add(panel); progressWindow.pack(); Point p = new Point(getLocation()); p.move((int) getSize().getWidth() >> 1, (int) getSize().getHeight() >> 1); progressWindow.setLocation(p); progressWindow.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(); progressWindow.setVisible(false); } }; if (OSXIntegration.IS_OSX) { /** * or OSX we do it in the current thread because otherwise returning * will exit the process before it's had a chance to save things * */ runnable.run(); } else { new Thread(runnable).start(); } return true; } /** * 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() { MessageCenter.getInstance().getLogger().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 current log panel */ LogPanel getCurrentLogPanel() { Component selectedTab = getTabbedPane().getSelectedComponent(); if (selectedTab instanceof LogPanel) { return (LogPanel) selectedTab; } return null; } /** * @param visible */ private void setStatusBarVisible(final boolean visible) { MessageCenter.getInstance().getLogger().debug("Setting StatusBar to " + visible); SwingUtilities.invokeLater(new Runnable() { public void run() { statusBar.setVisible(visible); } }); } boolean isStatusBarVisible() { return statusBar.isVisible(); } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public String getActiveTabName() { int index = getTabbedPane().getSelectedIndex(); if (index == -1) { return null; } else { return getTabbedPane().getTitleAt(index); } } /** * Causes the Welcome Panel to become visible, and shows the URL specified as * it's contents * * @param url * for content to show */ public void showHelp(URL url) { ensureWelcomePanelVisible(); // TODO ensure the Welcome Panel is the selected tab getWelcomePanel().setURL(url); } /** * DOCUMENT ME! * * @return welcome panel */ private WelcomePanel getWelcomePanel() { return welcomePanel; } /** * DOCUMENT ME! * * @return log tree panel visible flag */ public boolean isLogTreePanelVisible() { if (getCurrentLogPanel() == null) { return false; } return getCurrentLogPanel().isLogTreeVisible(); } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public Map getPanelMap() { return panelMap; } // public Map getLevelMap() { // return levelMap; // } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public SettingsManager getSettingsManager() { return sm; } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public List getFilterableColumns() { return filterableColumns; } /** * DOCUMENT ME! * * @param tbms * DOCUMENT ME! */ public void setToolBarAndMenus(ChainsawToolBarAndMenus tbms) { this.tbms = tbms; } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public ChainsawToolBarAndMenus getToolBarAndMenus() { return tbms; } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public Map getTableMap() { return tableMap; } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public Map getTableModelMap() { return tableModelMap; } /** * DOCUMENT ME! * * @param tabbedPane * DOCUMENT ME! */ public void setTabbedPane(ChainsawTabbedPane tabbedPane) { this.tabbedPane = tabbedPane; } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public ChainsawTabbedPane getTabbedPane() { return tabbedPane; } /** * @return Returns the applicationPreferenceModel. */ public final ApplicationPreferenceModel getApplicationPreferenceModel() { return applicationPreferenceModel; } /** * DOCUMENT ME! */ public void setupTutorial() { SwingUtilities.invokeLater(new Runnable() { public void run() { Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); setLocation(0, getLocation().y); double chainsawwidth = 0.7; double tutorialwidth = 1 - chainsawwidth; setSize((int) (screen.width * chainsawwidth), getSize().height); invalidate(); validate(); Dimension size = getSize(); Point loc = getLocation(); tutorialFrame.setSize((int) (screen.width * tutorialwidth), size.height); tutorialFrame.setLocation(loc.x + size.width, loc.y); tutorialFrame.setVisible(true); } }); } private void buildLogPanel(boolean customExpression, final String ident, final List events) throws IllegalArgumentException { final LogPanel thisPanel = new LogPanel(getStatusBar(), ident, cyclicBufferSize, allColorizers, applicationPreferenceModel); getSettingsManager().addSettingsListener(thisPanel); getSettingsManager().configure(thisPanel); /** * Now add the panel as a batch listener so it can handle it's own * batchs */ if (customExpression) { handler.addCustomEventBatchListener(ident, thisPanel); } else { identifierPanels.add(thisPanel); handler.addEventBatchListener(thisPanel); } TabIconHandler iconHandler = new TabIconHandler(ident); thisPanel.addEventCountListener(iconHandler); tabbedPane.addChangeListener(iconHandler); PropertyChangeListener toolbarMenuUpdateListener = new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { tbms.stateChange(); } }; thisPanel.addPropertyChangeListener(toolbarMenuUpdateListener); thisPanel.addPreferencePropertyChangeListener(toolbarMenuUpdateListener); thisPanel.addPropertyChangeListener("docked", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { LogPanel logPanel = (LogPanel) evt.getSource(); if (logPanel.isDocked()) { getPanelMap().put(logPanel.getIdentifier(), logPanel); getTabbedPane().addANewTab(logPanel.getIdentifier(), logPanel, null); getTabbedPane().setSelectedTab(getTabbedPane().indexOfTab(logPanel.getIdentifier())); } else { getTabbedPane().remove(logPanel); } } }); logger.debug("adding logpanel to tabbed pane: " + ident); //NOTE: tab addition is a very fragile process - if you modify this code, //verify the frames in the individual log panels initialize to their //correct sizes getTabbedPane().add(ident, thisPanel); getPanelMap().put(ident, thisPanel); /** * Let the new LogPanel receive this batch */ SwingUtilities.invokeLater(new Runnable() { public void run() { getTabbedPane().addANewTab(ident, thisPanel, new ImageIcon(ChainsawIcons.ANIM_RADIO_TOWER)); thisPanel.layoutComponents(); thisPanel.receiveEventBatch(ident, events); if (!getTabbedPane().tabSetting.isChainsawLog()) { displayPanel("chainsaw-log", false); } } }); String msg = "added tab " + ident; MessageCenter.getInstance().getLogger().debug(msg); } public void createCustomExpressionLogPanel(String ident) { //collect events matching the rule from all of the tabs try { List list = new ArrayList(); Rule rule = ExpressionRule.getRule(ident); Iterator iter = identifierPanels.iterator(); while (iter.hasNext()) { LogPanel panel = (LogPanel) iter.next(); Iterator iter2 = panel.getMatchingEvents(rule).iterator(); while (iter2.hasNext()) { LoggingEventWrapper e = (LoggingEventWrapper) iter2.next(); list.add(e.getLoggingEvent()); } } buildLogPanel(true, ident, list); } catch (IllegalArgumentException iae) { MessageCenter.getInstance().getLogger() .info("Unable to add tab using expression: " + ident + ", reason: " + iae.getMessage()); } } /** * Loads the log4j configuration file specified by the url, using * the PluginClassLoader instance as a TCCL, but only replacing it temporarily, with the original * TCCL being restored in a finally block to ensure consitency. * * @param url */ private void loadConfigurationUsingPluginClassLoader(final URL url) { ClassLoader classLoader = PluginClassLoaderFactory.getInstance().getClassLoader(); ClassLoader previousTCCL = Thread.currentThread().getContextClassLoader(); if (url != null) { try { // we temporarily swap the TCCL so that plugins can find resources Thread.currentThread().setContextClassLoader(classLoader); try { DOMConfigurator.configure(url); } catch (Exception e) { logger.warn("Unable to load configuration URL: " + url, e); } } finally { // now switch it back... Thread.currentThread().setContextClassLoader(previousTCCL); } } ensureChainsawAppenderHandlerAdded(); } private static void loadLookAndFeelUsingPluginClassLoader(String lookAndFeelClassName) { ClassLoader classLoader = PluginClassLoaderFactory.getInstance().getClassLoader(); ClassLoader previousTCCL = Thread.currentThread().getContextClassLoader(); try { // we temporarily swap the TCCL so that plugins can find resources Thread.currentThread().setContextClassLoader(classLoader); UIManager.setLookAndFeel(lookAndFeelClassName); UIManager.getLookAndFeelDefaults().put("ClassLoader", classLoader); } catch (Exception e) { e.printStackTrace(); } finally { // now switch it back... Thread.currentThread().setContextClassLoader(previousTCCL); } } /** * Makes sure that the LoggerRepository has the ChainsawAppenderHandler * added to the root logger so Chainsaw can receive all the events. */ private void ensureChainsawAppenderHandlerAdded() { if (!LogManager.getLoggerRepository().getRootLogger().isAttached(handler)) { LogManager.getLoggerRepository().getRootLogger().addAppender(handler); } } /** * 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 { /** * DOCUMENT ME! * * @param ident * @param events */ public void receiveEventBatch(final String ident, final List events) { if (events.size() == 0) { return; } 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); logger.debug("waiting for initialization to complete"); } catch (InterruptedException e) { } } logger.debug("out of system initialization wait loop"); } } if (!getPanelMap().containsKey(ident)) { logger.debug("panel " + ident + " does not exist - creating"); try { buildLogPanel(false, ident, events); } catch (IllegalArgumentException iae) { logger.error("error creating log panel", iae); //should not happen - not a custom expression panel } } } /* * (non-Javadoc) * * @see org.apache.log4j.chainsaw.EventBatchListener#getInterestedIdentifier() */ /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public String getInterestedIdentifier() { // we are interested in all batches so we can detect new identifiers return null; } } private class TabIconHandler implements EventCountListener, ChangeListener { //the tabIconHandler is associated with a new tab, and a new tab always //shows the 'new events' icon private boolean newEvents = true; private boolean seenEvents = false; private final String ident; ImageIcon NEW_EVENTS = new ImageIcon(ChainsawIcons.ANIM_RADIO_TOWER); ImageIcon HAS_EVENTS = new ImageIcon(ChainsawIcons.INFO); Icon SELECTED = LineIconFactory.createBlankIcon(); public TabIconHandler(String identifier) { ident = identifier; new Thread(new Runnable() { public void run() { while (true) { //if this tab is active, remove the icon //don't process undocked tabs if (getTabbedPane().indexOfTab(ident) > -1 && getTabbedPane().getSelectedIndex() == getTabbedPane().indexOfTab(ident)) { getTabbedPane().setIconAt(getTabbedPane().indexOfTab(ident), SELECTED); newEvents = false; seenEvents = true; } else if (getTabbedPane().indexOfTab(ident) > -1) { if (newEvents) { getTabbedPane().setIconAt(getTabbedPane().indexOfTab(ident), NEW_EVENTS); newEvents = false; seenEvents = false; } else if (!seenEvents) { getTabbedPane().setIconAt(getTabbedPane().indexOfTab(ident), HAS_EVENTS); } } try { Thread.sleep(handler.getQueueInterval() + 1000); } catch (InterruptedException ie) { } } } }).start(); } /** * DOCUMENT ME! * * @param currentCount * DOCUMENT ME! * @param totalCount * DOCUMENT ME! */ public void eventCountChanged(int currentCount, int totalCount) { newEvents = true; } public void stateChanged(ChangeEvent event) { if (getTabbedPane().indexOfTab(ident) > -1 && getTabbedPane().indexOfTab(ident) == getTabbedPane().getSelectedIndex()) { getTabbedPane().setIconAt(getTabbedPane().indexOfTab(ident), SELECTED); } } } }