com.eviware.soapui.support.log.JLogList.java Source code

Java tutorial

Introduction

Here is the source code for com.eviware.soapui.support.log.JLogList.java

Source

/*
 *  soapUI, copyright (C) 2004-2012 smartbear.com 
 *
 *  soapUI is free software; you can redistribute it and/or modify it under the 
 *  terms of version 2.1 of the GNU Lesser General Public License as published by 
 *  the Free Software Foundation.
 *
 *  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 
 *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 *  See the GNU Lesser General Public License for more details at gnu.org.
 */

package com.eviware.soapui.support.log;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.concurrent.Future;

import javax.swing.AbstractAction;
import javax.swing.AbstractListModel;
import javax.swing.BorderFactory;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;

import org.apache.commons.collections.list.TreeList;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.spi.LoggingEvent;

import com.eviware.soapui.SoapUI;
import com.eviware.soapui.support.UISupport;

/**
 * Component for displaying log entries
 * 
 * @author Ole.Matzura
 */

public class JLogList extends JPanel {
    private long maxRows = 1000;
    private JList logList;
    private SimpleAttributeSet requestAttributes;
    private SimpleAttributeSet responseAttributes;
    private LogListModel model;
    private List<Logger> loggers = new ArrayList<Logger>();
    private InternalLogAppender internalLogAppender = new InternalLogAppender();
    private boolean tailing = true;
    private Stack<Object> linesToAdd = new Stack<Object>();
    private EnableAction enableAction;
    private JCheckBoxMenuItem enableMenuItem;
    private final String title;
    private boolean released;
    private Future<?> future;

    public JLogList(String title) {
        super(new BorderLayout());
        this.title = title;

        model = new LogListModel();
        logList = new JList(model);
        logList.setToolTipText(title);
        logList.setCellRenderer(new LogAreaCellRenderer());
        logList.setPrototypeCellValue("Testing 123");
        logList.setFixedCellWidth(-1);

        JPopupMenu listPopup = new JPopupMenu();
        listPopup.add(new ClearAction());
        enableAction = new EnableAction();
        enableMenuItem = new JCheckBoxMenuItem(enableAction);
        enableMenuItem.setSelected(true);
        listPopup.add(enableMenuItem);
        listPopup.addSeparator();
        listPopup.add(new CopyAction());
        listPopup.add(new SetMaxRowsAction());
        listPopup.addSeparator();
        listPopup.add(new ExportToFileAction());

        logList.setComponentPopupMenu(listPopup);

        setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
        JScrollPane scrollPane = new JScrollPane(logList);
        UISupport.addPreviewCorner(scrollPane, true);
        add(scrollPane, BorderLayout.CENTER);

        requestAttributes = new SimpleAttributeSet();
        StyleConstants.setForeground(requestAttributes, Color.BLUE);

        responseAttributes = new SimpleAttributeSet();
        StyleConstants.setForeground(responseAttributes, Color.GREEN);

        try {
            maxRows = Long.parseLong(SoapUI.getSettings().getString("JLogList#" + title, "1000"));
        } catch (NumberFormatException e) {
        }
    }

    public void clear() {
        model.clear();
    }

    public JList getLogList() {
        return logList;
    }

    public long getMaxRows() {
        return maxRows;
    }

    public void setMaxRows(long maxRows) {
        this.maxRows = maxRows;
    }

    public synchronized void addLine(Object line) {
        if (!isEnabled())
            return;

        synchronized (model.lines) {
            if (line instanceof LoggingEvent) {
                LoggingEvent ev = (LoggingEvent) line;
                linesToAdd.push(new LoggingEventWrapper(ev));

                if (ev.getThrowableInformation() != null) {
                    Throwable t = ev.getThrowableInformation().getThrowable();
                    StringWriter sw = new StringWriter();
                    PrintWriter pw = new PrintWriter(sw);
                    t.printStackTrace(pw);
                    StringTokenizer st = new StringTokenizer(sw.toString(), "\r\n");
                    while (st.hasMoreElements())
                        linesToAdd.push("   " + st.nextElement());
                }
            } else {
                linesToAdd.push(line);
            }
        }
        if (future == null) {
            released = false;
            future = SoapUI.getThreadPool().submit(model);
        }
    }

