org.ngrinder.recorder.ui.RecordingControlPanel.java Source code

Java tutorial

Introduction

Here is the source code for org.ngrinder.recorder.ui.RecordingControlPanel.java

Source

/* 
 * Licensed 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.ngrinder.recorder.ui;

import static net.grinder.util.CollectionUtils.newArrayList;
import static net.grinder.util.CollectionUtils.newHashSet;
import static org.ngrinder.recorder.ui.component.SwingUtils.attachPopupMenu;
import static org.ngrinder.recorder.ui.component.SwingUtils.createMenuItem;
import static org.ngrinder.recorder.ui.component.SwingUtils.createSimpleDropDownButton;
import static org.ngrinder.recorder.ui.component.SwingUtils.createSimpleTextButton;
import static org.ngrinder.recorder.ui.component.SwingUtils.decoratedToSimpleButton;
import static org.ngrinder.recorder.ui.component.SwingUtils.wrapScroll;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.AbstractAction;
import javax.swing.BoxLayout;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTable;
import javax.swing.JToggleButton;
import javax.swing.SwingUtilities;
import javax.swing.border.TitledBorder;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

import net.grinder.plugin.http.tcpproxyfilter.ConnectionFilter;
import net.grinder.plugin.http.tcpproxyfilter.options.FileTypeCategory;
import net.grinder.plugin.http.tcpproxyfilter.options.GenerationOption;
import net.grinder.tools.tcpproxy.EndPoint;
import net.grinder.util.CollectionUtils;
import net.grinder.util.NoOp;
import net.grinder.util.Pair;

import org.apache.commons.io.FileUtils;
import org.ngrinder.recorder.browser.BrowserFactoryEx;
import org.ngrinder.recorder.event.MessageBus;
import org.ngrinder.recorder.event.Topics;
import org.ngrinder.recorder.ui.component.DropDownButton;
import org.ngrinder.recorder.ui.component.JExtendedCheckBox;

import com.google.gson.Gson;
import com.teamdev.jxbrowser.BrowserServices;
import com.teamdev.jxbrowser.BrowserType;
import com.teamdev.jxbrowser.cookie.HttpCookie;
import com.teamdev.jxbrowser.cookie.HttpCookieStorage;

/**
 * UI Component which contains the recoding options controller.
 * 
 * @author JunHo Yoon
 * @since 1.0
 */
public class RecordingControlPanel extends JPanel {

    private static final long serialVersionUID = 1L;
    private Timer timer;
    private MessageBus messageBus;
    private ConnectionFilter connectionFilter;
    private JComponent typeFilter;
    private JComponent generationOptions;
    private OptionPersistencyHandler typeFilterPersistentHandler;
    private OptionPersistencyHandler generationOptionsPersistentHandler;
    private JPopupMenu createFilterTablePopUp;
    private JTable createFilterTables;

    /**
     * Constructor.
     * 
     * @param connectionFilter
     *            {@link ConnectionFilter}
     */
    public RecordingControlPanel(ConnectionFilter connectionFilter) {
        this.connectionFilter = connectionFilter;
        initUI(connectionFilter);
        initEventHandler();
    }

