Logo Search packages:      
Sourcecode: jakarta-jmeter version File versions  Download package

MainFrame.java

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.apache.jmeter.gui;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.HashSet;
import java.util.Set;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTree;
import javax.swing.MenuElement;
import javax.swing.SwingUtilities;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;

import org.apache.jmeter.engine.event.LoopIterationEvent;
import org.apache.jmeter.gui.action.ActionNames;
import org.apache.jmeter.gui.action.ActionRouter;
import org.apache.jmeter.gui.tree.JMeterCellRenderer;
import org.apache.jmeter.gui.tree.JMeterTreeListener;
import org.apache.jmeter.gui.util.JMeterMenuBar;
import org.apache.jmeter.samplers.Remoteable;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.TestListener;
import org.apache.jmeter.threads.JMeterContextService;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.gui.ComponentUtil;

/**
 * The main JMeter frame, containing the menu bar, test tree, and an area for
 * JMeter component GUIs.
 *
 */
00075 public class MainFrame extends JFrame implements TestListener, Remoteable {

    // This is used to keep track of local (non-remote) tests
    // The name is chosen to be an unlikely host-name
    private static final String LOCAL = "*local*"; // $NON-NLS-1$

    // The default title for the Menu bar
    private static final String DEFAULT_TITLE =
        "Apache JMeter ("+JMeterUtils.getJMeterVersion()+")"; // $NON-NLS-1$ $NON-NLS-2$

    /** The menu bar. */
00086     private JMeterMenuBar menuBar;

    /** The main panel where components display their GUIs. */
00089     private JScrollPane mainPanel;

    /** The panel where the test tree is shown. */
00092     private JScrollPane treePanel;

    /** The test tree. */
00095     private JTree tree;

    /** An image which is displayed when a test is running. */
00098     private ImageIcon runningIcon = JMeterUtils.getImage("thread.enabled.gif");// $NON-NLS-1$

    /** An image which is displayed when a test is not currently running. */
00101     private ImageIcon stoppedIcon = JMeterUtils.getImage("thread.disabled.gif");// $NON-NLS-1$

    /** The button used to display the running/stopped image. */
00104     private JButton runningIndicator;

    /** The x coordinate of the last location where a component was dragged. */
00107     private int previousDragXLocation = 0;

    /** The y coordinate of the last location where a component was dragged. */
00110     private int previousDragYLocation = 0;

    /** The set of currently running hosts. */
00113     private Set hosts = new HashSet();

    /** A message dialog shown while JMeter threads are stopping. */
00116     private JDialog stoppingMessage;

    private JLabel totalThreads;
    private JLabel activeThreads;

    /**
     * Create a new JMeter frame.
     *
     * @param actionHandler
     *            this parameter is not used
     * @param treeModel
     *            the model for the test tree
     * @param treeListener
     *            the listener for the test tree
     */
00131     public MainFrame(ActionListener actionHandler, TreeModel treeModel, JMeterTreeListener treeListener) {
        // TODO: actionHandler isn't used -- remove it from the parameter list
        // this.actionHandler = actionHandler;

        // TODO: Make the running indicator its own class instead of a JButton
        runningIndicator = new JButton(stoppedIcon);
        runningIndicator.setMargin(new Insets(0, 0, 0, 0));
        runningIndicator.setBorder(BorderFactory.createEmptyBorder());

        totalThreads = new JLabel("0"); // $NON-NLS-1$
        activeThreads = new JLabel("0"); // $NON-NLS-1$

        tree = makeTree(treeModel, treeListener);

        GuiPackage.getInstance().setMainFrame(this);
        init();

        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
    }

    /**
     * Default constructor for the JMeter frame. This constructor will not
     * properly initialize the tree, so don't use it.
     *
     * @deprecated Do not use - only needed for JUnit tests
     */
00157     public MainFrame() {
    }

    // MenuBar related methods
    // TODO: Do we really need to have all these menubar methods duplicated
    // here? Perhaps we can make the menu bar accessible through GuiPackage?

    /**
     * Specify whether or not the File|Load menu item should be enabled.
     *
     * @param enabled
     *            true if the menu item should be enabled, false otherwise
     */
00170     public void setFileLoadEnabled(boolean enabled) {
        menuBar.setFileLoadEnabled(enabled);
    }

    /**
     * Specify whether or not the File|Save menu item should be enabled.
     *
     * @param enabled
     *            true if the menu item should be enabled, false otherwise
     */
00180     public void setFileSaveEnabled(boolean enabled) {
        menuBar.setFileSaveEnabled(enabled);
    }

    /**
     * Specify whether or not the File|Revert item should be enabled.
     *
     * @param enabled
     *            true if the menu item should be enabled, false otherwise
     */
00190     public void setFileRevertEnabled(boolean enabled) {
        menuBar.setFileRevertEnabled(enabled);
    }

    /**
     * Specify the project file that was just loaded
     *
     * @param file - the full path to the file that was loaded
     */
00199     public void setProjectFileLoaded(String file) {
        menuBar.setProjectFileLoaded(file);
    }

    /**
     * Set the menu that should be used for the Edit menu.
     *
     * @param menu
     *            the new Edit menu
     */
00209     public void setEditMenu(JPopupMenu menu) {
        menuBar.setEditMenu(menu);
    }

    /**
     * Specify whether or not the Edit menu item should be enabled.
     *
     * @param enabled
     *            true if the menu item should be enabled, false otherwise
     */
00219     public void setEditEnabled(boolean enabled) {
        menuBar.setEditEnabled(enabled);
    }

    /**
     * Set the menu that should be used for the Edit|Add menu.
     *
     * @param menu
     *            the new Edit|Add menu
     */
00229     public void setEditAddMenu(JMenu menu) {
        menuBar.setEditAddMenu(menu);
    }

    /**
     * Specify whether or not the Edit|Add menu item should be enabled.
     *
     * @param enabled
     *            true if the menu item should be enabled, false otherwise
     */
00239     public void setEditAddEnabled(boolean enabled) {
        menuBar.setEditAddEnabled(enabled);
    }

    /**
     * Specify whether or not the Edit|Remove menu item should be enabled.
     *
     * @param enabled
     *            true if the menu item should be enabled, false otherwise
     */
00249     public void setEditRemoveEnabled(boolean enabled) {
        menuBar.setEditRemoveEnabled(enabled);
    }

    /**
     * Close the currently selected menu.
     */
00256     public void closeMenu() {
        if (menuBar.isSelected()) {
            MenuElement[] menuElement = menuBar.getSubElements();
            if (menuElement != null) {
                for (int i = 0; i < menuElement.length; i++) {
                    JMenu menu = (JMenu) menuElement[i];
                    if (menu.isSelected()) {
                        menu.setPopupMenuVisible(false);
                        menu.setSelected(false);
                        break;
                    }
                }
            }
        }
    }

    /**
     * Show a dialog indicating that JMeter threads are stopping on a particular
     * host.
     *
     * @param host
     *            the host where JMeter threads are stopping
     */
00279     public void showStoppingMessage(String host) {
        if (stoppingMessage != null){
            stoppingMessage.dispose();
        }
        stoppingMessage = new JDialog(this, JMeterUtils.getResString("stopping_test_title"), true); //$NON-NLS-1$
        JLabel stopLabel = new JLabel(JMeterUtils.getResString("stopping_test") + ": " + host); //$NON-NLS-1$$NON-NLS-2$
        stopLabel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
        stoppingMessage.getContentPane().add(stopLabel);
        stoppingMessage.pack();
        ComponentUtil.centerComponentInComponent(this, stoppingMessage);
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                if (stoppingMessage != null) {// TODO - how can this be null?
                    stoppingMessage.show();
                }
            }
        });
    }

    public void updateCounts() {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                activeThreads.setText(Integer.toString(JMeterContextService.getNumberOfThreads()));
                totalThreads.setText(Integer.toString(JMeterContextService.getTotalThreads()));
            }
        });
    }

    public void setMainPanel(JComponent comp) {
        mainPanel.setViewportView(comp);
    }

    public JTree getTree() {
        return tree;
    }

    // TestListener implementation

    /**
     * Called when a test is started on the local system. This implementation
     * sets the running indicator and ensures that the menubar is enabled and in
     * the running state.
     */
