alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

JMeter example source code file (GenericTestBeanCustomizer.java)

This example JMeter source code file (GenericTestBeanCustomizer.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - JMeter tags/keywords

awt, bean, combostringeditor, gridbagconstraints, gui, integer, integer, javabean, jlabel, non-nls-1, non-nls-1, object, object, propertydescriptor, propertyeditor, string, string, swing, util, wrappereditor

The JMeter GenericTestBeanCustomizer.java source code

/*
 * 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.testbeans.gui;

import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.beans.BeanInfo;
import java.beans.PropertyDescriptor;
import java.beans.PropertyEditor;
import java.beans.PropertyEditorManager;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;

/**
 * The GenericTestBeanCustomizer is designed to provide developers with a
 * mechanism to quickly implement GUIs for new components.
 * <p>
 * It allows editing each of the public exposed properties of the edited type 'a
 * la JavaBeans': as far as the types of those properties have an associated
 * editor, there's no GUI development required.
 * <p>
 * This class understands the following PropertyDescriptor attributes:
 * <dl>
 * <dt>group: String
 * <dd>Group under which the property should be shown in the GUI. The string is
 * also used as a group title (but see comment on resourceBundle below). The
 * default group is "".</dd>
 * <dt>order: Integer
 * <dd>Order in which the property will be shown in its group. A smaller
 * integer means higher up in the GUI. The default order is 0. Properties of
 * equal order are sorted alphabetically.</dd>
 * <dt>tags: String[]
 * <dd>List of values to be offered for the property in addition to those
 * offered by its property editor.</dd>
 * <dt>notUndefined: Boolean
 * <dd>If true, the property should not be left undefined. A default
 * attribute must be provided if this is set.</dd>
 * <dd>notExpression: Boolean
 * <dd>If true, the property content should always be constant: JMeter
 * 'expressions' (strings using ${var}, etc...) can't be used.</dt>
 * <dd>notOther: Boolean
 * <dd>If true, the property content must always be one of the tags values or
 * null.</dt>
 * <dt>default: Object
 * <dd>Initial value for the property's GUI. Must be provided and be non-null
 * if <b>notUndefined is set. Must be one of the provided tags (or null) if
 * <b>notOther is set.
 * </dl>
 * <p>
 * The following BeanDescriptor attributes are also understood:
 * <dl>
 * <dt>group.group.order: Integer
 * <dd>where group is a group name used in a group
 * attribute in one or more PropertyDescriptors. Defines the order in which the
 * group will be shown in the GUI. A smaller integer means higher up in the GUI.
 * The default order is 0. Groups of equal order are sorted alphabetically.</dd>
 * <dt>resourceBundle: ResourceBundle
 * <dd>A resource bundle to be used for GUI localization: group display names
 * will be obtained from property "<b>group.displayName" if
 * available (where <b>group is the group name).
 * </dl>
 */
public class GenericTestBeanCustomizer extends JPanel implements SharedCustomizer {
    private static final long serialVersionUID = 240L;

    private static final Logger log = LoggingManager.getLoggerForClass();

    public static final String GROUP = "group"; //$NON-NLS-1$

    public static final String ORDER = "order"; //$NON-NLS-1$

    public static final String TAGS = "tags"; //$NON-NLS-1$

    public static final String NOT_UNDEFINED = "notUndefined"; //$NON-NLS-1$

    public static final String NOT_EXPRESSION = "notExpression"; //$NON-NLS-1$

    public static final String NOT_OTHER = "notOther"; //$NON-NLS-1$

    public static final String DEFAULT = "default"; //$NON-NLS-1$

    public static final String RESOURCE_BUNDLE = "resourceBundle"; //$NON-NLS-1$

    public static final String ORDER(String group) {
        return "group." + group + ".order";
    }

    public static final String DEFAULT_GROUP = "";

    private int scrollerCount = 0;

    /**
     * BeanInfo object for the class of the objects being edited.
     */
    private transient BeanInfo beanInfo;

    /**
     * Property descriptors from the beanInfo.
     */
    private transient PropertyDescriptor[] descriptors;

    /**
     * Property editors -- or null if the property can't be edited. Unused if
     * customizerClass==null.
     */
    private transient PropertyEditor[] editors;

    /**
     * Message format for property field labels:
     */
    private MessageFormat propertyFieldLabelMessage;

    /**
     * Message format for property tooltips:
     */
    private MessageFormat propertyToolTipMessage;

    /**
     * The Map we're currently customizing. Set by setObject().
     */
    private Map<String, Object> propertyMap;

    /**
     * @deprecated only for use by test code
     */
    @Deprecated
    public GenericTestBeanCustomizer(){
        log.warn("Constructor only intended for use in testing"); // $NON-NLS-1$
    }
    /**
     * Create a customizer for a given test bean type.
     *
     * @param testBeanClass
     *            a subclass of TestBean
     * @see org.apache.jmeter.testbeans.TestBean
     */
    GenericTestBeanCustomizer(BeanInfo beanInfo) {
        super();

        this.beanInfo = beanInfo;

        // Get and sort the property descriptors:
        descriptors = beanInfo.getPropertyDescriptors();
        Arrays.sort(descriptors, new PropertyComparator());

        // Obtain the propertyEditors:
        editors = new PropertyEditor[descriptors.length];
        for (int i = 0; i < descriptors.length; i++) {
            String name = descriptors[i].getName();

            // Don't get editors for hidden or non-read-write properties:
            if (descriptors[i].isHidden() || (descriptors[i].isExpert() && !JMeterUtils.isExpertMode())
                    || descriptors[i].getReadMethod() == null || descriptors[i].getWriteMethod() == null) {
                log.debug("No editor for property " + name);
                editors[i] = null;
                continue;
            }

            PropertyEditor propertyEditor;
            Class<?> editorClass = descriptors[i].getPropertyEditorClass();

            if (log.isDebugEnabled()) {
                log.debug("Property " + name + " has editor class " + editorClass);
            }

            if (editorClass != null) {
                try {
                    propertyEditor = (PropertyEditor) editorClass.newInstance();
                } catch (InstantiationException e) {
                    log.error("Can't create property editor.", e);
                    throw new Error(e.toString());
                } catch (IllegalAccessException e) {
                    log.error("Can't create property editor.", e);
                    throw new Error(e.toString());
                }
            } else {
                Class<?> c = descriptors[i].getPropertyType();
                propertyEditor = PropertyEditorManager.findEditor(c);
            }

            if (log.isDebugEnabled()) {
                log.debug("Property " + name + " has property editor " + propertyEditor);
            }

            if (propertyEditor == null) {
                log.debug("No editor for property " + name);
                editors[i] = null;
                continue;
            }

            if (!propertyEditor.supportsCustomEditor()) {
                propertyEditor = createWrapperEditor(propertyEditor, descriptors[i]);

                if (log.isDebugEnabled()) {
                    log.debug("Editor for property " + name + " is wrapped in " + propertyEditor);
                }
            }
            if(propertyEditor instanceof TestBeanPropertyEditor)
            {
                ((TestBeanPropertyEditor)propertyEditor).setDescriptor(descriptors[i]);
            }
            if (propertyEditor.getCustomEditor() instanceof JScrollPane) {
                scrollerCount++;
            }

            editors[i] = propertyEditor;

            // Initialize the editor with the provided default value or null:
            setEditorValue(i, descriptors[i].getValue(DEFAULT));

        }

        // Obtain message formats:
        propertyFieldLabelMessage = new MessageFormat(JMeterUtils.getResString("property_as_field_label")); //$NON-NLS-1$
        propertyToolTipMessage = new MessageFormat(JMeterUtils.getResString("property_tool_tip")); //$NON-NLS-1$

        // Initialize the GUI:
        init();
    }

    /**
     * Find the default typeEditor and a suitable guiEditor for the given
     * property descriptor, and combine them in a WrapperEditor.
     *
     * @param typeEditor
     * @param descriptor
     * @return
     */
    private WrapperEditor createWrapperEditor(PropertyEditor typeEditor, PropertyDescriptor descriptor) {
        String[] editorTags = typeEditor.getTags();
        String[] additionalTags = (String[]) descriptor.getValue(TAGS);
        String[] tags = null;
        if (editorTags == null) {
            tags = additionalTags;
        } else if (additionalTags == null) {
            tags = editorTags;
        } else {
            tags = new String[editorTags.length + additionalTags.length];
            int j = 0;
            for (int i = 0; i < editorTags.length; i++) {
                tags[j++] = editorTags[i];
            }
            for (int i = 0; i < additionalTags.length; i++) {
                tags[j++] = additionalTags[i];
            }
        }

        boolean notNull = Boolean.TRUE.equals(descriptor.getValue(NOT_UNDEFINED));
        boolean notExpression = Boolean.TRUE.equals(descriptor.getValue(NOT_EXPRESSION));
        boolean notOther = Boolean.TRUE.equals(descriptor.getValue(NOT_OTHER));

        PropertyEditor guiEditor;
        if (notNull && tags == null) {
            guiEditor = new FieldStringEditor();
        } else {
            ComboStringEditor e = new ComboStringEditor();
            e.setNoUndefined(notNull);
            e.setNoEdit(notExpression && notOther);
            e.setTags(tags);

            guiEditor = e;
        }

        WrapperEditor wrapper = new WrapperEditor(typeEditor, guiEditor, !notNull, // acceptsNull
                !notExpression, // acceptsExpressions
                !notOther, // acceptsOther
                descriptor.getValue(DEFAULT));

        return wrapper;
    }

    /**
     * Set the value of the i-th property, properly reporting a possible
     * failure.
     *
     * @param i
     *            the index of the property in the descriptors and editors
     *            arrays
     * @param value
     *            the value to be stored in the editor
     *
     * @throws IllegalArgumentException
     *             if the editor refuses the value
     */
    private void setEditorValue(int i, Object value) throws IllegalArgumentException {
        editors[i].setValue(value);
    }


    /**
     * {@inheritDoc}
     * @param map must be an instance of Map<String, Object>
     */
    @SuppressWarnings("unchecked")
    public void setObject(Object map) {
        propertyMap = (Map<String, Object>) map;

        if (propertyMap.size() == 0) {
            // Uninitialized -- set it to the defaults:
            for (int i = 0; i < editors.length; i++) {
                Object value = descriptors[i].getValue(DEFAULT);
                String name = descriptors[i].getName();
                if (value != null) {
                    propertyMap.put(name, value);
                    log.debug("Set " + name + "= " + value);
                }
                firePropertyChange(name, null, value);
            }
        }

        // Now set the editors to the element's values:
        for (int i = 0; i < editors.length; i++) {
            if (editors[i] == null) {
                continue;
            }
            try {
                setEditorValue(i, propertyMap.get(descriptors[i].getName()));
            } catch (IllegalArgumentException e) {
                // I guess this can happen as a result of a bad
                // file read? In this case, it would be better to replace the
                // incorrect value with anything valid, e.g. the default value
                // for the property.
                // But for the time being, I just prefer to be aware of any
                // problems occuring here, most likely programming errors,
                // so I'll bail out.
                // (MS Note) Can't bail out - newly create elements have blank
                // values and must get the defaults.
                // Also, when loading previous versions of jmeter test scripts,
                // some values
                // may not be right, and should get default values - MS
                // TODO: review this and possibly change to:
                setEditorValue(i, descriptors[i].getValue(DEFAULT));
            }
        }
    }

//  /**
//   * Find the index of the property of the given name.
//   *
//   * @param name
//   *            the name of the property
//   * @return the index of that property in the descriptors array, or -1 if
//   *         there's no property of this name.
//   */
//  private int descriptorIndex(String name) // NOTUSED
//  {
//      for (int i = 0; i < descriptors.length; i++) {
//          if (descriptors[i].getName().equals(name)) {
//              return i;
//          }
//      }
//      return -1;
//  }

    /**
     * Initialize the GUI.
     */
    private void init() {
        setLayout(new GridBagLayout());

        GridBagConstraints cl = new GridBagConstraints(); // for labels
        cl.gridx = 0;
        cl.anchor = GridBagConstraints.EAST;
        cl.insets = new Insets(0, 1, 0, 1);

        GridBagConstraints ce = new GridBagConstraints(); // for editors
        ce.fill = GridBagConstraints.BOTH;
        ce.gridx = 1;
        ce.weightx = 1.0;
        ce.insets = new Insets(0, 1, 0, 1);

        GridBagConstraints cp = new GridBagConstraints(); // for panels
        cp.fill = GridBagConstraints.BOTH;
        cp.gridx = 1;
        cp.gridy = GridBagConstraints.RELATIVE;
        cp.gridwidth = 2;
        cp.weightx = 1.0;

        JPanel currentPanel = this;
        String currentGroup = DEFAULT_GROUP;
        int y = 0;

        for (int i = 0; i < editors.length; i++) {
            if (editors[i] == null) {
                continue;
            }

            if (log.isDebugEnabled()) {
                log.debug("Laying property " + descriptors[i].getName());
            }

            String g = group(descriptors[i]);
            if (!currentGroup.equals(g)) {
                if (currentPanel != this) {
                    add(currentPanel, cp);
                }
                currentGroup = g;
                currentPanel = new JPanel(new GridBagLayout());
                currentPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(),
                        groupDisplayName(g)));
                cp.weighty = 0.0;
                y = 0;
            }

            Component customEditor = editors[i].getCustomEditor();

            boolean multiLineEditor = false;
            if (customEditor.getPreferredSize().height > 50 || customEditor instanceof JScrollPane) {
                // TODO: the above works in the current situation, but it's
                // just a hack. How to get each editor to report whether it
                // wants to grow bigger? Whether the property label should
                // be at the left or at the top of the editor? ...?
                multiLineEditor = true;
            }

            JLabel label = createLabel(descriptors[i]);
            label.setLabelFor(customEditor);

            cl.gridy = y;
            cl.gridwidth = multiLineEditor ? 2 : 1;
            cl.anchor = multiLineEditor ? GridBagConstraints.CENTER : GridBagConstraints.EAST;
            currentPanel.add(label, cl);

            ce.gridx = multiLineEditor ? 0 : 1;
            ce.gridy = multiLineEditor ? ++y : y;
            ce.gridwidth = multiLineEditor ? 2 : 1;
            ce.weighty = multiLineEditor ? 1.0 : 0.0;

            cp.weighty += ce.weighty;

            currentPanel.add(customEditor, ce);

            y++;
        }
        if (currentPanel != this) {
            add(currentPanel, cp);
        }

        // Add a 0-sized invisible component that will take all the vertical
        // space that nobody wants:
        cp.weighty = 0.0001;
        add(Box.createHorizontalStrut(0), cp);
    }

    private JLabel createLabel(PropertyDescriptor desc) {
        String text = desc.getDisplayName();
        if (!"".equals(text)) {
            text = propertyFieldLabelMessage.format(new Object[] { desc.getDisplayName() });
        }
        // if the displayName is the empty string, leave it like that.
        JLabel label = new JLabel(text);
        label.setHorizontalAlignment(JLabel.TRAILING);
        text = propertyToolTipMessage.format(new Object[] { desc.getName(), desc.getShortDescription() });
        label.setToolTipText(text);

        return label;
    }

    /**
     * Obtain a property descriptor's group.
     *
     * @param descriptor
     * @return the group String.
     */
    private String group(PropertyDescriptor d) {
        String group = (String) d.getValue(GROUP);
        if (group == null){
            group = DEFAULT_GROUP;
        }
        return group;
    }

    /**
     * Obtain a group's display name
     */
    private String groupDisplayName(String group) {
        try {
            ResourceBundle b = (ResourceBundle) beanInfo.getBeanDescriptor().getValue(RESOURCE_BUNDLE);
            if (b == null) {
                return group;
            }
            return b.getString(group + ".displayName");
        } catch (MissingResourceException e) {
            return group;
        }
    }

    /**
     * Comparator used to sort properties for presentation in the GUI.
     */
    private class PropertyComparator implements Comparator<PropertyDescriptor>, Serializable {
        private static final long serialVersionUID = 240L;

        public int compare(PropertyDescriptor d1, PropertyDescriptor d2) {
            int result;

            String g1 = group(d1), g2 = group(d2);
            Integer go1 = groupOrder(g1), go2 = groupOrder(g2);

            result = go1.compareTo(go2);
            if (result != 0) {
                return result;
            }

            result = g1.compareTo(g2);
            if (result != 0) {
                return result;
            }

            Integer po1 = propertyOrder(d1), po2 = propertyOrder(d2);
            result = po1.compareTo(po2);
            if (result != 0) {
                return result;
            }

            return d1.getName().compareTo(d2.getName());
        }

        /**
         * Obtain a group's order.
         *
         * @param group
         *            group name
         * @return the group's order (zero by default)
         */
        private Integer groupOrder(String group) {
            Integer order = (Integer) beanInfo.getBeanDescriptor().getValue(ORDER(group));
            if (order == null) {
                order = new Integer(0);
            }
            return order;
        }

        /**
         * Obtain a property's order.
         *
         * @param d
         * @return the property's order attribute (zero by default)
         */
        private Integer propertyOrder(PropertyDescriptor d) {
            Integer order = (Integer) d.getValue(ORDER);
            if (order == null) {
                order = new Integer(0);
            }
            return order;
        }
    }

    /**
     * Save values from the GUI fields into the property map
     */
    void saveGuiFields() {
        for (int i = 0; i < editors.length; i++) {
            PropertyEditor propertyEditor=editors[i]; // might be null (e.g. in testing)
            if (propertyEditor != null) {
                Object value = propertyEditor.getValue();
                String name = descriptors[i].getName();
                if (value == null) {
                    propertyMap.remove(name);
                    if (log.isDebugEnabled()) {
                        log.debug("Unset " + name);
                    }
                } else {
                    propertyMap.put(name, value);
                    if (log.isDebugEnabled()) {
                        log.debug("Set " + name + "= " + value);
                    }
                }
            }
        }
    }

    void clearGuiFields() {
        for (int i = 0; i < editors.length; i++) {
            PropertyEditor propertyEditor=editors[i]; // might be null (e.g. in testing)
            if (propertyEditor != null) {
                try {
                if (propertyEditor instanceof WrapperEditor){
                    WrapperEditor we = (WrapperEditor) propertyEditor;
                    String tags[]=we.getTags();
                    if (tags != null && tags.length > 0) {
                        we.setAsText(tags[0]);
                    } else {
                        we.setValue("");
                    }
                } else if (propertyEditor instanceof ComboStringEditor) {
                    ComboStringEditor cse = (ComboStringEditor) propertyEditor;
                    cse.setAsText(cse.getInitialEditValue());
                } else {
                    propertyEditor.setAsText("");
                }
                } catch (IllegalArgumentException ex){
                    log.error("Failed to set field "+descriptors[i].getName(),ex);
                }
            }
        }
    }

}

Other JMeter examples (source code examples)

Here is a short list of links related to this JMeter GenericTestBeanCustomizer.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.