com.intuit.tank.tools.debugger.AgentDebuggerFrame.java Source code

Java tutorial

Introduction

Here is the source code for com.intuit.tank.tools.debugger.AgentDebuggerFrame.java

Source

/**
 * Copyright 2011 Intuit Inc. All Rights Reserved
 */
package com.intuit.tank.tools.debugger;

/*
 * #%L
 * Intuit Tank Agent Debugger
 * %%
 * Copyright (C) 2011 - 2015 Intuit Inc.
 * %%
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * #L%
 */

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.HeadlessException;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
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.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

import javax.swing.BorderFactory;
import javax.swing.DefaultComboBoxModel;
import javax.swing.Icon;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JSplitPane;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.xml.bind.JAXBException;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.SquiggleUnderlineHighlightPainter;
import org.fife.ui.rtextarea.GutterIconInfo;
import org.fife.ui.rtextarea.RTextScrollPane;

import com.intuit.tank.api.model.v1.datafile.DataFileDescriptor;
import com.intuit.tank.api.model.v1.project.KeyPair;
import com.intuit.tank.api.model.v1.project.ProjectTO;
import com.intuit.tank.client.v1.datafile.DataFileClient;
import com.intuit.tank.harness.APITestHarness;
import com.intuit.tank.harness.TestPlanSingleton;
import com.intuit.tank.harness.data.HDScript;
import com.intuit.tank.harness.data.HDScriptGroup;
import com.intuit.tank.harness.data.HDScriptUseCase;
import com.intuit.tank.harness.data.HDTestPlan;
import com.intuit.tank.harness.data.HDTestVariables;
import com.intuit.tank.harness.data.HDWorkload;
import com.intuit.tank.harness.data.Header;
import com.intuit.tank.harness.data.RequestStep;
import com.intuit.tank.harness.data.TestStep;
import com.intuit.tank.harness.functions.JexlIOFunctions;
import com.intuit.tank.harness.functions.JexlStringFunctions;
import com.intuit.tank.harness.logging.LogUtil;
import com.intuit.tank.logging.LogEventType;
import com.intuit.tank.logging.LoggingProfile;
import com.intuit.tank.runner.TestStepContext;
import com.intuit.tank.tools.debugger.ActionProducer.IconSize;
import com.intuit.tank.vm.api.enumerated.WatsAgentCommand;
import com.intuit.tank.vm.common.TankConstants;

/**
 * ScrioptFilterRunner
 * 
 * @author dangleton
 * 
 */
public class AgentDebuggerFrame extends JFrame {

    private static final long serialVersionUID = 1L;
    private static final Logger LOG = Logger.getLogger(AgentDebuggerFrame.class);

    private RSyntaxTextArea scriptEditorTA;
    private RTextScrollPane scriptEditorScrollPane;
    private boolean standalone;
    private ActionProducer debuggerActions;
    private HDWorkload currentWorkload;
    private HDTestPlan currentTestPlan;
    private JComboBox testPlanChooser;
    private JComboBox<TankClientChoice> tankClientChooser;
    private List<StepListener> stepChangedListeners = new ArrayList<StepListener>();
    private List<ScriptChangedListener> scriptChangedListeners = new ArrayList<ScriptChangedListener>();
    private Map<String, String> projectVariables = new HashMap<String, String>();
    private List<Integer> datafileList = new ArrayList<Integer>();
    private List<DebugStep> steps = new ArrayList<DebugStep>();
    private int currentRunningStep;
    private ActionComponents actionComponents;
    private DebuggerFlowController flowController;
    private APITestHarness harness;
    private File workingDir;
    private Thread runningThread;
    private RSyntaxTextArea loggerTA;
    private InfiniteProgressPanel glassPane;

    private Icon errorIcon;
    private Icon modifiedIcon;
    private Icon skippedIcon;
    private RequestResponsePanel requestResponsePanel;
    private ScriptSource scriptSource;

    private int lastLine;
    private int multiSelectStart;
    private int multiSelectEnd;
    private boolean multiSelect;