00322     public void testStarted() {
        testStarted(LOCAL);
        menuBar.setEnabled(true);
    }

    /**
     * Called when a test is started on a specific host. This implementation
     * sets the running indicator and ensures that the menubar is in the running
     * state.
     *
     * @param host
     *            the host where the test is starting
     */
00335     public void testStarted(String host) {
        hosts.add(host);
        runningIndicator.setIcon(runningIcon);
        activeThreads.setText("0"); // $NON-NLS-1$
        totalThreads.setText("0"); // $NON-NLS-1$
        menuBar.setRunning(true, host);
    }

    /**
     * Called when a test is ended on the local system. This implementation
     * disables the menubar, stops the running indicator, and closes the
     * stopping message dialog.
     */
00348     public void testEnded() {
        testEnded(LOCAL);
        menuBar.setEnabled(false);
    }

    /**
     * Called when a test is ended on the remote system. This implementation
     * stops the running indicator and closes the stopping message dialog.
     *
     * @param host
     *            the host where the test is ending
     */
00360     public void testEnded(String host) {
        hosts.remove(host);
        if (hosts.size() == 0) {
            runningIndicator.setIcon(stoppedIcon);
            JMeterContextService.endTest();
        }
        menuBar.setRunning(false, host);
        if (stoppingMessage != null) {
            stoppingMessage.dispose();
            stoppingMessage = null;
        }
    }

    /* Implements TestListener#testIterationStart(LoopIterationEvent) */
