com.excilys.ebi.gatling.recorder.ui.component.RunningFrame.java Source code

Java tutorial

Introduction

Here is the source code for com.excilys.ebi.gatling.recorder.ui.component.RunningFrame.java

Source

/**
 * Copyright 2011-2012 eBusiness Information, Groupe Excilys (www.excilys.com)
 *
 * 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 com.excilys.ebi.gatling.recorder.ui.component;

import static com.excilys.ebi.gatling.recorder.http.event.RecorderEventBus.getEventBus;
import static com.excilys.ebi.gatling.recorder.ui.Constants.GATLING_REQUEST_BODIES_DIRECTORY_NAME;
import static org.apache.commons.io.IOUtils.closeQuietly;
import static org.apache.commons.lang.StringUtils.EMPTY;

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextField;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
import org.codehaus.plexus.util.Base64;
import org.codehaus.plexus.util.SelectorUtils;
import org.jboss.netty.handler.codec.http.HttpMethod;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.QueryStringDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.excilys.ebi.gatling.recorder.configuration.Configuration;
import com.excilys.ebi.gatling.recorder.configuration.Pattern;
import com.excilys.ebi.gatling.recorder.http.GatlingHttpProxy;
import com.excilys.ebi.gatling.recorder.http.event.BasicAuth;
import com.excilys.ebi.gatling.recorder.http.event.PauseEvent;
import com.excilys.ebi.gatling.recorder.http.event.RequestReceivedEvent;
import com.excilys.ebi.gatling.recorder.http.event.ResponseReceivedEvent;
import com.excilys.ebi.gatling.recorder.http.event.SecuredHostConnectionEvent;
import com.excilys.ebi.gatling.recorder.http.event.ShowConfigurationFrameEvent;
import com.excilys.ebi.gatling.recorder.http.event.ShowRunningFrameEvent;
import com.excilys.ebi.gatling.recorder.http.event.TagEvent;
import com.excilys.ebi.gatling.recorder.ui.enumeration.FilterStrategy;
import com.excilys.ebi.gatling.recorder.ui.enumeration.PauseType;
import com.excilys.ebi.gatling.recorder.ui.enumeration.ResultType;
import com.google.common.collect.MapDifference;
import com.google.common.collect.Maps;
import com.google.common.eventbus.Subscribe;

@SuppressWarnings("serial")
public class RunningFrame extends JFrame {

    private static final Logger logger = LoggerFactory.getLogger(RunningFrame.class);
    private static final int EVENTS_GROUPING = 100;

    private Configuration configuration;
    private GatlingHttpProxy proxy;
    private Date startDate;
    private Date lastRequest;
    private PauseEvent pause;
    private BasicAuth basicAuth = null;

    private JTextField txtTag = new JTextField(15);
    private JButton btnTag = new JButton("Add");
    private DefaultListModel events = new DefaultListModel();
    private JList executedEvents = new JList(events);
    private DefaultListModel hostsCertificate = new DefaultListModel();
    private JList requiredHostsCertificate = new JList(hostsCertificate);
    private TextAreaPanel stringRequest = new TextAreaPanel("Request:");
    private TextAreaPanel stringResponse = new TextAreaPanel("Response:");
    private TextAreaPanel stringRequestBody = new TextAreaPanel("Request Body:");
    private int numberOfRequests = 0;

    private List<Object> listEvents = new ArrayList<Object>();
    private String protocol;
    private String host;
    private int port;
    private String urlBase = null;
    private String urlBaseString = null;
    private LinkedHashMap<String, String> urls = new LinkedHashMap<String, String>();
    private LinkedHashMap<String, Map<String, String>> headers = new LinkedHashMap<String, Map<String, String>>();

    public RunningFrame() {

        /* Initialization of the frame */
        setTitle("Gatling Recorder - Running...");
        setMinimumSize(new Dimension(800, 640));
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        GridBagLayout gbl = new GridBagLayout();
        setLayout(gbl);

        setIconImages(Commons.getIconList());

        /* Declaration & initialization of components */
        JButton btnClear = new JButton("Clear");
        final JButton btnStop = new JButton("Stop !");
        btnStop.setSize(120, 30);

        JScrollPane panelFilters = new JScrollPane(executedEvents);
        panelFilters.setPreferredSize(new Dimension(300, 100));

        stringRequest.setPreferredSize(new Dimension(330, 100));
        JSplitPane requestResponsePane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JScrollPane(stringRequest),
                new JScrollPane(stringResponse));
        final JSplitPane sp = new JSplitPane(JSplitPane.VERTICAL_SPLIT, requestResponsePane, stringRequestBody);

        JScrollPane panelHostsCertificate = new JScrollPane(requiredHostsCertificate);
        panelHostsCertificate.setPreferredSize(new Dimension(300, 100));

        /* Layout */
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(10, 5, 0, 0);

        gbc.gridx = 0;
        gbc.anchor = GridBagConstraints.LINE_START;
        gbc.gridy = 0;
        add(new JLabel("Tag :"), gbc);

        gbc.gridx = 1;
        add(txtTag, gbc);

        gbc.gridx = 2;
        gbc.weightx = 0.5;
        add(btnTag, gbc);

        gbc.gridx = 3;
        gbc.anchor = GridBagConstraints.CENTER;
        gbc.weightx = 0.25;
        add(btnClear, gbc);

        gbc.gridx = 4;
        gbc.anchor = GridBagConstraints.LINE_END;
        add(btnStop, gbc);

        gbc.gridx = 0;
        gbc.gridy = 1;
        gbc.weightx = 0;
        gbc.anchor = GridBagConstraints.LINE_START;
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        add(new JLabel("Executed Events:"), gbc);

        gbc.gridy = 2;
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        gbc.weightx = 1;
        gbc.weighty = 0.20;
        gbc.fill = GridBagConstraints.BOTH;
        add(panelFilters, gbc);

        gbc.gridy = 4;
        gbc.weightx = 1;
        gbc.weighty = 0.75;
        gbc.fill = GridBagConstraints.BOTH;
        add(sp, gbc);

        gbc.gridy = 5;
        gbc.weighty = 0;
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        gbc.anchor = GridBagConstraints.CENTER;
        add(new JLabel("Secured hosts requiring accepting a certificate:"), gbc);

        gbc.gridy = 6;
        gbc.weighty = 0.05;
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        add(panelHostsCertificate, gbc);

        /* Listeners */
        btnTag.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                if (!txtTag.getText().equals(EMPTY)) {
                    TagEvent tag = new TagEvent(txtTag.getText());
                    events.addElement(tag.toString());
                    executedEvents.ensureIndexIsVisible(events.getSize() - 1);
                    listEvents.add(tag);
                    txtTag.setText(EMPTY);
                }
            }
        });

        executedEvents.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (executedEvents.getSelectedIndex() >= 0) {
                    Object obj = listEvents.get(executedEvents.getSelectedIndex());
                    if (obj instanceof ResponseReceivedEvent) {
                        ResponseReceivedEvent event = (ResponseReceivedEvent) obj;
                        stringRequest.txt.setText(event.getRequest().toString());
                        stringResponse.txt.setText(event.getResponse().toString());
                        stringRequestBody.txt.setText(new String(event.getRequest().getContent().array()));
                    } else {
                        stringRequest.txt.setText(EMPTY);
                        stringResponse.txt.setText(EMPTY);
                        stringRequestBody.txt.setText(EMPTY);
                    }
                }
            }
        });

        btnClear.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                clearOldRunning();
            }
        });

        btnStop.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                saveScenario();
                proxy.shutdown();
                proxy = null;
                if (!Configuration.getInstance().isConfigurationSkipped())
                    getEventBus().post(new ShowConfigurationFrameEvent());
                else
                    System.exit(0);
            }
        });
    }

    @Subscribe
    public void onShowConfigurationFrameEvent(ShowConfigurationFrameEvent event) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                setVisible(false);
            }
        });
    }

    @Subscribe
    public void onShowRunningFrameEvent(ShowRunningFrameEvent event) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                setVisible(true);
                clearOldRunning();
                configuration = Configuration.getInstance();
                startDate = new Date();
                proxy = new GatlingHttpProxy(configuration.getPort(), configuration.getSslPort(),
                        configuration.getProxy());
                proxy.start();
            }
        });
    }

    @Subscribe
    public void onSecuredHostConnectionEvent(final SecuredHostConnectionEvent event) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                if (!hostsCertificate.contains(event.getHostURI()))
                    hostsCertificate.addElement(event.getHostURI());
            }
        });
    }

    @Subscribe
    public void onRequestReceivedEvent(final RequestReceivedEvent event) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                if (event.getRequest().getMethod() == HttpMethod.CONNECT)
                    return;

                String header = event.getRequest().getHeader("Proxy-Authorization");
                if (header != null) {
                    // Split on " " and take 2nd group (Basic
                    // credentialsInBase64==)
                    String credentials = new String(Base64.decodeBase64(header.split(" ")[1].getBytes()));
                    configuration.getProxy().setUsername(credentials.split(":")[0]);
                    configuration.getProxy().setPassword(credentials.split(":")[1]);
                }

                if (addRequest(event.getRequest())) {
                    if (lastRequest != null) {
                        Date newRequest = new Date();
                        long diff = newRequest.getTime() - lastRequest.getTime();
                        long pauseMin, pauseMax;
                        PauseType pauseType;
                        if (diff < 1000) {
                            pauseMin = (diff / 100l) * 100;
                            pauseMax = pauseMin + 100;
                            pauseType = PauseType.MILLISECONDS;
                        } else {
                            pauseMin = diff / 1000l;
                            pauseMax = pauseMin + 1;
                            pauseType = PauseType.SECONDS;
                        }
                        lastRequest = newRequest;
                        pause = new PauseEvent(pauseMin, pauseMax, pauseType);
                    }
                }
            }
        });
    }

    @Subscribe
    public void onResponseReceivedEvent(final ResponseReceivedEvent event) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                if (addRequest(event.getRequest())) {
                    if (pause != null) {
                        events.addElement(pause.toString());
                        executedEvents.ensureIndexIsVisible(events.getSize() - 1);
                        listEvents.add(pause);
                    }
                    lastRequest = new Date();
                    processRequest(event);
                }
            }
        });
    }

    private void clearOldRunning() {
        events.removeAllElements();
        stringRequest.txt.setText(EMPTY);
        stringRequestBody.txt.setText(EMPTY);
        stringResponse.txt.setText(EMPTY);
        listEvents.clear();
        urls.clear();
        headers.clear();
        protocol = null;
        host = null;
        port = -1;
        urlBase = null;
        urlBaseString = null;
        lastRequest = null;
    }

    private boolean addRequest(HttpRequest request) {
        URI uri = null;
        try {
            uri = new URI(request.getUri());
        } catch (URISyntaxException ex) {
            logger.error("Can't create URI from request uri (" + request.getUri() + ")" + ex.getStackTrace());
            // FIXME error handling
            return false;
        }

        if (configuration.getFilterStrategy() != FilterStrategy.NONE) {

            String p = EMPTY;
            boolean add = true;
            if (configuration.getFilterStrategy() == FilterStrategy.ONLY)
                add = true;
            else if (configuration.getFilterStrategy() == FilterStrategy.EXCEPT)
                add = false;

            for (Pattern pattern : configuration.getPatterns()) {
                switch (pattern.getPatternType()) {
                case ANT:
                    p = SelectorUtils.ANT_HANDLER_PREFIX;
                    break;
                case JAVA:
                    p = SelectorUtils.REGEX_HANDLER_PREFIX;
                    break;
                }
                p += pattern.getPattern() + SelectorUtils.PATTERN_HANDLER_SUFFIX;
                if (SelectorUtils.matchPath(p, uri.getPath()))
                    return add;
            }
            return !add;
        }
        return true;
    }

    private void processRequest(ResponseReceivedEvent event) {

        HttpRequest request = event.getRequest();

        URI uri = null;
        try {
            uri = new URI(request.getUri());
        } catch (URISyntaxException ex) {
            logger.error("Can't create URI from request uri (" + request.getUri() + ")" + ex.getStackTrace());
        }

        events.addElement(request.getMethod() + " | " + request.getUri());
        executedEvents.ensureIndexIsVisible(events.getSize() - 1);

        int id = ++numberOfRequests;
        event.setId(id);

        /* URLs */
        if (urlBase == null) {
            protocol = uri.getScheme();
            host = uri.getHost();
            port = uri.getPort();
            urlBase = protocol + "://" + host;
            urlBaseString = "PROTOCOL + \"://\" + HOST";
            if (port != -1) {
                urlBase += ":" + port;
                urlBaseString += " + \":\" + PORT";
            }
        }

        String requestUrlBase = uri.getScheme() + "://" + uri.getHost();
        if (uri.getPort() != -1)
            requestUrlBase += ":" + uri.getPort();
        if (requestUrlBase.equals(urlBase))
            event.setWithUrlBase(true);
        else
            urls.put("url_" + id, requestUrlBase + uri.getPath());

        String headerAuthorization = event.getRequest().getHeader("Authorization");
        request.removeHeader("Authorization");
        if (headerAuthorization != null) {
            if (basicAuth == null) {
                // Split on " " and take 2nd group (Basic credentialsInBase64==)
                String credentials = new String(Base64.decodeBase64(headerAuthorization.split(" ")[1].getBytes()));
                basicAuth = new BasicAuth(requestUrlBase, credentials.split(":")[0], credentials.split(":")[1]);
                event.setBasicAuth(basicAuth);
            } else {
                if (requestUrlBase.equals(basicAuth.getUrlBase()))
                    event.setBasicAuth(basicAuth);
                else
                    basicAuth = null;
            }
        }

        /* Headers */
        Map<String, String> requestHeaders = new TreeMap<String, String>();
        for (Entry<String, String> entry : request.getHeaders())
            requestHeaders.put(entry.getKey(), entry.getValue());
        requestHeaders.remove("Cookie");

        int bestChoice = 0;
        String headerKey = EMPTY;
        MapDifference<String, String> diff;
        Map<String, String> fullHeaders = new TreeMap<String, String>();
        boolean containsHeaders = false;

        if (headers.size() > 0) {
            for (Entry<String, Map<String, String>> header : headers.entrySet()) {

                fullHeaders = new TreeMap<String, String>(header.getValue());
                containsHeaders = false;

                if (header.getValue().containsKey("headers")) {
                    fullHeaders.putAll(headers.get(header.getValue().get("headers")));
                    fullHeaders.remove("headers");
                    containsHeaders = true;
                }

                diff = Maps.difference(fullHeaders, requestHeaders);
                logger.debug(diff.toString());
                if (diff.areEqual()) {
                    headerKey = header.getKey();
                    bestChoice = 1;
                    break;
                } else if (diff.entriesOnlyOnLeft().size() == 0 && diff.entriesDiffering().size() == 0
                        && !containsHeaders) {
                    // header are included in requestHeaders
                    headerKey = header.getKey();
                    bestChoice = 2;
                } else if (bestChoice > 2 && diff.entriesOnlyOnRight().size() == 0
                        && diff.entriesDiffering().size() == 0 && !containsHeaders) {
                    // requestHeaders are included in header
                    headerKey = header.getKey();
                    bestChoice = 3;
                }
            }
        }

        switch (bestChoice) {
        case 1:
            event.setHeadersId(headerKey);
            break;
        case 2:
            diff = Maps.difference(headers.get(headerKey), requestHeaders);
            TreeMap<String, String> tm2 = new TreeMap<String, String>(diff.entriesOnlyOnRight());
            headers.put("headers_" + id, tm2);
            headers.get("headers_" + id).put("headers", headerKey);
            event.setHeadersId("headers_" + id);
            break;
        case 3:
            diff = Maps.difference(headers.get(headerKey), requestHeaders);
            TreeMap<String, String> tm3 = new TreeMap<String, String>(diff.entriesInCommon());
            headers.put("headers_" + id, tm3);
            event.setHeadersId("headers_" + id);
            headers.remove(headerKey);
            tm3 = new TreeMap<String, String>(diff.entriesOnlyOnLeft());
            headers.put(headerKey, tm3);
            headers.get(headerKey).put("headers", "headers_" + id);
            break;
        default:
            headers.put("headers_" + id, requestHeaders);
            event.setHeadersId("headers_" + id);
        }

        /* Add check if status is not in 20X */
        if ((event.getResponse().getStatus().getCode() < 200) || (event.getResponse().getStatus().getCode() > 210))
            event.setWithCheck(true);

        /* Params */
        QueryStringDecoder decoder = new QueryStringDecoder(request.getUri());
        event.getRequestParams().putAll((decoder.getParameters()));

        /* Content */
        if (request.getContent().capacity() > 0) {
            String content = new String(request.getContent().array());
            // We check if it's a form validation and so we extract post params
            if ("application/x-www-form-urlencoded".equals(request.getHeader("Content-Type"))) {
                decoder = new QueryStringDecoder("http://localhost/?" + content);
                event.getRequestParams().putAll(decoder.getParameters());
            } else {
                event.setWithBody(true);
                dumpRequestBody(id, content);
            }
        }

        listEvents.add(event);
    }

    private void dumpRequestBody(int idEvent, String content) {
        File dir = null;
        if (configuration.getRequestBodiesFolder() == null)
            dir = new File(getOutputFolder(),
                    ResultType.FORMAT.format(startDate) + "_" + GATLING_REQUEST_BODIES_DIRECTORY_NAME);
        else
            dir = getFolder("request bodies", configuration.getRequestBodiesFolder());

        if (!dir.exists())
            dir.mkdir();

        FileWriter fw = null;
        try {
            fw = new FileWriter(
                    new File(dir, ResultType.FORMAT.format(startDate) + "_request_" + idEvent + ".txt"));
            fw.write(content);
        } catch (IOException ex) {
            logger.error("Error, while dumping request body... {}", ex.getStackTrace());
        } finally {
            closeQuietly(fw);
        }
    }

    private File getOutputFolder() {
        return getFolder("output", configuration.getOutputFolder());
    }

    private File getFolder(String folderName, String folderPath) {
        File folder = new File(folderPath);

        if (!folder.exists())
            if (!folder.mkdirs())
                throw new RuntimeException("Can't create " + folderName + " folder");

        return folder;
    }

    private void saveScenario() {
        VelocityEngine ve = new VelocityEngine();
        ve.setProperty("file.resource.loader.class", ClasspathResourceLoader.class.getName());
        ve.init();

        VelocityContext context = new VelocityContext();
        context.put("protocol", protocol);
        context.put("host", host);
        context.put("port", port);
        context.put("urlBase", urlBaseString);
        context.put("proxy", configuration.getProxy());
        context.put("urls", urls);
        context.put("headers", headers);
        context.put("name", "Scenario name");

        if (listEvents.size() > EVENTS_GROUPING) {
            List<List<Object>> subListsEvents = new ArrayList<List<Object>>();
            int numberOfSubLists = listEvents.size() / EVENTS_GROUPING + 1;
            for (int i = 0; i < numberOfSubLists; i++)
                subListsEvents.add(listEvents.subList(0 + EVENTS_GROUPING * i,
                        Math.min(EVENTS_GROUPING * (i + 1), listEvents.size() - 1)));

            context.put("chainEvents", subListsEvents);
            context.put("events", new ArrayList<Object>());
        } else {
            context.put("events", listEvents);
            context.put("chainEvents", new ArrayList<List<Object>>());
        }

        context.put("package", Configuration.getInstance().getIdePackage());
        context.put("date", ResultType.FORMAT.format(startDate));
        URI uri = URI.create("");
        context.put("URI", uri);

        Template template = null;
        Writer writer = null;
        for (ResultType resultType : configuration.getResultTypes()) {
            try {
                template = ve.getTemplate(resultType.getTemplate());
                writer = new OutputStreamWriter(
                        new FileOutputStream(
                                new File(getOutputFolder(), resultType.getScenarioFileName(startDate))),
                        configuration.getEncoding());
                template.merge(context, writer);
                writer.flush();

            } catch (IOException e) {
                logger.error("Error, while saving '" + resultType + "' scenario..." + e.getStackTrace());

            } finally {
                closeQuietly(writer);
            }
        }
    }
}