#   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.

 Making a TestBean Plugin For JMeter

This component will be a CSV file reading element that will let users easily vary their input
data using csv files.

1. Pick a package and make three files:
    - [ComponentName].java (org.apache.jmeter.config.CSVDataSet.java)
    - [ComponentName]BeanInfo.java (org.apache.jmeter.config.CSVDataSetBeanInfo.java)
    - [ComponentName]Resources.properties (org.apache.jmeter.config.CSVDataSetResources.properties)

2. CSVDataSet.java must implement the TestBean interface.  In addition, it will extend
ConfigTestElement, and implement LoopIterationListener.
    - TestBean is a marker interface, so there are no methods to implement.
    - Extending ConfigTestElement will make our component a Config element in a test plan.  By
       extending different abstract classes, you can control the type of element your component will
       be (ie AbstractSampler, AbstractVisualizer, GenericController, etc - though you can also make
       different types of elements just by instantiating the right interfaces, the abstract classes can
       make your life easier).

3. CSVDataSetBeanInfo.java should extend org.apache.jmeter.testbeans.BeanInfoSupport
    - create a zero-parameter constructor in which we call super(CSVDataSet.class);
    - we'll come back to this.

4. CSVDataSetResources.properties - blank for now

5. Implement your special logic for you plugin class.
    - The CSVDataSet will read a single CSV file and will store the values it finds into
    JMeter's running context.  The user will define the file, define the variable names for
    each "column".  The CSVDataSet will open the file when the test starts, and close it
    when the test ends (thus we implement TestListener).  The CSVDataSet will update the
    contents of the variables for every test thread, and for each iteration through its
    parent controller, by reading new lines in the file.  When we reach the end of the file,
    we'll start again at the beginning.

    - When implementing a TestBean, pay careful attention to your properties.  These
    properties will become the basis of a gui form by which users will configure the CSVDataSet
    element.

    - Your element will be cloned by JMeter when the test starts.  Each thread will get it's own instance.  However, you will
    have a chance to control how the cloning is done - we'll be taking advantage of this for CSVDataSet (since we don't want to open the file X number of times from X number of threads).

    a. Properties: filename, variableNames.  With public getters and setters.
        - filename is self-explanatory, it will hold the name of the CSV file we'll read
        - variableNames is a String which will allow a user to enter the names of
        the variables we'll assign values to.  Why a String?  Why not a Collection - surely
        users will need to enter multiple (and unknown number) variable names?  True, but
        if we used a List or Collection, we'd have to write a gui component to handle
        collections, and I just want to do this quickly.  Instead, we'll let users input
        comma-delimited list of variable names.

    b. I then implemented the IterationStart method of the LoopIterationListener interface.  The point
       of this "event" is that your component is notified of when the test has entered it's parent
       controller.  For our purposes, every time the CSVDataSet's parent controller is entered, we will
       read a new line of the data file and set the variables.  Thus, for a regular controller, each
       loop through the test will result in a new set of values being read. For a loop controller, each
       iteration will do likewise.  Every test thread will get different values as well.

6. Setting up your gui elements in CSVDataSetBeanInfo:
    - You can create groupings for your component's properties.  Each grouping you create needs
       a label and a list of property names to include in that grouping.  Ie:

            createPropertyGroup("csv_data",new String[]{"filename","variableNames"});

       Creates a grouping called "csv_data" that will include gui input elements for the
       "filename" and "variableNames" properties of CSVDataSet.  Then, we need to define what kind of
       properties we want these to be:

               p = property("filename");
            p.setValue(NOT_UNDEFINED, Boolean.TRUE);
            p.setValue(DEFAULT, "");
            p.setValue(NOT_EXPRESSION,Boolean.TRUE);
            p = property("variableNames");
            p.setValue(NOT_UNDEFINED, Boolean.TRUE);
            p.setValue(DEFAULT, "");
            p.setValue(NOT_EXPRESSION,Boolean.TRUE);

       This essentially creates two properties whose value is not allowed to be null, and whose default
       values are "".  There are several such attributes that can be set for each property.  Here is a
       rundown:

               NOT_UNDEFINED  : The property will not be left null.
               DEFAULT        : A default values must be given if NOT_UNDEFINED is true.
               NOT_EXPRESSION : The value will not be parsed for functions if this is true.
               NOT_OTHER      : This is not a free form entry field - a list of values has to be provided.
               TAGS           : with a String[] as the value, this sets up a predefined list of acceptable values, and JMeter will create a dropdown select.

       Additionally, a custom property editor can be specified for a property:

               p.setPropertyEditorClass(FileEditor.class);

       This will create a text input plus browse button that opens a dialog for finding a file.

       Usually, complex property settings are not needed, as now.  For a more complex example, look
       at org.apache.jmeter.protocol.http.sampler.AccessLogSamplerBeanInfo

7. Defining your resource strings.  In CSVDataSetResources.properties we have to define all our string
   resources.  To provide translations, one would create additional files such as CSVDataSetResources_ja.properties, and
   CSVDataSetResources_de.properties.  For our component, we must define the following resources:

           displayName - This will provide a name for the element that will appear in menus.
           csv_data.displayName - we create a property grouping called "csv_data", so we have to provide a label for the grouping
           filename.displayName - a label for the filename input element.
           filename.shortDescription - a tool-tip-like help text blurb.
           variableNames.displayName - a label for the variable name input element.
           variableNames.shortDescription - tool tip for the variableNames input element.

8. Debug your component.