    /**
     * @throws HeadlessException
     */
    public AgentDebuggerFrame(final boolean isStandalone, String serviceUrl) throws HeadlessException {
        super("Intuit Tank Agent Debugger");
        workingDir = PanelBuilder.createWorkingDir(this, serviceUrl);
        setSize(new Dimension(1024, 800));
        setBounds(new Rectangle(getSize()));
        setPreferredSize(getSize());
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        setLayout(new BorderLayout());
        this.standalone = isStandalone;
        addWindowListener(new WindowAdapter() {
            public void windowClosed(WindowEvent e) {
                quit();
            }
        });
        errorIcon = ActionProducer.getIcon("bullet_error.png", IconSize.SMALL);
        modifiedIcon = ActionProducer.getIcon("bullet_code_change.png", IconSize.SMALL);
        skippedIcon = ActionProducer.getIcon("skip.png", IconSize.SMALL);

        this.glassPane = new InfiniteProgressPanel();
        setGlassPane(glassPane);
        debuggerActions = new ActionProducer(this, serviceUrl);
        requestResponsePanel = new RequestResponsePanel(this);
        requestResponsePanel.init();
        testPlanChooser = new JComboBox();
        testPlanChooser.addItemListener(new ItemListener() {

            @Override
            public void itemStateChanged(ItemEvent event) {
                if (event.getItem() != null) {
                    HDTestPlan selected = (HDTestPlan) event.getItem();
                    if (!selected.equals(currentTestPlan)) {
                        setCurrentTestPlan(selected);
                    }
                }

            }
        });

        tankClientChooser = new JComboBox<TankClientChoice>();
        debuggerActions.setChoiceComboBoxOptions(tankClientChooser);

        actionComponents = new ActionComponents(standalone, testPlanChooser, tankClientChooser, debuggerActions);
        addScriptChangedListener(actionComponents);
        setJMenuBar(actionComponents.getMenuBar());

        Component topPanel = PanelBuilder.createTopPanel(actionComponents);
        Component bottomPanel = PanelBuilder.createBottomPanel(this);
        Component contentPanel = PanelBuilder.createContentPanel(this);

        final JPopupMenu popup = actionComponents.getPopupMenu();
        scriptEditorTA.setPopupMenu(null);

        scriptEditorTA.addMouseListener(new MouseAdapter() {
            int lastHash;

            @Override
            public void mousePressed(MouseEvent e) {
                maybeShow(e);
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                maybeShow(e);
            }

            private void maybeShow(MouseEvent e) {
                if (lastHash == getHash(e)) {
                    return;
                }
                if (e.isPopupTrigger()) {
                    // select the line
                    try {
                        int offset = scriptEditorTA.viewToModel(e.getPoint());
                        Rectangle modelToView = scriptEditorTA.modelToView(offset);
                        Point point = new Point(modelToView.x + 1, e.getPoint().y);
                        if (modelToView.contains(point)) {
                            if (!multiSelect) {
                                int line = scriptEditorTA.getLineOfOffset(offset);
                                scriptEditorTA.setCurrentLine(line);
                            }
                            popup.show(e.getComponent(), e.getX(), e.getY());
                        }
                    } catch (BadLocationException e1) {
                        e1.printStackTrace();
                    }
                } else if (e.isShiftDown()) {
                    int line = scriptEditorTA.getCaretLineNumber();
                    int start = Math.min(line, lastLine);
                    int end = Math.max(line, lastLine);
                    multiSelect = end - start > 1;
                    if (multiSelect) {
                        multiSelectStart = start;
                        multiSelectEnd = end;
                        try {
                            scriptEditorTA.setEnabled(true);
                            scriptEditorTA.select(scriptEditorTA.getLineStartOffset(start),
                                    scriptEditorTA.getLineEndOffset(end));
                            scriptEditorTA.setEnabled(false);
                        } catch (BadLocationException e1) {
                            e1.printStackTrace();
                            multiSelect = false;
                        }
                    }
                } else {
                    multiSelect = false;
                    lastLine = scriptEditorTA.getCaretLineNumber();
                }
                lastHash = getHash(e);
            }

            private int getHash(MouseEvent e) {
                return new HashCodeBuilder().append(e.getButton()).append(e.getSource().hashCode())
                        .append(e.getPoint()).toHashCode();
            }

        });

        JSplitPane mainSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
        mainSplit.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
        mainSplit.setTopComponent(contentPanel);
        mainSplit.setBottomComponent(bottomPanel);
        mainSplit.setDividerLocation(600);
        mainSplit.setResizeWeight(0.8D);
        mainSplit.setDividerSize(5);

        add(topPanel, BorderLayout.NORTH);
        add(mainSplit, BorderLayout.CENTER);

        WindowUtil.centerOnScreen(this);
        pack();

        KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();

        manager.addKeyEventDispatcher(new KeyEventDispatcher() {

            @Override
            public boolean dispatchKeyEvent(KeyEvent e) {
                if (e.getID() == KeyEvent.KEY_PRESSED) {
                    handleKeyEvent(e);
                }
                return false;
            }
        });
    }