    public void setEnabled(boolean enabled) {
        super.setEnabled(enabled);
        logList.setEnabled(enabled);
        enableMenuItem.setSelected(enabled);
    }

    private static class LogAreaCellRenderer extends DefaultListCellRenderer {
        private Map<Level, Color> levelColors = new HashMap<Level, Color>();

        private LogAreaCellRenderer() {
            levelColors.put(Level.ERROR, new Color(192, 0, 0));
            levelColors.put(Level.INFO, new Color(0, 92, 0));
            levelColors.put(Level.WARN, Color.ORANGE.darker().darker());
            levelColors.put(Level.DEBUG, new Color(0, 0, 128));
        }

        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
                boolean cellHasFocus) {
            JLabel component = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected,
                    cellHasFocus);

            if (value instanceof LoggingEventWrapper) {
                LoggingEventWrapper eventWrapper = (LoggingEventWrapper) value;

                if (levelColors.containsKey(eventWrapper.getLevel()))
                    component.setForeground(levelColors.get(eventWrapper.getLevel()));
            }

            // Limit the length of the tool tip, to prevent long delays.
            String toolTip = component.getText();
            if (toolTip != null && toolTip.length() > 1000)
                toolTip = toolTip.substring(0, 1000);
            component.setToolTipText(toolTip);

