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

GuiPackage.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.Component;
import java.awt.event.MouseEvent;
import java.beans.Introspector;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;

import org.apache.jmeter.engine.util.ValueReplacer;
import org.apache.jmeter.exceptions.IllegalUserActionException;
import org.apache.jmeter.gui.tree.JMeterTreeListener;
import org.apache.jmeter.gui.tree.JMeterTreeModel;
import org.apache.jmeter.gui.tree.JMeterTreeNode;
import org.apache.jmeter.services.FileServer;
import org.apache.jmeter.testbeans.TestBean;
import org.apache.jmeter.testbeans.gui.TestBeanGUI;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.TestPlan;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.util.LocaleChangeEvent;
import org.apache.jmeter.util.LocaleChangeListener;
import org.apache.jorphan.collections.HashTree;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;

/**
 * GuiPackage is a static class that provides convenient access to information
 * about the current state of JMeter's GUI. Any GUI class can grab a handle to
 * GuiPackage by calling the static method {@link #getInstance()} and then use
 * it to query the GUI about it's state. When actions, for instance, need to
 * affect the GUI, they typically use GuiPackage to get access to different
 * parts of the GUI.
 *
 */
00058 public final class GuiPackage implements LocaleChangeListener {
    /** Logging. */
00060     private static final Logger log = LoggingManager.getLoggerForClass();

    /** Singleton instance. */
00063     private static GuiPackage guiPack;

    /**
     * Flag indicating whether or not parts of the tree have changed since they
     * were last saved.
     */
00069     private boolean dirty = false;

    /**
     * Map from TestElement to JMeterGUIComponent, mapping the nodes in the tree
     * to their corresponding GUI components.
     */
00075     private Map nodesToGui = new HashMap();

    /**
     * Map from Class to JMeterGUIComponent, mapping the Class of a GUI
     * component to an instance of that component.
     */
00081     private Map guis = new HashMap();

    /**
     * Map from Class to TestBeanGUI, mapping the Class of a TestBean to an
     * instance of TestBeanGUI to be used to edit such components.
     */
00087     private Map testBeanGUIs = new HashMap();

    /** The currently selected node in the tree. */
00090     private JMeterTreeNode currentNode = null;

    private boolean currentNodeUpdated = false;

    /** The model for JMeter's test tree. */
00095     private JMeterTreeModel treeModel;

    /** The listener for JMeter's test tree. */
00098     private JMeterTreeListener treeListener;

    /** The main JMeter frame. */
00101     private MainFrame mainFrame;

    /**
     * Private constructor to permit instantiation only from within this class.
     * Use {@link #getInstance()} to retrieve a singleton instance.
     */
00107     private GuiPackage() {
        JMeterUtils.addLocaleChangeListener(this);
    }

    /**
     * Retrieve the singleton GuiPackage instance.
     *
     * @return the GuiPackage instance
     */
00116     public static GuiPackage getInstance() {
        return guiPack;
    }

    /**
     * When GuiPackage is requested for the first time, it should be given
     * handles to JMeter's Tree Listener and TreeModel.
     *
     * @param listener
     *            the TreeListener for JMeter's test tree
     * @param treeModel
     *            the model for JMeter's test tree
     *
     * @return GuiPackage
     */
00131     public static GuiPackage getInstance(JMeterTreeListener listener, JMeterTreeModel treeModel) {
        if (guiPack == null) {
            guiPack = new GuiPackage();
            guiPack.setTreeListener(listener);
            guiPack.setTreeModel(treeModel);
        }
        return guiPack;
    }

    /**
     * Get a JMeterGUIComponent for the specified test element. If the GUI has
     * already been created, that instance will be returned. Otherwise, if a GUI
     * component of the same type has been created, and the component is not
     * marked as an {@link UnsharedComponent}, that shared component will be
     * returned. Otherwise, a new instance of the component will be created. The
     * TestElement's GUI_CLASS property will be used to determine the
     * appropriate type of GUI component to use.
     *
     * @param node
     *            the test element which this GUI is being created for
     *
     * @return the GUI component corresponding to the specified test element
     */
00154     public JMeterGUIComponent getGui(TestElement node) {
        String testClassName = node.getPropertyAsString(TestElement.TEST_CLASS);
        String guiClassName = node.getPropertyAsString(TestElement.GUI_CLASS);
        try {
            Class testClass;
            if (testClassName.equals("")) { // $NON-NLS-1$
                testClass = node.getClass();
            } else {
                testClass = Class.forName(testClassName);
            }
            Class guiClass = null;
            if (!guiClassName.equals("")) { // $NON-NLS-1$
                guiClass = Class.forName(guiClassName);
            }
            return getGui(node, guiClass, testClass);
        } catch (ClassNotFoundException e) {
            log.error("Could not get GUI for " + node, e);
            return null;
        }
    }

    /**
     * Get a JMeterGUIComponent for the specified test element. If the GUI has
     * already been created, that instance will be returned. Otherwise, if a GUI
     * component of the same type has been created, and the component is not
     * marked as an {@link UnsharedComponent}, that shared component will be
     * returned. Otherwise, a new instance of the component will be created.
     *
     * @param node
     *            the test element which this GUI is being created for
     * @param guiClass
     *            the fully qualifed class name of the GUI component which will
     *            be created if it doesn't already exist
     * @param testClass
     *            the fully qualifed class name of the test elements which have
     *            to be edited by the returned GUI component
     *
     * @return the GUI component corresponding to the specified test element
     */
00193     public JMeterGUIComponent getGui(TestElement node, Class guiClass, Class testClass) {
        try {
            JMeterGUIComponent comp = (JMeterGUIComponent) nodesToGui.get(node);
            if (comp == null) {
                comp = getGuiFromCache(guiClass, testClass);
                nodesToGui.put(node, comp);
            }
            log.debug("Gui retrieved = " + comp);
            return comp;
        } catch (Exception e) {
            log.error("Problem retrieving gui", e);
            return null;
        }
    }

    /**
     * Remove a test element from the tree. This removes the reference to any
     * associated GUI component.
     *
     * @param node
     *            the test element being removed
     */
00215     public void removeNode(TestElement node) {
        nodesToGui.remove(node);
    }

    /**
     * Convenience method for grabbing the gui for the current node.
     *
     * @return the GUI component associated with the currently selected node
     */
00224     public JMeterGUIComponent getCurrentGui() {
        try {
            updateCurrentNode();
            TestElement curNode = treeListener.getCurrentNode().getTestElement();
            JMeterGUIComponent comp = getGui(curNode);
            comp.clearGui();
            log.debug("Updating gui to new node");
            comp.configure(curNode);
            currentNodeUpdated = false;
            return comp;
        } catch (Exception e) {
            log.error("Problem retrieving gui", e);
            return null;
        }
    }

    /**
     * Find the JMeterTreeNode for a certain TestElement object.
     *
     * @param userObject
     *            the test element to search for
     * @return the tree node associated with the test element
     */
00247     public JMeterTreeNode getNodeOf(TestElement userObject) {
        return treeModel.getNodeOf(userObject);
    }

    /**
     * Create a TestElement corresponding to the specified GUI class.
     *
     * @param guiClass
     *            the fully qualified class name of the GUI component or a
     *            TestBean class for TestBeanGUIs.
     * @param testClass
     *            the fully qualified class name of the test elements edited by
     *            this GUI component.
     * @return the test element corresponding to the specified GUI class.
     */
00262     public TestElement createTestElement(Class guiClass, Class testClass) {
        try {
            JMeterGUIComponent comp = getGuiFromCache(guiClass, testClass);
            comp.clearGui();
            TestElement node = comp.createTestElement();
            nodesToGui.put(node, comp);
            return node;
        } catch (Exception e) {
            log.error("Problem retrieving gui", e);
            return null;
        }
    }

    /**
     * Create a TestElement for a GUI or TestBean class.
     * <p>
     * This is a utility method to help actions do with one single String
     * parameter.
     *
     * @param objClass
     *            the fully qualified class name of the GUI component or of the
     *            TestBean subclass for which a TestBeanGUI is wanted.
     * @return the test element corresponding to the specified GUI class.
     */
00286     public TestElement createTestElement(String objClass) {
        JMeterGUIComponent comp;
        Class c;
        try {
            c = Class.forName(objClass);
            if (TestBean.class.isAssignableFrom(c)) {
                comp = getGuiFromCache(TestBeanGUI.class, c);
            } else {
                comp = getGuiFromCache(c, null);
            }
            comp.clearGui();
            TestElement node = comp.createTestElement();
            nodesToGui.put(node, comp);
            return node;
        } catch (NoClassDefFoundError e) {
            log.error("Problem retrieving gui for " + objClass, e);
            String msg="Cannot find class: "+e.getMessage();
            JOptionPane.showMessageDialog(null,
                    msg,
                    "Missing jar? See log file." ,
                    JOptionPane.ERROR_MESSAGE);
            throw new RuntimeException(e.toString()); // Probably a missing
                                                        // jar
        } catch (ClassNotFoundException e) {
            log.error("Problem retrieving gui for " + objClass, e);
            throw new RuntimeException(e.toString()); // Programming error:
                                                        // bail out.
        } catch (InstantiationException e) {
            log.error("Problem retrieving gui for " + objClass, e);
            throw new RuntimeException(e.toString()); // Programming error:
                                                        // bail out.
        } catch (IllegalAccessException e) {
            log.error("Problem retrieving gui for " + objClass, e);
            throw new RuntimeException(e.toString()); // Programming error:
                                                        // bail out.
        }
    }

    /**
     * Get an instance of the specified JMeterGUIComponent class. If an instance
     * of the GUI class has previously been created and it is not marked as an
     * {@link UnsharedComponent}, that shared instance will be returned.
     * Otherwise, a new instance of the component will be created, and shared
     * components will be cached for future retrieval.
     *
     * @param guiClass
     *            the fully qualified class name of the GUI component. This
     *            class must implement JMeterGUIComponent.
     * @param testClass
     *            the fully qualified class name of the test elements edited by
     *            this GUI component. This class must implement TestElement.
     * @return an instance of the specified class
     *
     * @throws InstantiationException
     *             if an instance of the object cannot be created
     * @throws IllegalAccessException
     *             if access rights do not allow the default constructor to be
     *             called
     * @throws ClassNotFoundException
     *             if the specified GUI class cannot be found
     */
00347     private JMeterGUIComponent getGuiFromCache(Class guiClass, Class testClass) throws InstantiationException,
            IllegalAccessException {
        JMeterGUIComponent comp;
        if (guiClass == TestBeanGUI.class) {
            comp = (TestBeanGUI) testBeanGUIs.get(testClass);
            if (comp == null) {
                comp = new TestBeanGUI(testClass);
                testBeanGUIs.put(testClass, comp);
            }
        } else {
            comp = (JMeterGUIComponent) guis.get(guiClass);
            if (comp == null) {
                comp = (JMeterGUIComponent) guiClass.newInstance();
                if (!(comp instanceof UnsharedComponent)) {
                    guis.put(guiClass, comp);
                }
            }
        }
        return comp;
    }

    /**
     * Update the GUI for the currently selected node. The GUI component is
     * configured to reflect the settings in the current tree node.
     *
     */
00373     public void updateCurrentGui() {
        updateCurrentNode();
        currentNode = treeListener.getCurrentNode();
        TestElement element = currentNode.getTestElement();
        JMeterGUIComponent comp = getGui(element);
        comp.configure(element);
        currentNodeUpdated = false;
    }

    /**
     * This method should be called in order for GuiPackage to change the
     * current node. This will save any changes made to the earlier node before
     * choosing the new node.
     */
00387     public void updateCurrentNode() {
        try {
            if (currentNode != null && !currentNodeUpdated) {
                log.debug("Updating current node " + currentNode.getName());
                JMeterGUIComponent comp = getGui(currentNode.getTestElement());
                TestElement el = currentNode.getTestElement();
                comp.modifyTestElement(el);
            }
            // The current node is now updated
            currentNodeUpdated = true;
            currentNode = treeListener.getCurrentNode();
        } catch (Exception e) {
            log.error("Problem retrieving gui", e);
        }
    }

    public JMeterTreeNode getCurrentNode() {
        return treeListener.getCurrentNode();
    }

    public TestElement getCurrentElement() {
        return getCurrentNode().getTestElement();
    }

    /**
     * The dirty property is a flag that indicates whether there are parts of
     * JMeter's test tree that the user has not saved since last modification.
     * Various (@link Command actions) set this property when components are
     * modified/created/saved.
     *
     * @param dirty
     *            the new value of the dirty flag
     */
00420     public void setDirty(boolean dirty) {
        this.dirty = dirty;
    }

    /**
     * Retrieves the state of the 'dirty' property, a flag that indicates if
     * there are test tree components that have been modified since they were
     * last saved.
     *
     * @return true if some tree components have been modified since they were
     *         last saved, false otherwise
     */
00432     public boolean isDirty() {
        return dirty;
    }

    /**
     * Add a subtree to the currently selected node.
     *
     * @param subTree
     *            the subtree to add.
     *
     * @return the resulting subtree starting with the currently selected node
     *
     * @throws IllegalUserActionException
     *             if a subtree cannot be added to the currently selected node
     */
00447     public HashTree addSubTree(HashTree subTree) throws IllegalUserActionException {
        return treeModel.addSubTree(subTree, treeListener.getCurrentNode());
    }

    /**
     * Get the currently selected subtree.
     *
     * @return the subtree of the currently selected node
     */
00456     public HashTree getCurrentSubTree() {
        return treeModel.getCurrentSubTree(treeListener.getCurrentNode());
    }

    /**
     * Get the model for JMeter's test tree.
     *
     * @return the JMeter tree model
     */
    /*
     * TODO consider removing this method, and providing method wrappers instead.
     * This would allow the Gui package to do any additional clearups if required,
     * as has been done with clearTestPlan()
    */
00470     public JMeterTreeModel getTreeModel() {
        return treeModel;
    }

    /**
     * Set the model for JMeter's test tree.
     *
     * @param newTreeModel
     *            the new JMeter tree model
     */
00480     public void setTreeModel(JMeterTreeModel newTreeModel) {
        treeModel = newTreeModel;
    }

    /**
     * Get a ValueReplacer for the test tree.
     *
     * @return a ValueReplacer configured for the test tree
     */
00489     public ValueReplacer getReplacer() {
        return new ValueReplacer((TestPlan) ((JMeterTreeNode) getTreeModel().getTestPlan().getArray()[0])
                .getTestElement());
    }

    /**
     * Set the main JMeter frame.
     *
     * @param newMainFrame
     *            the new JMeter main frame
     */
00500     public void setMainFrame(MainFrame newMainFrame) {
        mainFrame = newMainFrame;
    }

    /**
     * Get the main JMeter frame.
     *
     * @return the main JMeter frame
     */
00509     public MainFrame getMainFrame() {
        return mainFrame;
    }

    /**
     * Set the listener for JMeter's test tree.
     *
     * @param newTreeListener
     *            the new JMeter test tree listener
     */
00519     public void setTreeListener(JMeterTreeListener newTreeListener) {
        treeListener = newTreeListener;
    }

    /**
     * Get the listener for JMeter's test tree.
     *
     * @return the JMeter test tree listener
     */
00528     public JMeterTreeListener getTreeListener() {
        return treeListener;
    }

    /**
     * Display the specified popup menu with the source component and location
     * from the specified mouse event.
     *
     * @param e
     *            the mouse event causing this popup to be displayed
     * @param popup
     *            the popup menu to display
     */
00541     public void displayPopUp(MouseEvent e, JPopupMenu popup) {
        displayPopUp((Component) e.getSource(), e, popup);
    }

    /**
     * Display the specified popup menu at the location specified by a mouse
     * event with the specified source component.
     *
     * @param invoker
     *            the source component
     * @param e
     *            the mouse event causing this popup to be displayed
     * @param popup
     *            the popup menu to display
     */
00556     public void displayPopUp(Component invoker, MouseEvent e, JPopupMenu popup) {
        if (popup != null) {
            log.debug("Showing pop up for " + invoker + " at x,y = " + e.getX() + "," + e.getY());

            popup.pack();
            popup.show(invoker, e.getX(), e.getY());
            popup.setVisible(true);
            popup.requestFocus();
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see org.apache.jmeter.util.LocaleChangeListener#localeChanged(org.apache.jmeter.util.LocaleChangeEvent)
     */
    public void localeChanged(LocaleChangeEvent event) {
        // FIrst make sure we save the content of the current GUI (since we
        // will flush it away):
        updateCurrentNode();

        // Forget about all GUIs we've created so far: we'll need to re-created
        // them all!
        guis = new HashMap();
        nodesToGui = new HashMap();
        testBeanGUIs = new HashMap();

        // BeanInfo objects also contain locale-sensitive data -- flush them
        // away:
        Introspector.flushCaches();

        // Now put the current GUI in place. [This code was copied from the
        // EditCommand action -- we can't just trigger the action because that
        // would populate the current node with the contents of the new GUI --
        // which is empty.]
        MainFrame mf = getMainFrame(); // Fetch once
        if (mf == null) // Probably caused by unit testing on headless system
        {
            log.warn("Mainframe is null");
        } else {
            mf.setMainPanel((javax.swing.JComponent) getCurrentGui());
            mf.setEditMenu(getTreeListener().getCurrentNode().createPopupMenu());
        }
    }

    private String testPlanFile;

    /**
     * Sets the filepath of the current test plan. It's shown in the main frame
     * title and used on saving.
     *
     * @param f
     */
00609     public void setTestPlanFile(String f) {
        testPlanFile = f;
        getMainFrame().setExtendedFrameTitle(testPlanFile);
        // Enable file revert action if a file is used
        getMainFrame().setFileRevertEnabled(f != null);
        getMainFrame().setProjectFileLoaded(f);

        try {
            FileServer.getFileServer().setBasedir(testPlanFile);
        } catch (IOException e1) {
            log.error("Failure setting file server's base dir", e1);
        }
    }

    public String getTestPlanFile() {
        return testPlanFile;
    }

    /**
     * Clears the test plan and associated objects.
     * Clears the test plan file name.
     */
00631     public void clearTestPlan() {
        getTreeModel().clearTestPlan();
        nodesToGui.clear();
        setTestPlanFile(null);
    }

    /**
     * Clears the test plan element and associated object
     *
     * @param element to clear
     */
00642     public void clearTestPlan(TestElement element) {
        getTreeModel().clearTestPlan(element);
        removeNode(element);
    }

    public static void showErrorMessage(final String message, final String title){
        showMessage(message,title,JOptionPane.ERROR_MESSAGE);
    }

    public static void showInfoMessage(final String message, final String title){
        showMessage(message,title,JOptionPane.INFORMATION_MESSAGE);
    }

    public static void showWarningMessage(final String message, final String title){
        showMessage(message,title,JOptionPane.WARNING_MESSAGE);
    }

    public static void showMessage(final String message, final String title, final int type){
        if (guiPack == null) {
            return ;
        }
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JOptionPane.showMessageDialog(null,message,title,type);
            }
        });

    }
}

Generated by  Doxygen 1.6.0   Back to index