    public ActionProducer getDebuggerActions() {
        return debuggerActions;
    }

    /**
     * @return the tankClientChooser
     */
    public JComboBox<TankClientChoice> getTankClientChooser() {
        return tankClientChooser;
    }

    private void handleKeyEvent(KeyEvent event) {
        if (!(event.getSource() instanceof JComboBox)) {
            if (event.getKeyCode() == KeyEvent.VK_UP) {
                moveCursor(true);
            } else if (event.getKeyCode() == KeyEvent.VK_DOWN) {
                moveCursor(false);
            }
        }
    }

    protected void moveCursor(boolean moveUp) {
        try {
            scriptEditorTA.grabFocus();
            int caretLineNumber = this.scriptEditorTA.getCaretLineNumber() + (moveUp ? -1 : 1);
            if (caretLineNumber > 0 && moveUp) {
                scriptEditorTA.setCurrentLine(caretLineNumber);
                fireStepChanged(caretLineNumber);
            }
            int lastLine = scriptEditorTA.getLineOfOffset(this.scriptEditorTA.getText().length());
            if (!moveUp && caretLineNumber <= lastLine) {
                scriptEditorTA.setCurrentLine(caretLineNumber);
                fireStepChanged(caretLineNumber);
            }
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
    }

    /**
     * @return the scriptSource
     */
    public ScriptSource getScriptSource() {
        return scriptSource;
    }

    /**
     * @param scriptSource
     *            the scriptSource to set
     */
    public void setScriptSource(ScriptSource scriptSource) {
        this.scriptSource = scriptSource;
        debuggerActions.getReloadAction().setEnabled(scriptSource != null);
    }

    public void startWaiting() {
        glassPane.start();
    }

    public void stopWaiting() {
        glassPane.stop();
    }

    public void addStepChangedListener(StepListener l) {
        stepChangedListeners.add(l);
    }

    public void addScriptChangedListener(ScriptChangedListener l) {
        scriptChangedListeners.add(l);
    }

    public boolean runTimingSteps() {
        return actionComponents.getRunTimingStepsCB().isSelected();
    }

    /**
     * @return the scriptEditorTA
     */
    public RSyntaxTextArea getScriptEditorTA() {
        return scriptEditorTA;
    }

    /**
     * @param scriptEditorTA
     *            the scriptEditorTA to set
     */
    public void setScriptEditorTA(RSyntaxTextArea scriptEditorTA) {
        this.scriptEditorTA = scriptEditorTA;
    }

    /**
     * @return the scriptEditorScrollPane
     */
    public RTextScrollPane getScriptEditorScrollPane() {
        return scriptEditorScrollPane;
    }

    /**
     * @param scriptEditorScrollPane
     *            the scriptEditorScrollPane to set
     */
    public void setScriptEditorScrollPane(RTextScrollPane scriptEditorScrollPane) {
        this.scriptEditorScrollPane = scriptEditorScrollPane;
    }

    /**
     * @return the steps
     */
    public List<DebugStep> getSteps() {
        return steps;
    }

    /**
     * @return the loggerTA
     */
    public RSyntaxTextArea getLoggerTA() {
        return loggerTA;
    }

    /**
     * @param loggerTA
     *            the loggerTA to set
     */
    public void setLoggerTA(RSyntaxTextArea loggerTA) {
        this.loggerTA = loggerTA;
    }

    /**
     * @return the projectVariables
     */
    public Map<String, String> getProjectVariables() {
        return projectVariables;
    }

    /**
     * @param projectVariables
     *            the projectVariables to set
     */
    public void setProjectVariables(Map<String, String> projectVariables) {
        this.projectVariables.clear();
        if (projectVariables != null) {
            this.projectVariables.putAll(projectVariables);
        }
    }

    /**
     * @return the currentWorkload
     */
    public HDWorkload getCurrentWorkload() {
        return currentWorkload;
    }

    public void clearBookmarks() {
        GutterIconInfo[] bookmarks = this.scriptEditorScrollPane.getGutter().getBookmarks();
        for (GutterIconInfo gi : bookmarks) {
            try {
                int line = this.scriptEditorTA.getLineOfOffset(gi.getMarkedOffset());
                this.scriptEditorScrollPane.getGutter().toggleBookmark(line);
            } catch (BadLocationException e) {
                LOG.error("Error unsetting bookmark: " + e);
            }
        }
    }

    public void clearSkips() {
        GutterIconInfo[] bookmarks = this.scriptEditorScrollPane.getGutter().getAllTrackingIcons();
        for (GutterIconInfo gi : bookmarks) {
            this.scriptEditorScrollPane.getGutter().removeTrackingIcon(gi);
            if (flowController != null) {
                flowController.getSkipList().clear();
            }
        }
    }

    public boolean setStepfromString(String stepXML) {
        boolean ret = false;
        try {
            TestStep unmarshalledStep = JaxbUtil.unmarshall(stepXML, TestStep.class);
            scriptEditorScrollPane.getGutter().addOffsetTrackingIcon(
                    scriptEditorTA.getLineStartOffset(unmarshalledStep.getStepIndex()), modifiedIcon);

            DebugStep debugStep = steps.get(unmarshalledStep.getStepIndex());
            debugStep.setStepRun(unmarshalledStep);
            if (currentTestPlan != null) {
                for (HDScriptGroup group : currentTestPlan.getGroup()) {
                    for (HDScript script : group.getGroupSteps()) {
                        for (HDScriptUseCase useCase : script.getUseCase()) {
                            for (int i = useCase.getScriptSteps().size(); --i >= 0;) {
                                TestStep step = useCase.getScriptSteps().get(i);
                                if (step.getStepIndex() == unmarshalledStep.getStepIndex()) {
                                    useCase.getScriptSteps().remove(i);
                                    updateStepLabel(unmarshalledStep);
                                    useCase.getScriptSteps().add(i, unmarshalledStep);
                                }
                            }
                        }
                    }
                }
                int pos = scriptEditorTA.getCaretPosition();
                buildEditorString(currentTestPlan);
                scriptEditorTA.setCaretPosition(pos);
            }
            ret = true;
        } catch (Exception e) {
            showError("Error unmarshalling step: " + e);
        }
        return ret;
    }

    private void updateStepLabel(TestStep testStep) {

        if (testStep instanceof RequestStep) {
            StringBuilder label = new StringBuilder();
            RequestStep step = (RequestStep) testStep;
            label.append(step.getRequest().getProtocol()).append("://").append(step.getRequest().getHost())
                    .append(step.getRequest().getPath());

            int qsCount = 0;
            if (step.getRequest().getQueryString() != null) {
                for (Header qs : step.getRequest().getQueryString()) {
                    label.append(qsCount == 0 ? "?" : "&");
                    label.append(qs.getKey()).append("=").append(qs.getValue());
                    qsCount++;
                }
            }
            step.getRequest().setLabel(StringUtils.abbreviate(label.toString(), 1024));
        }
        return;
    }

    public void showError(String msg) {
        LOG.error("Error: " + msg);
        stopWaiting();
        JOptionPane.showMessageDialog(this, msg, "Error", JOptionPane.ERROR_MESSAGE);
    }

    /**
     * Quits the app and attempts to delete the working dir
     */
    public void quit() {
        super.setVisible(false);
        if (workingDir.exists()) {
            try {
                FileUtils.deleteDirectory(workingDir);
            } catch (IOException e) {
                System.out.println("Error deleting directory " + workingDir.getAbsolutePath() + ": " + e);
            }
        }
        if (this.standalone) {
            System.exit(0);
        }
    }

    /**
     * @param currentWorkload
     *            the currentWorkload to set
     */
    public void setCurrentWorkload(HDWorkload currentWorkload) {
        setCurrentTestPlan(null);
        this.currentWorkload = currentWorkload;
        if (currentWorkload != null) {
            DefaultComboBoxModel model = new DefaultComboBoxModel(
                    currentWorkload.getPlans().toArray(new HDTestPlan[currentWorkload.getPlans().size()]));
            if (currentWorkload.getPlans().size() > 0) {
                setCurrentTestPlan(currentWorkload.getPlans().get(0));
                model.setSelectedItem(currentTestPlan);
            }
            testPlanChooser.setModel(model);
        } else {
            setCurrentTestPlan(null);
            testPlanChooser.setModel(new DefaultComboBoxModel());
        }
    }

    /**
     * @return the currentTestPlan
     */
    public HDTestPlan getCurrentTestPlan() {
        return currentTestPlan;
    }

    public void setCurrentStep(final int stepIndex) {
        currentRunningStep = stepIndex;
        int stepToSet = Math.max(0, currentRunningStep);
        if (currentRunningStep < 0) {
            scriptEditorTA.setActiveLineRange(-1, -1);
        } else {
            scriptEditorTA.setActiveLineRange(currentRunningStep + 1, currentRunningStep + 1);
        }
        scriptEditorTA.setCurrentLine(stepToSet);
        repaint();
        if (flowController.isSkipping()) {
            actionComponents.skipTo();
        } else {
            actionComponents.doneSkipping();
        }
    }

    /**
     * @return the currentRunningStep
     */
    public int getCurrentRunningStep() {
        return currentRunningStep;
    }

    public ActionComponents getActionComponents() {
        return actionComponents;
    }

    /**
     * @param currentTestPlan
     *            the currentTestPlan to set
     */
    public void setCurrentTestPlan(HDTestPlan currentTestPlan) {
        this.currentTestPlan = currentTestPlan;
        steps.clear();
        currentRunningStep = -1;
        scriptEditorScrollPane.getGutter().removeAllTrackingIcons();
        buildEditorString(currentTestPlan);
        if (!steps.isEmpty()) {
            fireStepChanged(0);
        }
        scriptEditorTA.setCaretPosition(0);
        fireScriptChanged();
    }

    private void buildEditorString(HDTestPlan currentTestPlan) {
        StringBuilder sb = new StringBuilder();
        if (currentTestPlan != null) {
            for (HDScriptGroup group : currentTestPlan.getGroup()) {
                for (HDScript script : group.getGroupSteps()) {
                    for (HDScriptUseCase useCase : script.getUseCase()) {
                        for (TestStep step : useCase.getScriptSteps()) {
                            if (sb.length() != 0) {
                                sb.append('\n');
                            }
                            sb.append(step.getInfo());
                            steps.add(new DebugStep(step));
                        }
                    }
                }
            }
        }
        GutterIconInfo[] allTrackingIcons = scriptEditorScrollPane.getGutter().getAllTrackingIcons();
        List<IconContainer> icons = new ArrayList<IconContainer>(allTrackingIcons.length);
        for (GutterIconInfo gi : allTrackingIcons) {
            try {
                icons.add(new IconContainer(scriptEditorTA.getLineOfOffset(gi.getMarkedOffset()), gi.getIcon()));
            } catch (BadLocationException e) {
                e.printStackTrace();
            }
        }
        scriptEditorScrollPane.getGutter().removeAllTrackingIcons();
        scriptEditorTA.setText(sb.toString());
        for (IconContainer ic : icons) {
            try {
                scriptEditorScrollPane.getGutter()
                        .addOffsetTrackingIcon(scriptEditorTA.getLineStartOffset(ic.getLine()), ic.getIcon());
            } catch (BadLocationException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @return the standalone
     */
    public boolean isStandalone() {
        return standalone;
    }

    private String buildScriptString() throws JAXBException {
        HDWorkload workload = new HDWorkload();
        workload.setName(currentWorkload.getName());
        workload.setDescription(currentWorkload.getDescription());
        if (currentWorkload.getVariables() != null) {
            HDTestVariables variables = new HDTestVariables(currentWorkload.getVariables().isAllowOverride());
            for (Entry<String, String> entry : this.projectVariables.entrySet()) {
                variables.addVariable(entry.getKey(), entry.getValue());
            }
            workload.setVariables(variables);
        }
        workload.getPlans().add(currentTestPlan);
        return JaxbUtil.marshall(workload);
    }

    void fireScriptChanged() {
        for (ScriptChangedListener l : scriptChangedListeners) {
            l.scriptChanged(currentTestPlan);
        }
    }

    void fireStepChanged(int stepIndex) {
        DebugStep step = null;
        if (stepIndex >= 0 && stepIndex < steps.size()) {
            step = steps.get(stepIndex);
        }
        for (StepListener l : stepChangedListeners) {
            l.stepChanged(step);
        }
    }

    void fireStepStarted(int stepIndex) {
        DebugStep step = null;
        if (stepIndex >= 0 && stepIndex < steps.size()) {
            step = steps.get(stepIndex);
        }
        for (StepListener l : stepChangedListeners) {
            l.stepEntered(step);
        }
    }

    void fireStepExited(int stepIndex) {
        DebugStep step = null;
        if (stepIndex >= 0 && stepIndex < steps.size()) {
            step = steps.get(stepIndex);
        }
        for (StepListener l : stepChangedListeners) {
            l.stepExited(step);
        }
    }

    public void setDataFromProject(ProjectTO selected) {
        projectVariables.clear();
        datafileList.clear();
        for (KeyPair pair : selected.getVariables()) {
            projectVariables.put(pair.getKey(), pair.getValue());
        }
        datafileList.addAll(selected.getDataFileIds());
    }

    public void next() {
        flowController.doNext();
    }

    public boolean hasBreakPoint(int i) {
        try {
            GutterIconInfo[] bookmarks = this.scriptEditorScrollPane.getGutter().getBookmarks();
            for (GutterIconInfo info : bookmarks) {
                int line = this.scriptEditorTA.getLineOfOffset(info.getMarkedOffset());
                if (line == i) {
                    return true;
                }
            }
        } catch (BadLocationException e) {
            LOG.warn("Error processing breakpoints: " + e);
        }
        return false;
    }

    public void runToBreakpoint() {
        flowController.setSkipping(true);
        actionComponents.skipTo();
        flowController.doNext();

    }

    public void stop() {
        try {
            if (harness != null) {
                harness.setCommand(WatsAgentCommand.kill);
            }
            if (runningThread != null) {
                runningThread.interrupt();
            }
        } finally {
            testFinished();
            runningThread = null;
            harness = null;
            actionComponents.stop();
        }
    }

    public void start() {
        startWaiting();
        flowController = new DebuggerFlowController(this);
        scriptEditorTA.getHighlighter().removeAllHighlights();
        actionComponents.start();
        loggerTA.setText("");
        loggerTA.setCaretPosition(0);
        loggerTA.repaint();
        new Thread(new Runnable() {
            public void run() {

                fireStepChanged(-1);
                if (!steps.isEmpty()) {
                    for (DebugStep step : steps) {
                        step.clear();
                    }
                    setCurrentStep(-1);
                    for (GutterIconInfo gi : scriptEditorScrollPane.getGutter().getAllTrackingIcons()) {
                        if (gi.getIcon() == errorIcon) {
                            scriptEditorScrollPane.getGutter().removeTrackingIcon(gi);
                        } else if (gi.getIcon() == skippedIcon) {
                            try {
                                flowController.skip(scriptEditorTA.getLineOfOffset(gi.getMarkedOffset()));
                            } catch (BadLocationException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                    // start apiHarness and get the variables....
                    try {
                        createHarness();
                        runningThread = new Thread(new Runnable() {
                            public void run() {
                                harness.runConcurrentTestPlans();
                            }
                        });

                        runningThread.start();
                    } catch (Exception e) {
                        showError("Error starting test: " + e);
                        actionComponents.stop();
                    }

                } else {
                    stopWaiting();
                    actionComponents.stop();
                }
            }
        }).start();
    }

    private void createHarness() throws JAXBException {
        APITestHarness.destroyCurrentInstance();
        harness = APITestHarness.getInstance();
        harness.setFlowControllerTemplate(flowController);
        TankClientChoice choice = (TankClientChoice) tankClientChooser.getSelectedItem();
        harness.setTankHttpClientClass(choice.getClientClass());
        harness.getAgentRunData().setActiveProfile(LoggingProfile.VERBOSE);
        harness.getAgentRunData().setInstanceId("DebuggerInstance");
        harness.getAgentRunData().setMachineName("debugger");
        harness.getAgentRunData().setNumUsers(1);
        harness.getAgentRunData().setJobId("debuggerJob");
        harness.getAgentRunData().setTotalAgents(1);
        harness.setDebug(true);

        List<String> testPlansXmls = new ArrayList<String>();
        testPlansXmls.add(buildScriptString());
        TestPlanSingleton.getInstance().setTestPlans(testPlansXmls);
        downloadDataFiles();
        JexlIOFunctions.resetStatics();
        JexlStringFunctions.resetStatics();
    }

    private void downloadDataFiles() {
        if (!datafileList.isEmpty()) {
            DataFileClient client = debuggerActions.getDataFileClient();
            for (Integer id : datafileList) {
                DataFileDescriptor dataFile = client.getDataFile(id);
                if (dataFile != null) {
                    saveDataFile(client, dataFile, datafileList.size() == 1);
                }
            }
        }
    }

    private void saveDataFile(DataFileClient client, DataFileDescriptor dataFileDescriptor, boolean isDefault) {
        File dataFile = new File(workingDir, dataFileDescriptor.getName());
        FileOutputStream fos = null;
        InputStream is = null;
        try {
            LOG.info("writing file " + dataFileDescriptor.getName() + " to " + dataFile.getAbsolutePath());
            is = client.getDataFileDataStream(dataFileDescriptor.getId());
            fos = new FileOutputStream(dataFile);
            IOUtils.copy(is, fos);
            IOUtils.closeQuietly(fos);
            if (isDefault && !dataFileDescriptor.getName().equals(TankConstants.DEFAULT_CSV_FILE_NAME)) {
                File defaultFile = new File(workingDir, TankConstants.DEFAULT_CSV_FILE_NAME);
                FileUtils.copyFile(dataFile, defaultFile);
            }
        } catch (Exception e) {
            LOG.error("Error downloading csv file: " + e, e);
            throw new RuntimeException(e);
        } finally {
            IOUtils.closeQuietly(is);
            IOUtils.closeQuietly(fos);
        }

    }

    public void setNextStep(TestStepContext context) {
        setCurrentStep(context.getTestStep().getStepIndex());
    }

    public void moveCursor(TestStepContext context) {
        int stepIndex = context.getTestStep().getStepIndex();
        scriptEditorTA.setActiveLineRange(stepIndex, stepIndex);
    }

    public void stepStarted(final TestStepContext context) {
        try {
            SwingUtilities.invokeAndWait(new Runnable() {
                public void run() {
                    actionComponents.stepping();
                    int stepIndex = context.getTestStep().getStepIndex();
                    setCurrentStep(stepIndex);
                    DebugStep debugStep = steps.get(currentRunningStep);
                    if (debugStep != null) {
                        debugStep.setEntryVariables(context.getVariables().getVaribleValues());
                        debugStep.setRequest(context.getRequest());
                        debugStep.setResponse(context.getResponse());
                    }
                    fireStepChanged(stepIndex);
                    fireStepStarted(stepIndex);
                }
            });
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    public void stepFinished(final TestStepContext context) {
        try {
            SwingUtilities.invokeAndWait(new Runnable() {
                public void run() {
                    actionComponents.doneStepping();
                    DebugStep debugStep = steps.get(currentRunningStep);
                    if (debugStep != null) {
                        debugStep.setExitVariables(context.getVariables().getVaribleValues());
                        debugStep.setRequest(context.getRequest());
                        debugStep.setResponse(context.getResponse());
                    }
                    try {
                        if (context.getResponse() != null && (context.getResponse().getHttpCode() >= 400
                                || context.getResponse().getHttpCode() == -1)) {
                            // highlight the line
                            int lineStartOffset = scriptEditorTA.getLineStartOffset(currentRunningStep);
                            int lineEndOffset = scriptEditorTA.getLineEndOffset(currentRunningStep);

                            scriptEditorTA.getHighlighter().addHighlight(lineStartOffset, lineEndOffset,
                                    new SquiggleUnderlineHighlightPainter(Color.RED));
                        }
                    } catch (BadLocationException e1) {
                        e1.printStackTrace();
                    }
                    if (!context.getErrors().isEmpty()) {
                        try {
                            debugStep.setErrors(context.getErrors());
                            scriptEditorScrollPane.getGutter().addOffsetTrackingIcon(
                                    scriptEditorTA.getLineStartOffset(currentRunningStep), errorIcon);
                        } catch (BadLocationException e) {
                            e.printStackTrace();
                        }
                    }
                    fireStepExited(context.getTestStep().getStepIndex());
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void testStarted() {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                stopWaiting();
                actionComponents.setRunningActions(true);
            }
        });

    }

    public void testFinished() {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                runningThread = null;
                harness = null;
                scriptEditorTA.setActiveLineRange(-1, -1);
                actionComponents.stop();
            }
        });

    }

    public void setDataFiles(List<DataFileDescriptor> selectedObjects) {
        this.datafileList.clear();
        for (DataFileDescriptor d : selectedObjects) {
            datafileList.add(d.getId());
        }

    }

    public TestStep getStep(int stepIndex) {
        return steps.get(stepIndex).getStepRun();

    }

    public void setCurrentTitle(String string) {
        this.actionComponents.setCurrentTitle(string);

    }

    public void pause() {
        flowController.skipTo(-1);
        flowController.setSkipping(false);
        this.actionComponents.doneSkipping();

    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        try {
            java.security.Security.setProperty("networkaddress.cache.ttl", "0");
        } catch (Throwable e1) {
            LOG.warn(LogUtil.getLogMessage("Error setting dns timeout: " + e1.toString(), LogEventType.System));
        }
        try {
            System.setProperty("jsse.enableSNIExtension", "false");
        } catch (Throwable e1) {
            LOG.warn(LogUtil.getLogMessage("Error disabling SNI extension: " + e1.toString(), LogEventType.System));
        }
        try {
            System.setProperty("jdk.certpath.disabledAlgorithms", "");
        } catch (Throwable e1) {
            System.err.println("Error setting property jdk.certpath.disabledAlgorithms: " + e1.toString());
            e1.printStackTrace();
        }
        String url = "";
        if (args.length > 0) {
            url = args[0];
        }
        Properties props = new Properties();
        try {
            InputStream configStream = AgentDebuggerFrame.class.getResourceAsStream("/log4j.properties");
            props.load(configStream);
            configStream.close();
        } catch (IOException e) {
            System.out.println("Error: Cannot laod configuration file ");
        }
        props.setProperty("log4j.appender.agent.File", "debugger.log");
        LogManager.resetConfiguration();
        PropertyConfigurator.configure(props);

        new AgentDebuggerFrame(true, url).setVisible(true);
    }

    public void skip() {
        flowController.skip(currentRunningStep + 1);
        try {
            int lineStartOffset = scriptEditorTA.getLineStartOffset(currentRunningStep + 1);
            for (GutterIconInfo info : scriptEditorScrollPane.getGutter().getAllTrackingIcons()) {
                if (info.getMarkedOffset() == lineStartOffset && info.getIcon() == skippedIcon) {
                    scriptEditorScrollPane.getGutter().removeTrackingIcon(info);
                }
            }
            scriptEditorScrollPane.getGutter()
                    .addOffsetTrackingIcon(scriptEditorTA.getLineStartOffset(currentRunningStep + 1), skippedIcon);
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
        flowController.doNext();
    }

    public void toggleBreakPoint() {
        try {
            if (multiSelect) {
                for (int line = multiSelectStart; line <= multiSelectEnd; line++) {
                    scriptEditorScrollPane.getGutter().toggleBookmark(line);
                }
            } else {
                int line = scriptEditorTA.getCaretLineNumber();
                scriptEditorScrollPane.getGutter().toggleBookmark(line);
            }
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
    }

    public void toggleSkip() {
        if (multiSelect) {
            for (int line = multiSelectStart; line <= multiSelectEnd; line++) {
                doToggleSkip(line);
            }
        } else {
            int line = scriptEditorTA.getCaretLineNumber();
            doToggleSkip(line);
        }
    }

    private void doToggleSkip(int line) {
        try {
            boolean found = false;
            for (GutterIconInfo info : scriptEditorScrollPane.getGutter().getAllTrackingIcons()) {
                if (scriptEditorTA.getLineOfOffset(info.getMarkedOffset()) == line
                        && info.getIcon() == skippedIcon) {
                    scriptEditorScrollPane.getGutter().removeTrackingIcon(info);
                    found = true;
                }
            }
            if (!found) {
                scriptEditorScrollPane.getGutter().addOffsetTrackingIcon(scriptEditorTA.getLineStartOffset(line),
                        skippedIcon);
            }
            if (flowController != null) {
                if (flowController.getSkipList().contains(line)) {
                    flowController.removeSkip(line);
                } else {
                    flowController.skip(line);
                }
            }
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
    }

    public RequestResponsePanel getRequestResponsePanel() {
        return requestResponsePanel;
    }

}