            return component;
        }
    }

    private final static class LoggingEventWrapper {
        private final LoggingEvent loggingEvent;
        private String str;

        public LoggingEventWrapper(LoggingEvent loggingEvent) {
            this.loggingEvent = loggingEvent;
        }

        public Level getLevel() {
            return loggingEvent.getLevel();
        }

        public String toString() {
            if (str == null) {
                StringBuilder builder = new StringBuilder();
                builder.append(new Date(loggingEvent.timeStamp));
                builder.append(':').append(loggingEvent.getLevel()).append(':').append(loggingEvent.getMessage());
                str = builder.toString();
            }

            return str;
        }
    }

    public void addLogger(String loggerName, boolean addAppender) {
        Logger logger = Logger.getLogger(loggerName);
        if (addAppender)
            logger.addAppender(internalLogAppender);

        loggers.add(logger);
    }

    public Logger[] getLoggers() {
        return loggers.toArray(new Logger[loggers.size()]);
    }

    public void setLevel(Level level) {
        for (Logger logger : loggers) {
            logger.setLevel(level);
        }
    }

    public Logger getLogger(String loggerName) {
        for (Logger logger : loggers) {
            if (logger.getName().equals(loggerName))
                return logger;
        }

        return null;
    }

    private class InternalLogAppender extends AppenderSkeleton {
        protected void append(LoggingEvent event) {
            addLine(event);
        }

        public void close() {
        }

        public boolean requiresLayout() {
            return false;
        }
    }

    public boolean monitors(String loggerName) {
        for (Logger logger : loggers) {
            if (loggerName.startsWith(logger.getName()))
                return true;
        }

        return false;
    }

    public void removeLogger(String loggerName) {
        for (Logger logger : loggers) {
            if (loggerName.equals(logger.getName())) {
                logger.removeAppender(internalLogAppender);
            }
        }
    }

    public boolean isTailing() {
        return tailing;
    }

    public void setTailing(boolean tail) {
        this.tailing = tail;
    }

    private class ClearAction extends AbstractAction {
        public ClearAction() {
            super("Clear");
        }

        public void actionPerformed(ActionEvent e) {
            model.clear();
        }
    }

    private class SetMaxRowsAction extends AbstractAction {
        public SetMaxRowsAction() {
            super("Set Max Rows");
        }

        public void actionPerformed(ActionEvent e) {
            String val = UISupport.prompt("Set maximum number of log rows to keep", "Set Max Rows",
                    String.valueOf(maxRows));
            if (val != null) {
                try {
                    maxRows = Long.parseLong(val);
                    SoapUI.getSettings().setString("JLogList#" + title, val);
                } catch (NumberFormatException e1) {
                    UISupport.beep();
                }
            }
        }
    }

    private class ExportToFileAction extends AbstractAction {
        public ExportToFileAction() {
            super("Export to File");
        }

        public void actionPerformed(ActionEvent e) {
            if (model.getSize() == 0) {
                UISupport.showErrorMessage("Log is empty; nothing to export");
                return;
            }

            File file = UISupport.getFileDialogs().saveAs(JLogList.this, "Save Log [] to File", "*.log", "*.log",
                    null);
            if (file != null)
                saveToFile(file);
        }
    }

    private class CopyAction extends AbstractAction {
        public CopyAction() {
            super("Copy to clipboard");
        }

        public void actionPerformed(ActionEvent e) {
            Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();

            StringBuffer buf = new StringBuffer();
            int[] selectedIndices = logList.getSelectedIndices();
            if (selectedIndices.length == 0) {
                for (int c = 0; c < logList.getModel().getSize(); c++) {
                    buf.append(logList.getModel().getElementAt(c).toString());
                    buf.append("\r\n");
                }
            } else {
                for (int c = 0; c < selectedIndices.length; c++) {
                    buf.append(logList.getModel().getElementAt(selectedIndices[c]).toString());
                    buf.append("\r\n");
                }
            }

            StringSelection selection = new StringSelection(buf.toString());
            clipboard.setContents(selection, selection);
        }
    }

    private class EnableAction extends AbstractAction {
        public EnableAction() {
            super("Enable");
        }

        public void actionPerformed(ActionEvent e) {
            JLogList.this.setEnabled(enableMenuItem.isSelected());
        }
    }

    /**
     * Internal list model that for optimized storage and notifications
     * 
     * @author Ole.Matzura
     */

    @SuppressWarnings("unchecked")
    private final class LogListModel extends AbstractListModel implements Runnable {
        private List<Object> lines = new TreeList();

        public int getSize() {
            synchronized (lines) {
                return lines.size();
            }
        }

        public Object getElementAt(int index) {
            synchronized (lines) {
                return lines.get(index);
            }
        }

        public void clear() {
            int sz = lines.size();
            if (sz == 0)
                return;

            synchronized (lines) {
                lines.clear();
                fireIntervalRemoved(this, 0, sz - 1);
            }
        }

        public void run() {
            Thread.currentThread().setName("LogList Updater for " + title);

            try {
                while (!released && !linesToAdd.isEmpty()) {
                    try {
                        if (!linesToAdd.isEmpty()) {
                            SwingUtilities.invokeAndWait(new Runnable() {
                                public void run() {
                                    try {
                                        synchronized (lines) {
                                            while (!linesToAdd.isEmpty()) {
                                                int sz = lines.size();
                                                lines.addAll(linesToAdd);
                                                linesToAdd.clear();
                                                fireIntervalAdded(LogListModel.this, sz, lines.size() - sz);
                                            }

                                            int cnt = 0;
                                            while (lines.size() > maxRows) {
                                                lines.remove(0);
                                                cnt++;
                                            }

                                            if (cnt > 0)
                                                fireIntervalRemoved(LogListModel.this, 0, cnt - 1);

                                            if (tailing) {
                                                logList.ensureIndexIsVisible(lines.size() - 1);
                                            }
                                        }
                                    } catch (Throwable e) {
                                        SoapUI.logError(e);
                                    }
                                }
                            });
                        }

                        Thread.sleep(500);
                    } catch (Throwable e) {
                        SoapUI.logError(e);
                    }
                }
            } finally {
                future = null;
            }
        }
    }

    public void release() {
        released = true;
    }

    public void saveToFile(File file) {
        try {
            PrintWriter writer = new PrintWriter(file);
            for (int c = 0; c < model.getSize(); c++) {
                writer.println(model.getElementAt(c));
            }

            writer.close();
        } catch (Exception e) {
            UISupport.showErrorMessage(e);
        }
    }
}