00374     public void testIterationStart(LoopIterationEvent event) {
    }

    /**
     * Create the GUI components and layout.
     */
00380     private void init() {
        menuBar = new JMeterMenuBar();
        setJMenuBar(menuBar);

        JPanel all = new JPanel(new BorderLayout());
        all.add(createToolBar(), BorderLayout.NORTH);

        JSplitPane treeAndMain = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);

        treePanel = createTreePanel();
        treeAndMain.setLeftComponent(treePanel);

        mainPanel = createMainPanel();
        treeAndMain.setRightComponent(mainPanel);

        treeAndMain.setResizeWeight(.2);
        treeAndMain.setContinuousLayout(true);
        all.add(treeAndMain, BorderLayout.CENTER);

        getContentPane().add(all);

        tree.setSelectionRow(1);
        addWindowListener(new WindowHappenings());

        setTitle(DEFAULT_TITLE);
        setIconImage(JMeterUtils.getImage("jmeter.jpg").getImage());// $NON-NLS-1$
    }

    public void setExtendedFrameTitle(String fname) {
        // file New operation may set to null, so just return app name
        if (fname == null) {
            setTitle(DEFAULT_TITLE);
            return;
        }

        // allow for windows / chars in filename
        String temp = fname.replace('\\', '/'); // $NON-NLS-1$ // $NON-NLS-2$
        String simpleName = temp.substring(temp.lastIndexOf("/") + 1);// $NON-NLS-1$
        setTitle(simpleName + " (" + fname + ") - " + DEFAULT_TITLE); // $NON-NLS-1$ // $NON-NLS-2$
    }

    /**
     * Create the JMeter tool bar pane containing the running indicator.
     *
     * @return a panel containing the running indicator
     */