    /**
     * Initialize event handler.
     */
    protected void initEventHandler() {
        // Global Event
        messageBus = MessageBus.getInstance();
        messageBus.connect().subscribe(Topics.PREPARE_TO_CLOSE, new PropertyChangeListener() {
            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                File home = (File) evt.getNewValue();
                typeFilterPersistentHandler.save(home);
                generationOptionsPersistentHandler.save(home);
            }

        });
        messageBus.connect().subscribe(Topics.PREPARE_TO_VIEW, new PropertyChangeListener() {
            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                File home = (File) evt.getNewValue();
                typeFilterPersistentHandler.load(home);
                generationOptionsPersistentHandler.load(home);
            }

        });

        // Attach event.
        createFilterTablePopUp = createFilterTablePopUp();
        attachPopupMenu(createFilterTables, createFilterTablePopUp);
    }

    /**
     * Initialize UI.
     * 
     * @param connectionFilter
     *            connection filter
     */
    protected void initUI(ConnectionFilter connectionFilter) {
        GridBagLayout gbl = new GridBagLayout();
        setLayout(gbl);
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(5, 5, 5, 5);
        gbc.fill = GridBagConstraints.BOTH;

        addGrid(gbl, gbc, createRecordingButton(), new Rectangle(0, 0, 1, 1), 0, 0);
        createFilterTables = createFilterTables(connectionFilter);
        addGrid(gbl, gbc, wrapScroll(createFilterTables), new Rectangle(0, 1, 1, 5), 1, 0.7);
        addGrid(gbl, gbc, createFilterButtonPanel(), new Rectangle(0, 7, 1, 1), 0, 0);

        typeFilter = createTypeFilterPanel();
        typeFilterPersistentHandler = new OptionPersistencyHandler(typeFilter, "type_filter");
        addGrid(gbl, gbc, typeFilter, new Rectangle(0, 8, 1, 3), 0, 0);
        generationOptions = createGenerationOptionsPanel();
        generationOptionsPersistentHandler = new OptionPersistencyHandler(generationOptions, "generation_options");
        addGrid(gbl, gbc, generationOptions, new Rectangle(0, 11, 1, 1), 0, 0);
    }

    private void addGrid(GridBagLayout gbl, GridBagConstraints gbc, Component c, Rectangle rect, double weightx,
            double weighty) {
        gbc.gridx = rect.x;
        gbc.gridy = rect.y;
        gbc.gridwidth = rect.width;
        gbc.gridheight = rect.height;
        gbc.weightx = weightx;
        gbc.weighty = weighty;
        gbl.setConstraints(c, gbc);
        add(c);
    }

    private JPopupMenu createFilterTablePopUp() {
        final JPopupMenu result = new JPopupMenu();
        JMenuItem excludeSelectedHosts = new JMenuItem("Exclude selected hosts", KeyEvent.VK_U);
        result.add(excludeSelectedHosts);
        excludeSelectedHosts.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                int[] selectedRows = createFilterTables.getSelectedRows();
                ((FilterTableModel) createFilterTables.getModel()).setSelection(selectedRows, false);
            }
        });

        JMenuItem includeSelectedHost = new JMenuItem("Include selected hosts", KeyEvent.VK_I);
        result.add(includeSelectedHost);
        includeSelectedHost.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                int[] selectedRows = createFilterTables.getSelectedRows();
                ((FilterTableModel) createFilterTables.getModel()).setSelection(selectedRows, true);
            }
        });
        result.addSeparator();

        JMenuItem deleteSelectedHost = new JMenuItem("Deleted selected hosts", KeyEvent.VK_D);
        result.add(deleteSelectedHost);
        deleteSelectedHost.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                int[] selectedRows = createFilterTables.getSelectedRows();
                ((FilterTableModel) createFilterTables.getModel()).removeRows(selectedRows);
            }
        });
        return result;
    }

    /**
     * Create filter by type panel.
     * 
     * @return created panel
     */
    protected JPanel createTypeFilterPanel() {
        JPanel panel = new JPanel();
        panel.setBorder(new TitledBorder("Recorded Type"));
        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
        for (FileTypeCategory each : FileTypeCategory.values()) {
            panel.add(new JExtendedCheckBox(each.name(), each.getDisplayName(), true));
        }
        return panel;
    }

    /**
     * {@link JExtendedCheckBox} save / load handler.
     * 
     * @author JunHo Yoon
     * @since 1.0
     */
    static class OptionPersistencyHandler {
        private final Container container;
        private final String fileName;

        private static final Gson GSON = new Gson();

        /**
         * Constructor.
         * 
         * @param container
         *            container of the {@link JExtendedCheckBox} components.
         * @param fileName
         *            file to be saved or loaded
         */
        public OptionPersistencyHandler(Container container, String fileName) {
            this.container = container;
            this.fileName = fileName;
        }

        /**
         * Save the selected components in the given home folder.
         * 
         * @param home
         *            home
         */
        public void save(File home) {
            try {
                List<String> list = CollectionUtils.newArrayList();
                for (Component each : container.getComponents()) {
                    if (each instanceof JExtendedCheckBox) {
                        JExtendedCheckBox checkBox = (JExtendedCheckBox) each;
                        if (checkBox.isSelected()) {
                            list.add(checkBox.getId());
                        }
                    }

                    if (each instanceof JPanel) {
                        for (Component subEach : ((JPanel) each).getComponents()) {
                            if (subEach instanceof JComboBox) {
                                JComboBox comboBox = (JComboBox) subEach;
                                list.add(((GenerationOption) comboBox.getSelectedItem()).name());
                            }
                        }
                    }
                }
                String json = GSON.toJson(list);
                File recodingTypeFile = new File(home, fileName);

                FileUtils.write(recodingTypeFile, json);
            } catch (Exception e) {
                NoOp.noOp();
            }
        }

        /**
         * Load the selected components in the given home folder.
         * 
         * @param home
         *            home
         */
        @SuppressWarnings("unchecked")
        public void load(File home) {
            File savedFile = new File(home, fileName);
            if (savedFile.exists()) {
                ArrayList<String> savedData;
                try {
                    savedData = (ArrayList<String>) GSON.fromJson(FileUtils.readFileToString(savedFile),
                            ArrayList.class);

                    for (Component each : container.getComponents()) {
                        if (each instanceof JExtendedCheckBox) {
                            JExtendedCheckBox checkBox = (JExtendedCheckBox) each;
                            checkBox.setSelected(savedData.contains(checkBox.getId()));
                        }
                        if (each instanceof JPanel) {
                            for (Component subEach : ((JPanel) each).getComponents()) {
                                if (subEach instanceof JComboBox) {
                                    JComboBox comboBox = (JComboBox) subEach;
                                    if (savedData.contains("Jython")) {
                                        comboBox.setSelectedItem(GenerationOption.Jython);
                                    } else {
                                        comboBox.setSelectedItem(GenerationOption.Groovy);
                                    }
                                }
                            }
                        }
                    }
                } catch (Exception e) {
                    NoOp.noOp();
                }
            }

        }
    }

    /**
     * Get FileTypeCategory list which will be filtered.
     * 
     * @return {@link FileTypeCategory} list
     */
    private List<FileTypeCategory> getFilteredFileTypes() {
        List<FileTypeCategory> list = newArrayList();
        for (Component each : this.typeFilter.getComponents()) {
            if (each instanceof JCheckBox) {
                JExtendedCheckBox checkBox = (JExtendedCheckBox) each;
                if (!checkBox.isSelected()) {
                    list.add(FileTypeCategory.valueOf(checkBox.getId()));
                }
            }
        }
        return list;
    }

    /**
     * Get the selected {@link GenerationOption} list.
     * 
     * @return set of selected {@link GenerationOption}
     */
    private Set<GenerationOption> getGenerationOption() {
        Set<GenerationOption> list = newHashSet();
        for (Component each : this.generationOptions.getComponents()) {
            if (each instanceof JCheckBox) {
                JExtendedCheckBox checkBox = (JExtendedCheckBox) each;
                if (checkBox.isSelected()) {
                    list.add(GenerationOption.valueOf(checkBox.getId()));
                }
            }
            if (each instanceof JPanel) {
                for (Component subEach : ((JPanel) each).getComponents()) {
                    if (subEach instanceof JComboBox) {
                        JComboBox comboBox = (JComboBox) subEach;
                        list.add((GenerationOption) comboBox.getSelectedItem());
                    }
                }
            }
        }
        return list;
    }

    /**
     * Create "Start Recording" Button and attach the event handler.
     * 
     * @return created Button.
     */
    private JToggleButton createRecordingButton() {
        JToggleButton button = decoratedToSimpleButton(new JToggleButton("Start Recording"));
        button.setText("Start Recording");
        button.setSelected(false);
        button.setMinimumSize(new Dimension(100, 30));
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JToggleButton button = (JToggleButton) e.getSource();
                if (button.isSelected()) {
                    button.setText("Stop Recording");
                    button.setSelected(true);
                    messageBus.getPublisher(Topics.START_RECORDING).propertyChange(new PropertyChangeEvent(
                            RecordingControlPanel.this, "Start Recording", null, getFilteredFileTypes()));
                } else {
                    if (!stopConfirm()) {
                        button.setSelected(true);
                        return;
                    }
                    messageBus.getPublisher(Topics.STOP_RECORDING)
                            .propertyChange(new PropertyChangeEvent(RecordingControlPanel.this, "Stop Recording",
                                    null, Pair.of(getFilteredFileTypes(), getGenerationOption())));
                    button.setText("Start Recording");
                }
            }
        });
        return button;
    }

    private boolean stopConfirm() {
        JOptionPane pane = new JOptionPane("Do you want to stop recording?", JOptionPane.WARNING_MESSAGE,
                JOptionPane.YES_NO_OPTION, null);
        JDialog dialog = pane.createDialog(SwingUtilities.getWindowAncestor(this), "Stop recording");
        dialog.setVisible(true);
        Object selectedValue = pane.getValue();
        if (selectedValue == null) {
            return false;
        }
        return JOptionPane.YES_OPTION == (Integer) selectedValue;
    }

    /**
     * Create Generation Options Panel.
     * 
     * @return generated panel
     */
    protected JPanel createGenerationOptionsPanel() {
        JPanel panel = new JPanel();
        panel.setBorder(new TitledBorder("Script Generation Options"));
        GridLayout mgr = new GridLayout(3, 1);
        panel.setLayout(mgr);
        for (GenerationOption each : GenerationOption.getOptions(null)) {
            JExtendedCheckBox comp = new JExtendedCheckBox(each.name(), each.getDisplayName(), true);
            panel.add(comp);

        }
        JPanel subPanel = new JPanel();
        subPanel.setLayout(new GridLayout(1, 2));
        JLabel languageLabel = new JLabel("Language");
        subPanel.add(languageLabel);
        JComboBox languageCombo = new JComboBox();
        for (GenerationOption each : GenerationOption.getOptions("Language")) {
            languageCombo.addItem(each);
        }
        subPanel.add(languageCombo);
        panel.add(subPanel);
        return panel;
    }

    /**
     * Create Filter Button Panel.
     * 
     * @return filter button panel
     */
    protected JPanel createFilterButtonPanel() {
        JPanel panel = new JPanel();
        panel.setLayout(new FlowLayout());
        panel.add(createSimpleTextButton("Unselect All", new AbstractAction() {
            /** UUID */
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                for (EndPoint each : connectionFilter.getConnectionEndPoints()) {
                    connectionFilter.setFilter(each, true);
                }
            }
        }));
        DropDownButton resetDropDownButton = createSimpleDropDownButton("Reset", null);
        panel.add(resetDropDownButton);
        JPopupMenu menu = new JPopupMenu();
        menu.add(createMenuItem("Clear all recording", new AbstractAction() {
            /** UUID */
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                messageBus.getPublisher(Topics.RESET)
                        .propertyChange(new PropertyChangeEvent(RecordingControlPanel.this, "RESET", null, null));
                connectionFilter.makeZeroCount();
            }
        }));

        menu.add(createMenuItem("Clear all filters", new AbstractAction() {
            /** UUID */
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                connectionFilter.reset();
            }
        }));

        menu.add(createMenuItem("Clear connection count", new AbstractAction() {
            /** UUID */
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                connectionFilter.makeZeroCount();
            }
        }));

        menu.add(createMenuItem("Reset browser cache", new AbstractAction() {
            /** UUID */
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                BrowserServices browserServices = BrowserServices.getInstance();
                for (BrowserType type : BrowserFactoryEx.getSupportedBrowser()) {
                    clearCacheIfSupported(browserServices, type);
                }
            }

            private void clearCacheIfSupported(BrowserServices browserServices, BrowserType type) {
                if (type.isSupported()) {
                    try {
                        browserServices.getCacheStorage(type).clearCache();
                    } catch (Exception e) {
                        NoOp.noOp();
                    }
                }
            }

        }));
        menu.add(createMenuItem("Reset cookies", new AbstractAction() {
            /** UUID */
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                BrowserServices browserServices = BrowserServices.getInstance();
                for (BrowserType type : BrowserFactoryEx.getSupportedBrowser()) {
                    clearCookieIfSupported(browserServices, type);
                }
            }

            private void clearCookieIfSupported(BrowserServices browserServices, BrowserType type) {
                if (type.isSupported()) {
                    try {
                        HttpCookieStorage cookieStorage = browserServices.getCookieStorage(type);
                        List<HttpCookie> cookies = cookieStorage.getCookies();
                        cookieStorage.deleteCookie(cookies);
                    } catch (Exception e) {
                        NoOp.noOp();
                    }
                }
            }

        }));
        resetDropDownButton.setMenu(menu);
        return panel;
    }

    @Override
    public Dimension getMinimumSize() {
        return new Dimension(200, 0);
    }

    @Override
    public Dimension getMaximumSize() {
        return new Dimension(200, 0);
    }

    protected JTable createFilterTables(ConnectionFilter connectionFilter) {
        final FilterTableModel dm = new FilterTableModel(connectionFilter);
        JTable jTable = new JTable(dm);
        TableColumnModel columnModel = jTable.getColumnModel();
        setColumnWidth(columnModel.getColumn(0), 30, 30, 30);
        setColumnWidth(columnModel.getColumn(2), 30, 50, 30);
        jTable.setRowSorter(new TableRowSorter<TableModel>(dm));
        createUpdateCheckScheduledTask(dm);
        return jTable;
    }

    private void createUpdateCheckScheduledTask(final FilterTableModel dm) {
        this.timer = new Timer(true);
        this.timer.schedule(new TimerTask() {
            @Override
            public void run() {
                dm.update();
            }
        }, 1000, 1000);
    }

    private void setColumnWidth(TableColumn column, int initial, int max, int min) {
        if (initial != 0) {
            column.setPreferredWidth(initial);
        }
        if (max != 0) {
            column.setMaxWidth(max);
        }
        if (min != 0) {
            column.setMinWidth(min);
        }
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(100, 1000);
    }

}