00426     private Component createToolBar() {
        Box toolPanel = new Box(BoxLayout.X_AXIS);
        toolPanel.add(Box.createRigidArea(new Dimension(10, 15)));
        toolPanel.add(Box.createGlue());
        toolPanel.add(activeThreads);
        toolPanel.add(new JLabel(" / "));
        toolPanel.add(totalThreads);
        toolPanel.add(Box.createRigidArea(new Dimension(10, 15)));
        toolPanel.add(runningIndicator);
        return toolPanel;
    }

    /**
     * Create the panel where the GUI representation of the test tree is
     * displayed. The tree should already be created before calling this method.
     *
     * @return a scroll pane containing the test tree GUI
     */
00444     private JScrollPane createTreePanel() {
        JScrollPane treeP = new JScrollPane(tree);
        treeP.setMinimumSize(new Dimension(100, 0));
        return treeP;
    }

    /**
     * Create the main panel where components can display their GUIs.
     *
     * @return the main scroll pane
     */
00455     private JScrollPane createMainPanel() {
        return new JScrollPane();
    }

    /**
     * Create and initialize the GUI representation of the test tree.
     *
     * @param treeModel
     *            the test tree model
     * @param treeListener
     *            the test tree listener
     *
     * @return the initialized test tree GUI
     */
00469     private JTree makeTree(TreeModel treeModel, JMeterTreeListener treeListener) {
        JTree treevar = new JTree(treeModel) {
            public String getToolTipText(MouseEvent event) {
                TreePath path = this.getPathForLocation(event.getX(), event.getY());
                if (path != null) {
                    Object treeNode = path.getLastPathComponent();
                    if (treeNode instanceof DefaultMutableTreeNode) {
                        Object testElement = ((DefaultMutableTreeNode) treeNode).getUserObject();
                        if (testElement instanceof TestElement) {
                            String comment = ((TestElement) testElement).getComment();
                            if (comment != null && comment.length() > 0) {
                                return comment;
                                }
                            }
                        }
                    }
                return null;
                }
            };
           treevar.setToolTipText("");
        treevar.setCellRenderer(getCellRenderer());
        treevar.setRootVisible(false);
        treevar.setShowsRootHandles(true);

        treeListener.setJTree(treevar);
        treevar.addTreeSelectionListener(treeListener);
        treevar.addMouseListener(treeListener);
        treevar.addMouseMotionListener(treeListener);
        treevar.addKeyListener(treeListener);

        return treevar;
    }

    /**
     * Create the tree cell renderer used to draw the nodes in the test tree.
     *
     * @return a renderer to draw the test tree nodes
     */
00507     private TreeCellRenderer getCellRenderer() {
        DefaultTreeCellRenderer rend = new JMeterCellRenderer();
        rend.setFont(new Font("Dialog", Font.PLAIN, 11));
        return rend;
    }

    /**
     * Repaint pieces of the GUI as needed while dragging. This method should
     * only be called from the Swing event thread.
     *
     * @param dragIcon
     *            the component being dragged
     * @param x
     *            the current mouse x coordinate
     * @param y
     *            the current mouse y coordinate
     */
00524     public void drawDraggedComponent(Component dragIcon, int x, int y) {
        Dimension size = dragIcon.getPreferredSize();
        treePanel.paintImmediately(previousDragXLocation, previousDragYLocation, size.width, size.height);
        this.getLayeredPane().setLayer(dragIcon, 400);
        SwingUtilities.paintComponent(treePanel.getGraphics(), dragIcon, treePanel, x, y, size.width, size.height);
        previousDragXLocation = x;
        previousDragYLocation = y;
    }

    /**
     * A window adapter used to detect when the main JMeter frame is being
     * closed.
     */
00537     private static class WindowHappenings extends WindowAdapter {
        /**
         * Called when the main JMeter frame is being closed. Sends a
         * notification so that JMeter can react appropriately.
         *
         * @param event
         *            the WindowEvent to handle
         */
00545         public void windowClosing(WindowEvent event) {
            ActionRouter.getInstance().actionPerformed(new ActionEvent(this, event.getID(), ActionNames.EXIT));
        }
    }
}

Generated by  Doxygen 1.6.0   Back to index