mstover1    01/03/24 06:39:59

  Modified:    docs     index.html
               docs/extending index.html
               docs/user_manual index.html
               src/org/apache/jmeter TextDriver.java
               src/org/apache/jmeter/control GenericController.java
                        SamplerController.java
               src/org/apache/jmeter/protocol/http/parser HtmlParser.java
               src/org/apache/jmeter/save/handlers
                        AbstractConfigElementHandler.java
                        JMeterHandler.java
               xdocs/extending index.xml
               xdocs/user_manual index.xml
  Added:       docs/user_manual rmi.html
  Log:
  
  
  Revision  Changes    Path
  1.23      +31 -20    jakarta-jmeter/docs/index.html
  
  Index: index.html
  ===================================================================
  RCS file: /home/cvs/jakarta-jmeter/docs/index.html,v
  retrieving revision 1.22
  retrieving revision 1.23
  diff -u -r1.22 -r1.23
  --- index.html        2001/03/17 22:25:39     1.22
  +++ index.html        2001/03/24 14:39:57     1.23
  @@ -77,32 +77,43 @@
         <tr><td>
           <blockquote>
                                       <p>
  -<b>Apache JMeter</b> is a 100% pure Java desktop application designed to load test 
functional
  -behavior and measure performance.  It was originally designed for testing Web 
Applications but has
  -since expanded to other test functions.
  -</p>
  +       <b>Apache JMeter</b> is a 100% pure Java desktop application designed 
  +       to load test functional behavior and measure performance.  It was 
  +       originally designed for testing Web Applications but has
  +       since expanded to other test functions.
  +     </p>
                                                   <h2>What can I do with it?</h2>
  -                                                <p>
  -Apache JMeter may be used to test performance both on static and dynamic
  -  resources (files or CGI, Servlets, Perl scripts, Java Objects). It may well be 
used to simulate a heavy
  -      load on a server, network or object to test its strength or to analyse 
overall performance under
  -      different load types. You can use it to make a graphical analysis of 
performance or to
  -  test your server/script/object behavior under heavy concurrent load.</p>
                                                   <p>
  -<a HREF="how_to_use_jmeter.html">Using JMeter</a>
  +       Apache JMeter may be used to test performance both on static and dynamic
  +       resources (files, Servlets, Perl scripts, Java Objects, Data Bases and 
  +       Queries, FTP Servers and more). It can be used to simulate a heavy
  +load on a server, network or object to test its strength or to analyze 
  +overall performance under different load types. You can use it to make a 
  +graphical analysis of performance or to test your server/script/object 
  +behavior under heavy concurrent load.
   </p>
  +                                                <p><a 
HREF="user_manual/index.html">Using JMeter</a></p>
                                                   <h2>What does it do?</h2>
                                                   <p>Apache JMeter features 
include:</p>
                                                   <ul>
  -      <li>Complete portability and <b>100% Java purity</b>.</li>
  -      <li>Full <b>Swing</b> and lightweight component support (precompiled JAR uses 
packages <code>javax.swing.*</code>).</li>
  -      <li>Pluggable Samplers allow unlimited testing capabilities.</li>
  -      <li>Full <b>multithreading</b> framework allows concurrent sampling by many 
threads and simultaneous sampling of different functions by seperate thread 
groups.</li>
  -      <li>Several load statistics may be choosen with <b>pluggable timers</b>.</li>
  -      <li>Data analisys and <b>visualisation plugins</b> allow great extendibility 
as well as
  -             personalization.</li>
  -      <li>Careful <b>GUI</b> design allows faster operation and more precise 
timings.</li>
  -  </ul>
  +     <li>Can load and performance test HTTP and FTP servers as well as
  +             arbitrary database queries (via JDBC)</li>
  +     <li>Complete portability and <b>100% Java purity</b>.</li>
  +     <li>Full <b>Swing</b> and lightweight component support (precompiled JAR uses 
packages 
  +             <code>javax.swing.*</code>).</li>
  +     <li>Full <b>multithreading</b> framework allows concurrent sampling by many 
threads and
  +             simultaneous sampling of different functions by seperate thread 
groups.</li>
  +     <li>Careful <b>GUI</b> design allows faster operation and more precise 
timings.</li>
  +     <li>Caching and offline analysis/replaying of test results.</li>
  +     <li> Highly Extensible:
  +       <ul>
  +             <li>Pluggable Samplers allow unlimited testing capabilities.</li>
  +             <li>Several load statistics may be choosen with <b>pluggable 
timers</b>.</li>
  +             <li>Data analysis and <b>visualization plugins</b> allow great 
extendibility 
  +             as well as personalization.</li>
  +       </ul>
  +     </li>
  +</ul>
                               </blockquote>
         </td></tr>
       </table>
  
  
  
  1.2       +308 -1    jakarta-jmeter/docs/extending/index.html
  
  Index: index.html
  ===================================================================
  RCS file: /home/cvs/jakarta-jmeter/docs/extending/index.html,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- index.html        2001/03/17 22:25:40     1.1
  +++ index.html        2001/03/24 14:39:57     1.2
  @@ -87,8 +87,8 @@
        <li>Creating your own logic SamplerController</li>
        <li>Creating your own test sample SamplerController</li>
        <li>Creating your own Sampler</li>
  -     <li>Making your custom elements saveable and loadable from within JMeter</li>
        <li>Making your custom elements play nice as a JMeter UI component</li>
  +     <li>Making your custom elements saveable and loadable from within JMeter</li>
   </ul>
                                                   <h3>Creating your own Timer</h3>
                                                   <p>The timer interface:</p>
  @@ -163,6 +163,313 @@
   on their own), and you may have to do casting and parsing.  Example: an Integer will
   have to be converted from a String to an int, so your getXXX() method should check
   for this possibility to avoid exceptions.
  +</p>
  +                                                <h3>Creating your own logic 
SamplerController</h3>
  +                                                <p>The SamplerController interface 
looks as follows:</p>
  +                                                <pre>
  +       Entry nextEntry();
  +       Collection getListeners();
  +       void addSamplerController(SamplerController controller);
  +       void addConfigElement(ConfigElement config);
  +       Object clone();
  +</pre>
  +                                                <p>Again, <b>clone()</b> is a 
method that must be implemented to all SamplerControllers to avoid
  +contamination between sampling threads.</p>
  +                                                <p>The <b>nextEntry()</b> method is 
the essential job of a SamplerController - to deliver
  +Entry objects to be sampled.  An Entry object encapsulates all the information 
needed
  +by a Sampler to do its job.  The nextEntry() method should work like an iterator and
  +continuously return new Entry objects.
  +</p>
  +                                                <p>There are two boundary 
conditions that need to be handled.  If the Controller has no
  +more Entries to give, for the rest of the test, it should return <b>null</b>.  
Therefore,
  +if your Controller has sub-controllers it is receiving Entries from, it should 
remove
  +them from its list of controllers to get Entries from.  The other condition is when
  +your controller reaches the end of its list of Entries, and it needs to start over
  +from the beginning.  The parent Controller needs to know this so that it can move
  +on to its next controller in its list.  Therefore, at the end of each iteration,
  +your SamplerController needs to return a CycleEntry object instead of a normal 
Entry.
  +Conversely, this means that if your Controller receives a CycleEntry object, it 
should
  +move on to the next Controller in its list.</p>
  +                                                <p>A logic controller does not 
generate Entries on its own, but simply regulates
  +the flow of Entries from its sub-controllers.  A logic controller might provide
  +looping logic, or it might modify the Entries that pass through it, or whatever.
  +GenericController provides an implementation that does absolutely nothing but
  +pass Entries on from its sub-controllers.  This class is useful both for reference
  +purposes and to extend, since it provides a lot of methods you're likely to find
  +useful
  +</p>
  +                                                <p><b>getListeners()</b> is an odd 
member of this Class.  It's there to serve those who
  +want their controller to receive sample data.  This would be useful for a controller
  +that modified Entry objects based on previous sample results (like an HTML spider
  +that dynamically reacted to previously sampled webpages for links and forms).  The
  +responsibility of the controller implementer is to collect all potential listeners
  +from the sub-controller list, and add themselves if desired.  Most 
SamplerControllers
  +that extend GenericController don't have to do anything.</p>
  +                                                
<p><b>addSamplerController(SamplerController controller)</b> is the method used to
  +add sub controllers to your SamplerController. </p>
  +                                                
<p><b>addConfigElement(ConfigElement config)</b> Your SamplerController should also
  +be capable of holding configuration elements and adding them to Entries as they
  +pass through your controller.  Again, see GenericController for reference.  
Essentially,
  +all Entry objects that get returned by nextEntry() are handed all the ConfigElements
  +of the controller.
  +</p>
  +                                                <h3>Creating your own test sample 
SampleController</h3>
  +                                                <p>A SamplerController that 
generates Entry objects is just like a logic controller
  +except that it creates its own Entry objects instead of gathering them from
  +sub-controllers (although, to be fully correct, your test sample SampleController
  +should handle both possibilities).  Your test sample SampleController can also
  +benefit from extending GenericController.  By doing so, most of your cloning and
  +saving needs are handled (but probably not entirely).  See HttpTestSample as
  +reference.</p>
  +                                                <h3>Creating your own Sampler</h3>
  +                                                <p>The Sampler interface:</p>
  +                                                <pre>
  +       public SampleResult sample(Entry e)
  +</pre>
  +                                                <p>Your Sampler has two 
responsibilities.  Of lesser importance, it should do whatever
  +it is you want to do, given an Entry object that hopefully contains information
  +about what is to be sampled.  Of greater importance, your sampler should return
  +a SampleResult object that holds information about the sampling.  Information such
  +as how long the sample took, the text response from the sample (if appropriate), and
  +a string that describes the location of what was sampled.  The SampleResult 
interface
  +is essentially a Map with public static Strings as keys. </p>
  +                                                <h3>Making your custom elements 
play nice as a JMeter UI component</h3>
  +                                                <p>In order to take part in the 
JMeter UI, your component needs to implement the
  +JMeterComponentModel interface:</p>
  +                                                <pre>
  +       Class getGuiClass();
  +       public String getName();
  +       public void setName(String name);
  +       public Collection getAddList();
  +       public String getClassLabel();
  +       public void uncompile();
  +</pre>
  +                                                <p>Most of this stuff is easy, 
boring, and tedious.  getName(), setName() is a simple
  +String property that is the name of the object.  getClassLabel() should return
  +a String that describes the class.  This string will be displayed to the user and
  +so should be short but meaningful.  getGuiClass() should return a Class object for
  +the class that will be used as a GUI component.  This class should be a subclass
  +of java.awt.Container, and preferably a subclass of javax.swing.JComponent.</p>
  +                                                <p><b>getAddList()</b> should 
return a list of either Strings or JMenus.  These Strings
  +represent the Classes that can be added to your SamplerController.  Each String
  +should correspond to the target class's getClassLabel() String.  MenuFactory is
  +a class that will return some preset menu lists (such as all available 
SamplerControllers,
  +all available ConfigElements, etc).</p>
  +                                                <p><b>uncompile</b> is a cleanup 
method used between sampling runs.  When the user
  +hits "Start", JMeter "compiles" the objects in the tree.  Child nodes are added
  +to their parent objects recursively until there is one TestPlan object, which is
  +then submitted for testing.  Afterward, these elements have to un-added from their
  +parent objects, or uncompiled.  To uncompile your class, simply clear all your
  +data structures that are holding sub-elements.  For your SamplerController, this
  +will be the list of sub-controllers and the list of ConfigElements.</p>
  +                                                <p>That's it, except for your GUI 
class.  If your SamplerController has no
  +configuration needs, just return org.apache.jmeter.gui.NamePanel, and the user will
  +at least be able to change the name of your component.  Otherwise, create a gui 
class
  +that implements the ModelSupported interface:</p>
  +                                                <pre>
  +       void setModel(Object model);
  +       public void updateGui();
  +</pre>
  +                                                <p>setModel is used to hand your 
JMeterModelComponent class to the GUI class when
  +it is instantiated.  It is your responsibility for providing the means by which
  +the Gui class updates the values in the model class.  For updating in the other
  +direction, there is <b>updateGui()</b>, which the model class can call if necessary.
  +Note, normally, this call is made for you automatically whenever the Gui is brought
  +to the screen.  If you are creating a Visualizer, then you may need to use 
updateGui().
  +For reference, refer to UrlConfigGui (in 
org.apache.jmeter.protocol.http.config.gui).</p>
  +                                                <p>If you have done all this 
correctly, there's just one more step.  If you compile
  +your classes into the ApacheJMeter.jar file, then you're done.  Your classes will
  +be automatically found and used.  Otherwise, you will need to modify 
jmeter.properties.
  +The <i>search_paths</i> property should be modified to include the path where your
  +classes are.  This does not obviate the need for your classes to be in the JVM's
  +CLASSPATH - it is an additional requirement.  Otherwise, your classes will be
  +detected, and the Gui will not make them available to the user.</p>
  +                                                <h3>Making your custom elements 
saveable and loadable from within JMeter</h3>
  +                                                <p>The Saveable interface has just 
one method:</p>
  +                                                <pre>
  +       public Class getTagHandlerClass()
  +</pre>
  +                                                <p>This method simply returns the 
Class object that represents the Class that handles
  +the saving and loading of your component.</p>
  +                                                <p>To write this SaveHandler, make 
a class that extends TagHandler
  +(from org.apache.jmeter.save.xml).  Note, if your component extends 
AbstractConfigElement,
  +you don't need to do this - provided you only need to save information stored in
  +the Map from AbstractConfigElement.</p>
  +                                                <p>To write your own TagHandler, 
you will have to implement the following methods:</p>
  +                                                <pre>
  +       public abstract void setAtts(Attributes atts) throws Exception
  +       public String getPrimaryTagName()
  +       public void save(Saveable objectToSave,Writer out) throws IOException
  +</pre>
  +                                                <p><b>getPrimaryTagName()</b> 
should return the String that is the XML tagname that your
  +class handles.  When you save your object, it should all be contained within an
  +XML tag of the same name.  This will ensure that when JMeter's parser hits that tag,
  +your class will be called upon to handle the data.</p>
  +                                                <p><b>setAtts(Attributes atts)</b> 
is called when the parser first hits your tag.
  +If this primary tag has any attributes, this method represents your chance to save
  +the information.</p>
  +                                                <p><b>save(Saveable 
objectToSave,Writer out)</b> - when the user selects "Save",
  +JMeter will call this method and hand the Saveable object to be saved (it will be
  +the object that specified your TagHandler as the class responsible for it's saving).
  +This method should use the given Writer object to print all the XML necessary to
  +save the current state of the objectToSave.</p>
  +                                                <p>There's more you have to do to 
handle creating a new Object when JMeter parses
  +an XML file.  However, there's no standard interface you need to implement, but 
rather,
  +JMeter uses reflection to generate method calls into your class.  When JMeter hits
  +a tag that corresponds to your PrimaryTagName, an instance of your TagHandler will
  +be created, and it's setAtts() method will get called.  Thereafter, methods are 
called
  +depending on subsequent tags and character data.  For every tag, JMeter calls
  +&lt;tag-name&gt;TagStart(Attributes atts), and for every end tag, JMeter calls
  +&lt;tag-name&gt;TagEnd().</p>
  +                                                <p>Additionally, JMeter will call a 
method that corresponds to all tags that are
  +current.  So, for instance, if JMeter runs into a tag name "foo", then
  +<b>foo(Attributes atts)</b> will be called.  If JMeter then parses character data,
  +then foo(String data) will be called.  If JMeter parses a tag within foo, called
  +"nestedFoo", then JMeter will call <b>foo_nestedFoo(Attributes atts)</b> and
  +<b>foo_nestedFoo(String data)</b>.  And so on.
  +</p>
  +                                                <p>An annotated example:</p>
  +                                                <pre>
  +public class AbstractConfigElementHandler extends TagHandler
  +{
  +     private AbstractConfigElement config;
  +     private String currentProperty;
  +
  +     public AbstractConfigElementHandler()
  +     {
  +     }
  +
  +     /**
  +      * Returns the AbstractConfigElement object parsed from the XML.  This method
  +      * is required to fulfill the SaveHandler interface.  It is used by the XML
  +      * routines to gather all the saved objects.
  +      */
  +     public Object getModel()
  +     {
  +             return config;
  +     }
  +
  +     /**
  +      * This is called when a tag is first encountered for this handler class to 
handle.
  +      * The attributes of the tag are passed, and the SaveHandler object is expected
  +      * to instantiate a new object.
  +      */
  +     public void setAtts(Attributes atts) throws Exception
  +     {
  +             String className = atts.getValue("type");
  +             config = (AbstractConfigElement)Class.forName(className).newInstance();
  +     }
  +
  +     /**
  +      * Called by reflection when a &lt;property&gt; tag is encountered.  Again, the
  +      * attributes are passed.
  +      */
  +     public void property(Attributes atts)
  +     {
  +             currentProperty = atts.getValue("name");
  +     }
  +
  +     /**
  +      * Called by reflection when text between the begin and end &lt;property&gt;
  +      * tag is encountered.
  +      */
  +     public void property(String data)
  +     {
  +
  +             if(data != null &amp;&amp; data.trim().length() &gt; 0)
  +             {
  +                     config.putProperty(currentProperty,data);
  +                     currentProperty = null;
  +             }
  +     }
  +
  +     /**
  +      * Called by reflection when the &lt;property&gt; tag is ended.
  +      */
  +     public void propertyTagEnd()
  +     {
  +             // Here's a tricky bit.  See below for explanation.
  +             List children = xmlParent.takeChildObjects(this);
  +             if(children.size() == 1)
  +             {
  +                     
config.putProperty(currentProperty,((TagHandler)children.get(0)).getModel());
  +             }
  +     }
  +
  +
  +       /**
  +     * Gets the tag name that will trigger the use of this object's TagHandler.
  +     */
  +     public String getPrimaryTagName()
  +     {
  +             return "ConfigElement";
  +     }
  +
  +  /**
  +     * Tells the object to save itself to the given output stream.
  +     */
  +     public void save(Saveable obj,Writer out) throws IOException
  +     {
  +             AbstractConfigElement saved = (AbstractConfigElement)obj;
  +             out.write("&lt;ConfigElement type=\"");
  +             out.write(saved.getClass().getName());
  +             out.write("\"&gt;\n");
  +             Iterator iter = saved.getPropertyNames().iterator();
  +             while (iter.hasNext())
  +             {
  +                     String key = (String)iter.next();
  +                     Object value = saved.getProperty(key);
  +                     writeProperty(out,key,value);
  +             }
  +             out.write(&lt;/ConfigElement&gt;");
  +     }
  +
  +     /**
  +      * Routine to write each property to xml.
  +      */
  +     private void writeProperty(Writer out,String key,Object value) throws 
IOException
  +     {
  +             out.write("&lt;property name=\"");
  +             out.write(key);
  +             out.write("\"&gt;\n");
  +             JMeterHandler.writeObject(value,out);
  +             out.write("\n&lt;/property&gt;\n");
  +     }
  +</pre>
  +                                                <p>
  +In the propertyTagEnd method, takeChildObjects() is called on the xmlParent
  +instance variable.  xmlParent is inherited from TagHandler.  It is the 
DocumentHandler
  +object that is running the show.  xmlParent takes an XML file that represents a 
portion of
  +the test configuration tree, and recreates a tree-like data structure.  When it is
  +done, it will convert its tree-like data structure into the test configuration tree
  +structure.
  +</p>
  +                                                <p>However, sometimes, a tree 
element has sub objects that you do not want represented
  +in the tree - rather, they are objects that are part of your object.  But, they may
  +be complicated enough to warrant their own SaveHandler class, and thus, the 
xmlParent
  +picks them up as part of its tree.  When the tag is done, and you know that there 
are
  +child objects you want to grab, you can call the "takeChildObjects" method and get a
  +List object containing them all.  This will remove them from the tree, and you can 
add
  +them to your object that you're creating.
  +</p>
  +                                                <p>
  +UrlConfig is good example.  It extends AbstractConfigElement, so it uses exactly the
  +code above to save and reload itself from XML.  However, one of the pieces of data
  +that UrlConfig stores is an Arguments object.  Arguments is too complicated to save
  +to file as a simple string, so it has its own Handler object (ArgumentsHandler).  In
  +the above code, when the call to JMeterHandler.writeObject(value,out) is made, the
  +writeObject method detects whether the object implements Saveable, and if so, calls
  +the object's SaveHandler class to handle saving it.  This means, however, that when
  +reading that XML file, the Argument object will show up as a separate entity in
  +the data tree, whereas it originally was just part of the data of the UrlConfig
  +object.  In order to preserve that relationship, it's necessary for the
  +AbstractConfigElementHandler to check after each property tag is done for child
  +objects in the tree, and take them for its own use.
  +</p>
  +                                                <p>
  +Please study the other SaveHandler objects and the TagHandler class to learn more
  +about how saving is accomplished.  Once you understand the design, writing your
  +own SaveHandler is very easy.
   </p>
                               </blockquote>
         </td></tr>
  
  
  
  1.2       +46 -28    jakarta-jmeter/docs/user_manual/index.html
  
  Index: index.html
  ===================================================================
  RCS file: /home/cvs/jakarta-jmeter/docs/user_manual/index.html,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- index.html        2001/03/17 22:25:40     1.1
  +++ index.html        2001/03/24 14:39:58     1.2
  @@ -86,28 +86,36 @@
        <li><a HREF="timers.html">Using Timers</a></li>
        <li><a HREF="Running.html">Running the test script</a></li>
        <li><a href="saving.html">Saving test script elements</a></li>
  +     <li><a href="rmi.html">Running JMeter over RMI</a></li>
  +
   </UL>
  -                                                <p>
  -<a NAME="overview" />
  -<H2>Overview</H2>
  -JMeter 1.6 has a new UI layout.  The screen is divided into two sections.  On the 
left is
  -a tree which represents your test configuration.  Trees are good for representing
  -data that is hierarchical and ordered, and your test data is both. On the right,
  -or the main window, control panels will be shown allowing you to enter your test 
data for
  -each element in the tree.  It is also the window for you to view the data 
visualizers.
  -</p>
                                                   <p>
  +<i>
   Throughout this documentation, examples will most often be given in terms of testing
   web applications, although JMeter is capable of testing most any type of 
server-client
   application.
  +</i>
   </p>
  +                                                <a NAME="overview" />
  +                                                <H2>Overview</H2>
  +                                                <p>
  +JMeter 1.6 has a new UI layout.  The window is divided into two sections.  On the 
left is
  +a tree which represents a test configuration.  The tree represents both
  +the hierarchical and ordered nature of the test.  A test can be made up of
  +one or many subtests and each of these subtests may have a particular
  +ordering.
  +The main display is on the right side of the window.
  +Whenever an element in the tree is selected, its control panel is shown in
  +the main display allowing you to enter your test data.
  +When a visualizer is selected the main display will contain the
  +visualizer's view of the current test.</p>
                                                   <table>
                           <tr>
                           <td bgcolor="#a0ddf0" colspan="" rowspan="" valign="top" 
align="left">
       <font color="#000000" size="-1" face="arial,helvetica,sanserif">
           
                       <b>Most functions in the UI are available from popup menus
  -that appear when you right-click on the element you wish to affect</b>
  +that appear when you right-click on an element in the test tree.</b>
                   &nbsp;
       </font>
   </td>
  @@ -115,35 +123,43 @@
   
               </table>
                                                   <p>
  -The tree begins with two elements - TestPlan and WorkBench.  Under the testplan 
element
  -will go all the elements involved with your test.  The workbench is simply an area 
to
  -store test elements while you work.
  +The test configuration tree begins with two elements - <b>TestPlan</b>
  +and <b>WorkBench</b>.  The <b>TestPlan</b> element
  +will contain all the elements which make up your test.
  +The <b>WorkBench</b> is simply an area to store test elements while you
  +are in the process of constructing a test.
   </p>
                                                   <p>
  -A testplan consists of one or more ThreadGroups. A ThreadGroup may contain 
<b>timers</b>,
  -<b>listeners</b>, <b>controllers</b>, and <b>config elements</b>.  It also defines 
a number of threads
  -to be used for the threadgroup.  ThreadGroups cannot be nested.
  +A <b>TestPlan</b> consists of one or more <b>ThreadGroups</b>. A
  +<b>ThreadGroup</b> is a root element (it can not be nested) which may contain 
<b>timers</b>,
  +<b>listeners</b>, <b>controllers</b>, and <b>config elements</b>.  A 
<b>ThreadGroup</b> also
  +defines the number of threads available to the threadgroup.
   </p>
                                                   <ul>
   <li>A <b>timer</b> is a simple element that controls how long JMeter should delay 
between each test
  -sample when it runs.  This allows JMeter to simulate human actions more closely.  
Timers do not
  -contain sub-elements in the tree.
  +sample when it runs.  This allows JMeter to simulate human actions more closely.
  +Timer element's are leaves in the test tree they can not contain
  +sub-elements.
   </li>
   <li>A <b>listener</b> receives information about response data while JMeter runs.  
For instance, during testing
   of a website, a listener receives and collects sample data that indicates how many
   milliseconds it took the web server to respond to each request.  Normally, these 
listeners
   are visualizers (represent the data visually in the main window), or reporters 
(store the data
  -to file).  Listeners also do not contain sub-elements in the tree.
  +to file).  Listeners are also leaves in a test configuration tree.
   </li>
   <li>A <b>controller</b> is an element that controls the flow of test samples.  It 
also controls the process by which
  -test samples are created.  They are the heart of JMeter. Controllers may have other 
controllers and/or config elements as
  -sub-elements in the tree.
  +test samples are created.  Controllers implement JMeter's various testing
  +protocols. They may have other controllers and/or config elements as
  +sub-elements.
   </li>
  -<li>A <b>Config Element</b> represents a coherent set of information that is 
usually specifically targeted at a particular
  -protocol.  For instance, setting up a database test requires three config elements 
- one to configure the basic
  -information about the database (what host, what driver, login and password to use), 
one to configure the SQL query
  -to be tested, and one to configure the pool of database connections (how many 
connections to store in pool, etc).
  -Config Elements do not have sub-elements in the tree.
  +<li>A <b>Config Element</b> represents a coherent set of information that is
  +usually specifically targeted to a particular
  +protocol or controller.  For instance, setting up a database test requires three
  +config elements - one to configure the basic information about the database (what 
host,
  +what driver, login and password to use), one to configure the SQL query
  +to be tested, and one to configure the pool of database connections (how many 
connections
  +to store in pool, etc).
  +Config Elements are leaves in the test configuration tree.
   </li>
   </ul>
                                                   <p>As a user, you create elements 
in the tree, and start your test when ready.  When you start the test, JMeter
  @@ -154,8 +170,10 @@
   iterates through the test cases.
   </p>
                                                   <p>
  -It's important to understand that all elements in the tree will be applied to all 
elements at
  -that level and below.  This is why it makes sense to add a URL Config Element to the
  +When a test is run, every element in the tree receives every element that
  +is above it in the test configuration.  That is a Timer inserted into the
  +test configuration tree at the highest level will apply to every element
  +that lies below it.  This is why it makes sense to add a URL Config Element to the
   ThreadGroup in addition to a Web Test Controller with multiple test samples.  If the
   top level config element has only a host name, the host name will be applied to all
   URL test samples that are used within the ThreadGroup.
  
  
  
  1.1                  jakarta-jmeter/docs/user_manual/rmi.html
  
  Index: rmi.html
  ===================================================================
  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">
  
  <!-- Content Stylesheet for Site -->
  
          
  <!-- start the processing -->
      <!-- ====================================================================== -->
      <!-- Main Page Section -->
      <!-- ====================================================================== -->
      <html>
          <head>
              <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>
  
                                                      <meta name="author" 
value="Michael Stover, brendan Burns, et al.">
              <meta name="email" value="[EMAIL PROTECTED]">
              
              <title>JMeter - Using JMeter</title>
          </head>
  
          <body bgcolor="#ffffff" text="#000000" link="#525D76">        
              <table border="0" width="100%" cellspacing="0">
                  <!-- TOP IMAGE -->
                  <tr>
                      <td colspan="2">
  <a href="http://jakarta.apache.org"><img 
src="http://jakarta.apache.org/images/jakarta-logo.gif" align="left" border="0"/></a>
  </td>
                  </tr>
              </table>
              <table border="0" width="100%" cellspacing="4">
                  <tr><td colspan="2">
                      <hr noshade="" size="1"/>
                  </td></tr>
                  
                  <tr>
                      <!-- LEFT SIDE NAVIGATION -->
                      <td valign="top" nowrap="true">
                                  <p><strong>About</strong></p>
          <ul>
                      <li>    <a href="../index.html">Overview</a>
  </li>
                      <li>    <a href="../user_manual/index.html">Usage</a>
  </li>
                      <li>    <a 
href="http://jakarta.apache.org/builds/jakarta-jmeter/">Download</a>
  </li>
                      <li>    <a href="../installing.html">Install</a>
  </li>
                      <li>    <a href="../running.html">Running</a>
  </li>
                      <li>    <a href="../extending/index.html">Extending</a>
  </li>
                      <li>    <a href="../changes.html">Changes</a>
  </li>
                      <li>    <a href="../bugs.html">Known Bugs</a>
  </li>
                      <li>    <a href="../license.html">License</a>
  </li>
                      <li>    <a href="../todo.html">TODO</a>
  </li>
                  </ul>
              <p><strong>Community</strong></p>
          <ul>
                      <li>    <a 
href="http://jakarta.apache.org/site/getinvolved.html">Get Involved</a>
  </li>
                      <li>    <a 
href="http://jakarta.apache.org/site/mail.html">Mailing Lists</a>
  </li>
                      <li>    <a 
href="http://jakarta.apache.org/site/cvsindex.html">CVS Repositories</a>
  </li>
                  </ul>
                          </td>
                      <td align="left" valign="top">
                                                                      <table 
border="0" cellspacing="0" cellpadding="2" width="100%">
        <tr><td bgcolor="#525D76">
          <font color="#ffffff" face="arial,helvetica,sanserif">
            <strong>JMeter over RMI</strong>
          </font>
        </td></tr>
        <tr><td>
          <blockquote>
                                      <p>
  In most real world web application environments the machine running the web
  application is not the machine which is sitting on your desk.  However to 
  get a true estimate of performance you would like to be measuring the 
  true response time of your server, not any (highly variable) network 
  bottle-necks that might exist between you and the machine on which your web 
  application is running.  At the same time using a GUI especially a Java 
  GUI over the network is at best painful (using Un*x and X) and at times not 
  possible (using windows).
  </p>
                                                  <p>
  To solve this problem JMeter provides a client-server architecture for load 
  and performance testing which allows the JMeterEngine (the tester) to 
  reside and run on your application server while the configuration GUI and 
  visualization executes on the development machine.  This offers a number 
  of features:
  <ul>
        <li>True performance measurment without network bottlenecks</li>
        <li>Fast responsive GUI</li>
        <li>Saving of test samples to a local machine</li>
        <li>Managment of multiple JMeterEngines from a single machine</li>
  </ul>
  </p>
                              </blockquote>
        </td></tr>
      </table>
                                                  <table border="0" cellspacing="0" 
cellpadding="2" width="100%">
        <tr><td bgcolor="#525D76">
          <font color="#ffffff" face="arial,helvetica,sanserif">
            <strong>Running the JMeter engine on the server</strong>
          </font>
        </td></tr>
        <tr><td>
          <blockquote>
                                      <p>
  Follow the <a href="installing.html">instructions</a> for installing 
  JMeter.  Once JMeter is sucessfully installed you will need to put the 
  <code>.../JMeter1.6/bin/ApacheJMeter.jar</code> into your 
  <code>CLASSPATH</code> environment variable and execute 
  <code>rmiregistry</code>.  Once the RMI registry is started you will need 
  to start the actual JMeterServer.  To do this run the 
  <code>jmeter-server</code> (Un*x) or <code>jmeter-server.bat</code> 
  script (windows) to start the server.  Your server should now be started, 
  to test it follow the instructions for running the JMeter client.
  </p>
                              </blockquote>
        </td></tr>
      </table>
                                                  <table border="0" cellspacing="0" 
cellpadding="2" width="100%">
        <tr><td bgcolor="#525D76">
          <font color="#ffffff" face="arial,helvetica,sanserif">
            <strong>Running the JMeter client</strong>
          </font>
        </td></tr>
        <tr><td>
          <blockquote>
                                      <p>
  Once JMeter is sucessfully installed you need to modify the 
  <code>jmeter.properties</code> file in the bin directory.  Change the 
  <code>remote_hosts</code> property to include the address of your server.  
  Next run <code>jmeter-client</code> (Un*x) or 
  <code>jmeter-client.bat</code> (windows) to connect to the JMeter Engine.
  If everything works as planned you will be connected with the JMeter 
  Engine and JMeter will behave exactly as if you were running it all 
  locally.  See the <a href="user_manual/index.html">user manual</a> for 
  detailed instructions.
  </p>
                              </blockquote>
        </td></tr>
      </table>
                                          </td>
                  </tr>
  
                  <!-- FOOTER -->
                  <tr><td colspan="2">
                      <hr noshade="" size="1"/>
                  </td></tr>
                  <tr><td colspan="2">
                      <div align="center"><font color="#525D76" size="-1"><em>
                      Copyright &#169; 1999-2001, Apache Software Foundation
                      </em></font></div>
                  </td></tr>
              </table>
          </body>
      </html>
  <!-- end the processing -->
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  1.10      +4 -5      jakarta-jmeter/src/org/apache/jmeter/TextDriver.java
  
  Index: TextDriver.java
  ===================================================================
  RCS file: /home/cvs/jakarta-jmeter/src/org/apache/jmeter/TextDriver.java,v
  retrieving revision 1.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- TextDriver.java   2001/03/17 22:25:41     1.9
  +++ TextDriver.java   2001/03/24 14:39:58     1.10
  @@ -75,8 +75,8 @@
    *  !ToDo (Class description)
    *
    *@author     $Author: mstover1 $
  - *@created    $Date: 2001/03/17 22:25:41 $
  - *@version    $Revision: 1.9 $
  + *@created    $Date: 2001/03/24 14:39:58 $
  + *@version    $Revision: 1.10 $
    ***********************************************************/
   public class TextDriver
   {
  @@ -127,7 +127,6 @@
                config.putProperty(UrlConfig.PATH, u.getFile());
                SamplerController test = new HttpTestSample();
                test.addConfigElement(config);
  -             test.setName("Default");
                return test;
        }
   
  @@ -198,8 +197,8 @@
         *  !ToDo (Class description)
         *
         *@author     $Author: mstover1 $
  -      *@created    $Date: 2001/03/17 22:25:41 $
  -      *@version    $Revision: 1.9 $
  +      *@created    $Date: 2001/03/24 14:39:58 $
  +      *@version    $Revision: 1.10 $
         ***********************************************************/
        private class OutputListener implements SampleListener
        {
  
  
  
  1.11      +2 -2      
jakarta-jmeter/src/org/apache/jmeter/control/GenericController.java
  
  Index: GenericController.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-jmeter/src/org/apache/jmeter/control/GenericController.java,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- GenericController.java    2001/03/17 22:25:43     1.10
  +++ GenericController.java    2001/03/24 14:39:58     1.11
  @@ -64,7 +64,7 @@
    *  Title: JMeter Description: Copyright: Copyright (c) 2000 Company: Apache
    *
    *@author     Michael Stover
  - *@created    $Date: 2001/03/17 22:25:43 $
  + *@created    $Date: 2001/03/24 14:39:58 $
    *@version    1.0
    ***********************************************************/
   
  @@ -337,7 +337,7 @@
                {
                        
control.addSamplerController((SamplerController)((SamplerController)iter.next()).clone());

                }
  -             control.setName(getName());
  +             //control.setName(getName());
        }
   
        /************************************************************
  
  
  
  1.8       +1 -15     
jakarta-jmeter/src/org/apache/jmeter/control/SamplerController.java
  
  Index: SamplerController.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-jmeter/src/org/apache/jmeter/control/SamplerController.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- SamplerController.java    2001/03/17 22:25:43     1.7
  +++ SamplerController.java    2001/03/24 14:39:58     1.8
  @@ -64,27 +64,13 @@
    *  Apache Foundation
    *
    *@author     Michael Stover
  - *@created    $Date: 2001/03/17 22:25:43 $
  + *@created    $Date: 2001/03/24 14:39:58 $
    *@version    1.0
    ***********************************************************/
   
   public interface SamplerController extends Cloneable
   {
        Entry nextEntry();
  -
  -     /************************************************************
  -      *  Returns the name of this Controller
  -      *
  -      *@return    The Name value
  -      ***********************************************************/
  -     String getName();
  -
  -     /************************************************************
  -      *  Sets the name of this controller.
  -      *
  -      *@param  name  The new Name value
  -      ***********************************************************/
  -     void setName(String name);
   
        /************************************************************
         *  Returns a sample result to the controller. Most controllers will likely
  
  
  
  1.3       +8 -0      
jakarta-jmeter/src/org/apache/jmeter/protocol/http/parser/HtmlParser.java
  
  Index: HtmlParser.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-jmeter/src/org/apache/jmeter/protocol/http/parser/HtmlParser.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- HtmlParser.java   2001/03/17 22:25:50     1.2
  +++ HtmlParser.java   2001/03/24 14:39:58     1.3
  @@ -56,6 +56,8 @@
   
   import java.util.*;
   
  +import org.apache.oro.text.regex.*;
  +
   import org.apache.jmeter.control.ResponseParser;
   import org.apache.jmeter.samplers.Entry;
   import org.apache.jmeter.protocol.http.config.UrlConfig;
  @@ -72,9 +74,15 @@
   public class HtmlParser implements ResponseParser
   {
        Collection parsedConfigs;
  +     Perl5Compiler compiler;
  +     List hrefPatterns;
  +     private int compilerOptions = Perl5Compiler.CASE_INSENSITIVE_MASK |
  +             Perl5Compiler.MULTILINE_MASK | Perl5Compiler.READ_ONLY_MASK;
   
        public HtmlParser()
        {
  +             compiler = new Perl5Compiler();
  +             hrefPatterns = new LinkedList();
        }
   
        public void modifyEntry(Entry entry, String currentResponse)
  
  
  
  1.4       +27 -18    
jakarta-jmeter/src/org/apache/jmeter/save/handlers/AbstractConfigElementHandler.java
  
  Index: AbstractConfigElementHandler.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-jmeter/src/org/apache/jmeter/save/handlers/AbstractConfigElementHandler.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- AbstractConfigElementHandler.java 2001/03/17 22:25:56     1.3
  +++ AbstractConfigElementHandler.java 2001/03/24 14:39:58     1.4
  @@ -81,22 +81,40 @@
        {
        }
   
  +     /**
  +      * Returns the AbstractConfigElement object parsed from the XML.  This method
  +      * is required to fulfill the SaveHandler interface.  It is used by the XML
  +      * routines to gather all the saved objects.
  +      */
        public Object getModel()
        {
                return config;
        }
   
  +     /**
  +      * This is called when a tag is first encountered for this handler class to 
handle.
  +      * The attributes of the tag are passed, and the SaveHandler object is 
expected
  +      * to instantiate a new object.
  +      */
        public void setAtts(Attributes atts) throws Exception
        {
                String className = atts.getValue("type");
                config = 
(AbstractConfigElement)Class.forName(className).newInstance();
        }
   
  +     /**
  +      * Called by reflection when a &lt;property&gt; tag is encountered.  Again, 
the
  +      * attributes are passed.
  +      */
        public void property(Attributes atts)
        {
                currentProperty = atts.getValue("name");
        }
   
  +     /**
  +      * Called by reflection when text between the begin and end &lt;property&gt;
  +      * tag is encountered.
  +      */
        public void property(String data)
        {
   
  @@ -107,6 +125,9 @@
                }
        }
   
  +     /**
  +      * Called by reflection when the &lt;property&gt; tag is ended.
  +      */
        public void propertyTagEnd()
        {
                List children = xmlParent.takeChildObjects(this);
  @@ -144,27 +165,15 @@
                out.write("</ConfigElement>");
        }
   
  +     /**
  +      * Routine to write each property to xml.
  +      */
        private void writeProperty(Writer out,String key,Object value) throws 
IOException
        {
                out.write("<property name=\"");
                out.write(key);
  -             out.write("\">");
  -             if(value instanceof Saveable)
  -             {
  -                     out.write("\n");
  -                     try
  -                     {
  -                             
((SaveHandler)((Saveable)value).getTagHandlerClass().newInstance()).save((Saveable)value,out);

  -                     }
  -                     catch (Exception ex)
  -                     {
  -                             ex.printStackTrace();
  -                     }
  -             }
  -             else
  -             {
  -                     out.write(value.toString());
  -             }
  -             out.write("</property>\n");
  +             out.write("\">\n");
  +             JMeterHandler.writeObject(value,out);
  +             out.write("\n</property>\n");
        }
   }
  
  
  
  1.3       +5 -0      
jakarta-jmeter/src/org/apache/jmeter/save/handlers/JMeterHandler.java
  
  Index: JMeterHandler.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-jmeter/src/org/apache/jmeter/save/handlers/JMeterHandler.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- JMeterHandler.java        2001/03/17 22:25:56     1.2
  +++ JMeterHandler.java        2001/03/24 14:39:59     1.3
  @@ -136,6 +136,11 @@
                                ex.printStackTrace();
                        }
                }
  +
  +             else
  +             {
  +                     out.write(obj.toString());
  +             }
        }
   
   
  
  
  
  1.2       +407 -100  jakarta-jmeter/xdocs/extending/index.xml
  
  Index: index.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-jmeter/xdocs/extending/index.xml,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- index.xml 2001/03/17 22:26:02     1.1
  +++ index.xml 2001/03/24 14:39:59     1.2
  @@ -1,101 +1,408 @@
  -<?xml version="1.0"?>
  -<document>
  -  <properties>
  -      <author email="[EMAIL PROTECTED]">Michael Stover, brendan Burns, et 
al.</author>
  -      <title>Extending JMeter</title>
  -  </properties>
  -<body>
  -<section name="Extending JMeter">
  -
  -<h2>Extending JMeter</h2>:
  -<p>There are several ways to extend JMeter and add functionality.  JMeter is 
designed
  -to make this task easier.
  -</p>
  -<ul>
  -     <li>Creating your own Timer</li>
  -     <li>Creating your own SampleListener (such as a visualizer, or reporter)</li>
  -     <li>Creating your own Config Element</li>
  -     <li>Creating your own logic SamplerController</li>
  -     <li>Creating your own test sample SamplerController</li>
  -     <li>Creating your own Sampler</li>
  -     <li>Making your custom elements saveable and loadable from within JMeter</li>
  -     <li>Making your custom elements play nice as a JMeter UI component</li>
  -</ul>
  -<h3>Creating your own Timer</h3>
  -<p>The timer interface:</p>
  -<pre>
  -       public long delay();
  -</pre>
  -<p>Not too complicated.  Your delay method must, each time it is called, return a
  -long representing the number of milliseconds to delay.  The constant timer returns 
the
  -same number every time it's called.  A random timer returns a different number each 
time.
  -</p>
  -<h3>Creating your own SampleListener</h3>
  -<p>The SampleListener interface:</p>
  -<pre>
  -       public void sampleOccurred(SampleEvent e);
  -       public void sampleStarted(SampleEvent e);
  -       public void sampleStopped(SampleEvent e);
  -</pre>
  -<p>sampleOccurred is the method called when a sample is completed, and the data has 
been
  -collected.  The SampleEvent object should contain all the information gathered
  -from the sample.  If your sample listener is primarily concerned with collecting the
  -data from a test run, you can implement this method - the other two are for other 
purposes and
  -can be ignored (though the methods have to be there for your class to compile).
  -</p>
  -<p>sampleStarted and sampleStopped are used to indicate the state of the sampling 
thread.
  -This is useful for visualizers that show the user the state of all running threads
  -(ie, they are running and waiting for response, or they're stopped and waiting
  -to begin again).
  -</p>
  -<h3>Creating your own Config Element</h3>
  -<p>The ConfigElement interface:</p>
  -<pre>
  -       public void addConfigElement(ConfigElement config);
  -       public boolean expectsModification();
  -       public Object clone();
  -</pre>
  -<p>The ConfigElement interface is sparse.  All ConfigElements are expected to 
implement
  -a public clone method.  The reason for this is that config elements will be cloned
  -for each different sampling thread, and most will be cloned for each sample.  </p>
  -<p>If your config element expects to be modified in the process of a test run,
  -and you want those modifications to carry over from sample to sample (as in
  -a cookie manager - you want to save all cookies that gets set throughout
  -the test), then return true for this method. Your config element will not be
  -cloned for each sample. If your config elements are more static in nature,
  -return false. If in doubt, return false.</p>
  -<p>The addConfigElement is required so that config elements can be layered.  For
  -instance, let's say a user creates a URL entry that contains default values -
  -they might use this to specify a server.  Then, all their test samples configure
  -individual test cases, but leave out the server field.  This information is combined
  -via the addConfigElement method.  Your custom config elements should do the right
  -thing when this method is called.  Normally, this involves ignoring such calls 
unless
  -the passed in ConfigElement is of the same type as yours, and then only merging in
  -values that are not already set in the object receiving the call (ie you probably
  -don't want to overwrite any values).
  -</p>
  -<p>You may have noticed there's no specification on how to get the config 
information
  -<b>out</b> of a ConfigElement.  This raises the question, who is going to use it?
  -At the end of the line, there will be a Sampler that will need the information held
  -in your config element.  The sampler that uses your config element needs to know 
more
  -about the class than the rest of JMeter - that information is not part of this 
interface.
  -</p>
  -<p>If at all possible, extend AbstractConfigElement when creating your own.  By 
doing so,
  -and by following some simple rules, you will get cloning and saving to XML of your
  -config element for free (as in, you don't have to do anything!).  
AbstractConfigElement
  -stores all it's values in a Map, and provides getProperty and putProperty methods.  
Your
  -config element can provide getXXX() and setXXX() methods, but these should delegate
  -to getProperty() and setProperty(), probably using static Strings as keys in the 
Map.
  -<br/>You can store any type of object, provided the objects are clonable and 
Saveable
  -(Strings, Integer, Long, Double, Float are all good in this regard).
  -</p>
  -<p>One caveat - if your config element has been restored from file, all the values
  -held in the Map will be String objects (except for elements that implement Saveable
  -on their own), and you may have to do casting and parsing.  Example: an Integer will
  -have to be converted from a String to an int, so your getXXX() method should check
  -for this possibility to avoid exceptions.
  -</p>
  -
  -</section>
  -</body>
  +<?xml version="1.0"?>
  +<document>
  +  <properties>
  +      <author email="[EMAIL PROTECTED]">Michael Stover, brendan Burns, et 
al.</author>
  +      <title>Extending JMeter</title>
  +  </properties>
  +<body>
  +<section name="Extending JMeter">
  +
  +<h2>Extending JMeter</h2>:
  +<p>There are several ways to extend JMeter and add functionality.  JMeter is 
designed
  +to make this task easier.
  +</p>
  +<ul>
  +     <li>Creating your own Timer</li>
  +     <li>Creating your own SampleListener (such as a visualizer, or reporter)</li>
  +     <li>Creating your own Config Element</li>
  +     <li>Creating your own logic SamplerController</li>
  +     <li>Creating your own test sample SamplerController</li>
  +     <li>Creating your own Sampler</li>
  +     <li>Making your custom elements play nice as a JMeter UI component</li>
  +     <li>Making your custom elements saveable and loadable from within JMeter</li>
  +</ul>
  +<h3>Creating your own Timer</h3>
  +<p>The timer interface:</p>
  +<pre>
  +       public long delay();
  +</pre>
  +<p>Not too complicated.  Your delay method must, each time it is called, return a
  +long representing the number of milliseconds to delay.  The constant timer returns 
the
  +same number every time it's called.  A random timer returns a different number each 
time.
  +</p>
  +<h3>Creating your own SampleListener</h3>
  +<p>The SampleListener interface:</p>
  +<pre>
  +       public void sampleOccurred(SampleEvent e);
  +       public void sampleStarted(SampleEvent e);
  +       public void sampleStopped(SampleEvent e);
  +</pre>
  +<p>sampleOccurred is the method called when a sample is completed, and the data has 
been
  +collected.  The SampleEvent object should contain all the information gathered
  +from the sample.  If your sample listener is primarily concerned with collecting 
the
  +data from a test run, you can implement this method - the other two are for other 
purposes and
  +can be ignored (though the methods have to be there for your class to compile).
  +</p>
  +<p>sampleStarted and sampleStopped are used to indicate the state of the sampling 
thread.
  +This is useful for visualizers that show the user the state of all running threads
  +(ie, they are running and waiting for response, or they're stopped and waiting
  +to begin again).
  +</p>
  +<h3>Creating your own Config Element</h3>
  +<p>The ConfigElement interface:</p>
  +<pre>
  +       public void addConfigElement(ConfigElement config);
  +       public boolean expectsModification();
  +       public Object clone();
  +</pre>
  +<p>The ConfigElement interface is sparse.  All ConfigElements are expected to 
implement
  +a public clone method.  The reason for this is that config elements will be cloned
  +for each different sampling thread, and most will be cloned for each sample.  </p>
  +<p>If your config element expects to be modified in the process of a test run,
  +and you want those modifications to carry over from sample to sample (as in
  +a cookie manager - you want to save all cookies that gets set throughout
  +the test), then return true for this method. Your config element will not be
  +cloned for each sample. If your config elements are more static in nature,
  +return false. If in doubt, return false.</p>
  +<p>The addConfigElement is required so that config elements can be layered.  For
  +instance, let's say a user creates a URL entry that contains default values -
  +they might use this to specify a server.  Then, all their test samples configure
  +individual test cases, but leave out the server field.  This information is 
combined
  +via the addConfigElement method.  Your custom config elements should do the right
  +thing when this method is called.  Normally, this involves ignoring such calls 
unless
  +the passed in ConfigElement is of the same type as yours, and then only merging in
  +values that are not already set in the object receiving the call (ie you probably
  +don't want to overwrite any values).
  +</p>
  +<p>You may have noticed there's no specification on how to get the config 
information
  +<b>out</b> of a ConfigElement.  This raises the question, who is going to use it?
  +At the end of the line, there will be a Sampler that will need the information held
  +in your config element.  The sampler that uses your config element needs to know 
more
  +about the class than the rest of JMeter - that information is not part of this 
interface.
  +</p>
  +<p>If at all possible, extend AbstractConfigElement when creating your own.  By 
doing so,
  +and by following some simple rules, you will get cloning and saving to XML of your
  +config element for free (as in, you don't have to do anything!).  
AbstractConfigElement
  +stores all it's values in a Map, and provides getProperty and putProperty methods.  
Your
  +config element can provide getXXX() and setXXX() methods, but these should delegate
  +to getProperty() and setProperty(), probably using static Strings as keys in the 
Map.
  +<br/>You can store any type of object, provided the objects are clonable and 
Saveable
  +(Strings, Integer, Long, Double, Float are all good in this regard).
  +</p>
  +<p>One caveat - if your config element has been restored from file, all the values
  +held in the Map will be String objects (except for elements that implement Saveable
  +on their own), and you may have to do casting and parsing.  Example: an Integer 
will
  +have to be converted from a String to an int, so your getXXX() method should check
  +for this possibility to avoid exceptions.
  +</p>
  +<h3>Creating your own logic SamplerController</h3>
  +<p>The SamplerController interface looks as follows:</p>
  +<pre>
  +       Entry nextEntry();
  +       Collection getListeners();
  +       void addSamplerController(SamplerController controller);
  +       void addConfigElement(ConfigElement config);
  +       Object clone();
  +</pre>
  +<p>Again, <b>clone()</b> is a method that must be implemented to all 
SamplerControllers to avoid
  +contamination between sampling threads.</p>
  +<p>The <b>nextEntry()</b> method is the essential job of a SamplerController - to 
deliver
  +Entry objects to be sampled.  An Entry object encapsulates all the information 
needed
  +by a Sampler to do its job.  The nextEntry() method should work like an iterator 
and
  +continuously return new Entry objects.
  +</p>
  +<p>There are two boundary conditions that need to be handled.  If the Controller 
has no
  +more Entries to give, for the rest of the test, it should return <b>null</b>.  
Therefore,
  +if your Controller has sub-controllers it is receiving Entries from, it should 
remove
  +them from its list of controllers to get Entries from.  The other condition is when
  +your controller reaches the end of its list of Entries, and it needs to start over
  +from the beginning.  The parent Controller needs to know this so that it can move
  +on to its next controller in its list.  Therefore, at the end of each iteration,
  +your SamplerController needs to return a CycleEntry object instead of a normal 
Entry.
  +Conversely, this means that if your Controller receives a CycleEntry object, it 
should
  +move on to the next Controller in its list.</p>
  +<p>A logic controller does not generate Entries on its own, but simply regulates
  +the flow of Entries from its sub-controllers.  A logic controller might provide
  +looping logic, or it might modify the Entries that pass through it, or whatever.
  +GenericController provides an implementation that does absolutely nothing but
  +pass Entries on from its sub-controllers.  This class is useful both for reference
  +purposes and to extend, since it provides a lot of methods you're likely to find
  +useful
  +</p>
  +<p><b>getListeners()</b> is an odd member of this Class.  It's there to serve those 
who
  +want their controller to receive sample data.  This would be useful for a 
controller
  +that modified Entry objects based on previous sample results (like an HTML spider
  +that dynamically reacted to previously sampled webpages for links and forms).  The
  +responsibility of the controller implementer is to collect all potential listeners
  +from the sub-controller list, and add themselves if desired.  Most 
SamplerControllers
  +that extend GenericController don't have to do anything.</p>
  +<p><b>addSamplerController(SamplerController controller)</b> is the method used to
  +add sub controllers to your SamplerController. </p>
  +<p><b>addConfigElement(ConfigElement config)</b> Your SamplerController should also
  +be capable of holding configuration elements and adding them to Entries as they
  +pass through your controller.  Again, see GenericController for reference.  
Essentially,
  +all Entry objects that get returned by nextEntry() are handed all the 
ConfigElements
  +of the controller.
  +</p>
  +<h3>Creating your own test sample SampleController</h3>
  +<p>A SamplerController that generates Entry objects is just like a logic controller
  +except that it creates its own Entry objects instead of gathering them from
  +sub-controllers (although, to be fully correct, your test sample SampleController
  +should handle both possibilities).  Your test sample SampleController can also
  +benefit from extending GenericController.  By doing so, most of your cloning and
  +saving needs are handled (but probably not entirely).  See HttpTestSample as
  +reference.</p>
  +<h3>Creating your own Sampler</h3>
  +<p>The Sampler interface:</p>
  +<pre>
  +       public SampleResult sample(Entry e)
  +</pre>
  +<p>Your Sampler has two responsibilities.  Of lesser importance, it should do 
whatever
  +it is you want to do, given an Entry object that hopefully contains information
  +about what is to be sampled.  Of greater importance, your sampler should return
  +a SampleResult object that holds information about the sampling.  Information such
  +as how long the sample took, the text response from the sample (if appropriate), 
and
  +a string that describes the location of what was sampled.  The SampleResult 
interface
  +is essentially a Map with public static Strings as keys. </p>
  +<h3>Making your custom elements play nice as a JMeter UI component</h3>
  +<p>In order to take part in the JMeter UI, your component needs to implement the
  +JMeterComponentModel interface:</p>
  +<pre>
  +       Class getGuiClass();
  +       public String getName();
  +       public void setName(String name);
  +       public Collection getAddList();
  +       public String getClassLabel();
  +       public void uncompile();
  +</pre>
  +<p>Most of this stuff is easy, boring, and tedious.  getName(), setName() is a 
simple
  +String property that is the name of the object.  getClassLabel() should return
  +a String that describes the class.  This string will be displayed to the user and
  +so should be short but meaningful.  getGuiClass() should return a Class object for
  +the class that will be used as a GUI component.  This class should be a subclass
  +of java.awt.Container, and preferably a subclass of javax.swing.JComponent.</p>
  +<p><b>getAddList()</b> should return a list of either Strings or JMenus.  These 
Strings
  +represent the Classes that can be added to your SamplerController.  Each String
  +should correspond to the target class's getClassLabel() String.  MenuFactory is
  +a class that will return some preset menu lists (such as all available 
SamplerControllers,
  +all available ConfigElements, etc).</p>
  +<p><b>uncompile</b> is a cleanup method used between sampling runs.  When the user
  +hits "Start", JMeter "compiles" the objects in the tree.  Child nodes are added
  +to their parent objects recursively until there is one TestPlan object, which is
  +then submitted for testing.  Afterward, these elements have to un-added from their
  +parent objects, or uncompiled.  To uncompile your class, simply clear all your
  +data structures that are holding sub-elements.  For your SamplerController, this
  +will be the list of sub-controllers and the list of ConfigElements.</p>
  +<p>That's it, except for your GUI class.  If your SamplerController has no
  +configuration needs, just return org.apache.jmeter.gui.NamePanel, and the user will
  +at least be able to change the name of your component.  Otherwise, create a gui 
class
  +that implements the ModelSupported interface:</p>
  +<pre>
  +       void setModel(Object model);
  +       public void updateGui();
  +</pre>
  +<p>setModel is used to hand your JMeterModelComponent class to the GUI class when
  +it is instantiated.  It is your responsibility for providing the means by which
  +the Gui class updates the values in the model class.  For updating in the other
  +direction, there is <b>updateGui()</b>, which the model class can call if 
necessary.
  +Note, normally, this call is made for you automatically whenever the Gui is brought
  +to the screen.  If you are creating a Visualizer, then you may need to use 
updateGui().
  +For reference, refer to UrlConfigGui (in 
org.apache.jmeter.protocol.http.config.gui).</p>
  +<p>If you have done all this correctly, there's just one more step.  If you compile
  +your classes into the ApacheJMeter.jar file, then you're done.  Your classes will
  +be automatically found and used.  Otherwise, you will need to modify 
jmeter.properties.
  +The <i>search_paths</i> property should be modified to include the path where your
  +classes are.  This does not obviate the need for your classes to be in the JVM's
  +CLASSPATH - it is an additional requirement.  Otherwise, your classes will be
  +detected, and the Gui will not make them available to the user.</p>
  +<h3>Making your custom elements saveable and loadable from within JMeter</h3>
  +<p>The Saveable interface has just one method:</p>
  +<pre>
  +       public Class getTagHandlerClass()
  +</pre>
  +<p>This method simply returns the Class object that represents the Class that 
handles
  +the saving and loading of your component.</p>
  +<p>To write this SaveHandler, make a class that extends TagHandler
  +(from org.apache.jmeter.save.xml).  Note, if your component extends 
AbstractConfigElement,
  +you don't need to do this - provided you only need to save information stored in
  +the Map from AbstractConfigElement.</p>
  +<p>To write your own TagHandler, you will have to implement the following 
methods:</p>
  +<pre>
  +       public abstract void setAtts(Attributes atts) throws Exception
  +       public String getPrimaryTagName()
  +       public void save(Saveable objectToSave,Writer out) throws IOException
  +</pre>
  +<p><b>getPrimaryTagName()</b> should return the String that is the XML tagname that 
your
  +class handles.  When you save your object, it should all be contained within an
  +XML tag of the same name.  This will ensure that when JMeter's parser hits that 
tag,
  +your class will be called upon to handle the data.</p>
  +<p><b>setAtts(Attributes atts)</b> is called when the parser first hits your tag.
  +If this primary tag has any attributes, this method represents your chance to save
  +the information.</p>
  +<p><b>save(Saveable objectToSave,Writer out)</b> - when the user selects "Save",
  +JMeter will call this method and hand the Saveable object to be saved (it will be
  +the object that specified your TagHandler as the class responsible for it's 
saving).
  +This method should use the given Writer object to print all the XML necessary to
  +save the current state of the objectToSave.</p>
  +<p>There's more you have to do to handle creating a new Object when JMeter parses
  +an XML file.  However, there's no standard interface you need to implement, but 
rather,
  +JMeter uses reflection to generate method calls into your class.  When JMeter hits
  +a tag that corresponds to your PrimaryTagName, an instance of your TagHandler will
  +be created, and it's setAtts() method will get called.  Thereafter, methods are 
called
  +depending on subsequent tags and character data.  For every tag, JMeter calls
  +&lt;tag-name&gt;TagStart(Attributes atts), and for every end tag, JMeter calls
  +&lt;tag-name&gt;TagEnd().</p>
  +<p>Additionally, JMeter will call a method that corresponds to all tags that are
  +current.  So, for instance, if JMeter runs into a tag name "foo", then
  +<b>foo(Attributes atts)</b> will be called.  If JMeter then parses character data,
  +then foo(String data) will be called.  If JMeter parses a tag within foo, called
  +"nestedFoo", then JMeter will call <b>foo_nestedFoo(Attributes atts)</b> and
  +<b>foo_nestedFoo(String data)</b>.  And so on.
  +</p>
  +<p>An annotated example:</p>
  +<pre>
  +public class AbstractConfigElementHandler extends TagHandler
  +{
  +     private AbstractConfigElement config;
  +     private String currentProperty;
  +
  +     public AbstractConfigElementHandler()
  +     {
  +     }
  +
  +     /**
  +      * Returns the AbstractConfigElement object parsed from the XML.  This method
  +      * is required to fulfill the SaveHandler interface.  It is used by the XML
  +      * routines to gather all the saved objects.
  +      */
  +     public Object getModel()
  +     {
  +             return config;
  +     }
  +
  +     /**
  +      * This is called when a tag is first encountered for this handler class to 
handle.
  +      * The attributes of the tag are passed, and the SaveHandler object is 
expected
  +      * to instantiate a new object.
  +      */
  +     public void setAtts(Attributes atts) throws Exception
  +     {
  +             String className = atts.getValue("type");
  +             config = 
(AbstractConfigElement)Class.forName(className).newInstance();
  +     }
  +
  +     /**
  +      * Called by reflection when a &lt;property&gt; tag is encountered.  Again, 
the
  +      * attributes are passed.
  +      */
  +     public void property(Attributes atts)
  +     {
  +             currentProperty = atts.getValue("name");
  +     }
  +
  +     /**
  +      * Called by reflection when text between the begin and end &lt;property&gt;
  +      * tag is encountered.
  +      */
  +     public void property(String data)
  +     {
  +
  +             if(data != null &amp;&amp; data.trim().length() &gt; 0)
  +             {
  +                     config.putProperty(currentProperty,data);
  +                     currentProperty = null;
  +             }
  +     }
  +
  +     /**
  +      * Called by reflection when the &lt;property&gt; tag is ended.
  +      */
  +     public void propertyTagEnd()
  +     {
  +             // Here's a tricky bit.  See below for explanation.
  +             List children = xmlParent.takeChildObjects(this);
  +             if(children.size() == 1)
  +             {
  +                     
config.putProperty(currentProperty,((TagHandler)children.get(0)).getModel());
  +             }
  +     }
  +
  +
  +       /**
  +     * Gets the tag name that will trigger the use of this object's TagHandler.
  +     */
  +     public String getPrimaryTagName()
  +     {
  +             return "ConfigElement";
  +     }
  +
  +  /**
  +     * Tells the object to save itself to the given output stream.
  +     */
  +     public void save(Saveable obj,Writer out) throws IOException
  +     {
  +             AbstractConfigElement saved = (AbstractConfigElement)obj;
  +             out.write("&lt;ConfigElement type=\"");
  +             out.write(saved.getClass().getName());
  +             out.write("\">\n");
  +             Iterator iter = saved.getPropertyNames().iterator();
  +             while (iter.hasNext())
  +             {
  +                     String key = (String)iter.next();
  +                     Object value = saved.getProperty(key);
  +                     writeProperty(out,key,value);
  +             }
  +             out.write(&lt;/ConfigElement&gt;");
  +     }
  +
  +     /**
  +      * Routine to write each property to xml.
  +      */
  +     private void writeProperty(Writer out,String key,Object value) throws 
IOException
  +     {
  +             out.write("&lt;property name=\"");
  +             out.write(key);
  +             out.write("\">\n");
  +             JMeterHandler.writeObject(value,out);
  +             out.write("\n&lt;/property&gt;\n");
  +     }
  +</pre>
  +<p>
  +In the propertyTagEnd method, takeChildObjects() is called on the xmlParent
  +instance variable.  xmlParent is inherited from TagHandler.  It is the 
DocumentHandler
  +object that is running the show.  xmlParent takes an XML file that represents a 
portion of
  +the test configuration tree, and recreates a tree-like data structure.  When it is
  +done, it will convert its tree-like data structure into the test configuration tree
  +structure.
  +</p>
  +<p>However, sometimes, a tree element has sub objects that you do not want 
represented
  +in the tree - rather, they are objects that are part of your object.  But, they may
  +be complicated enough to warrant their own SaveHandler class, and thus, the 
xmlParent
  +picks them up as part of its tree.  When the tag is done, and you know that there 
are
  +child objects you want to grab, you can call the "takeChildObjects" method and get 
a
  +List object containing them all.  This will remove them from the tree, and you can 
add
  +them to your object that you're creating.
  +</p>
  +<p>
  +UrlConfig is good example.  It extends AbstractConfigElement, so it uses exactly 
the
  +code above to save and reload itself from XML.  However, one of the pieces of data
  +that UrlConfig stores is an Arguments object.  Arguments is too complicated to save
  +to file as a simple string, so it has its own Handler object (ArgumentsHandler).  
In
  +the above code, when the call to JMeterHandler.writeObject(value,out) is made, the
  +writeObject method detects whether the object implements Saveable, and if so, calls
  +the object's SaveHandler class to handle saving it.  This means, however, that when
  +reading that XML file, the Argument object will show up as a separate entity in
  +the data tree, whereas it originally was just part of the data of the UrlConfig
  +object.  In order to preserve that relationship, it's necessary for the
  +AbstractConfigElementHandler to check after each property tag is done for child
  +objects in the tree, and take them for its own use.
  +</p>
  +<p>
  +Please study the other SaveHandler objects and the TagHandler class to learn more
  +about how saving is accomplished.  Once you understand the design, writing your
  +own SaveHandler is very easy.
  +</p>
  +
  +</section>
  +</body>
   </document>
  
  
  
  1.4       +23 -23    jakarta-jmeter/xdocs/user_manual/index.xml
  
  Index: index.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-jmeter/xdocs/user_manual/index.xml,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- index.xml 2001/03/20 23:31:45     1.3
  +++ index.xml 2001/03/24 14:39:59     1.4
  @@ -28,39 +28,39 @@
   application.
   </i>
   </p>
  -<p>
   <a NAME="overview"></a>
   <H2>Overview</H2>
  +<p>
   JMeter 1.6 has a new UI layout.  The window is divided into two sections.  On the 
left is
  -a tree which represents a test configuration.  The tree represents both 
  -the hierarchical and ordered nature of the test.  A test can be made up of 
  -one or many subtests and each of these subtests may have a particular 
  +a tree which represents a test configuration.  The tree represents both
  +the hierarchical and ordered nature of the test.  A test can be made up of
  +one or many subtests and each of these subtests may have a particular
   ordering.
  -The main display is on the right side of the window. 
  -Whenever an element in the tree is selected, its control panel is shown in 
  +The main display is on the right side of the window.
  +Whenever an element in the tree is selected, its control panel is shown in
   the main display allowing you to enter your test data.
  -When a visualizer is selected the main display will contain the 
  -visualizer's view of the current test.
  +When a visualizer is selected the main display will contain the
  +visualizer's view of the current test.</p>
   
   <table border="5"><tr><td><b>Most functions in the UI are available from popup menus
   that appear when you right-click on an element in the test 
tree.</b></td></tr></table>
   <p>
  -The test configuration tree begins with two elements - <b>TestPlan</b> 
  +The test configuration tree begins with two elements - <b>TestPlan</b>
   and <b>WorkBench</b>.  The <b>TestPlan</b> element
  -will contain all the elements which make up your test.  
  -The <b>WorkBench</b> is simply an area to store test elements while you 
  +will contain all the elements which make up your test.
  +The <b>WorkBench</b> is simply an area to store test elements while you
   are in the process of constructing a test.
   </p>
   <p>
  -A <b>TestPlan</b> consists of one or more <b>ThreadGroups</b>. A 
  +A <b>TestPlan</b> consists of one or more <b>ThreadGroups</b>. A
   <b>ThreadGroup</b> is a root element (it can not be nested) which may contain 
<b>timers</b>,
  -<b>listeners</b>, <b>controllers</b>, and <b>config elements</b>.  A 
<b>ThreadGroup</b> also 
  +<b>listeners</b>, <b>controllers</b>, and <b>config elements</b>.  A 
<b>ThreadGroup</b> also
   defines the number of threads available to the threadgroup.
   </p>
   <ul>
   <li>A <b>timer</b> is a simple element that controls how long JMeter should delay 
between each test
  -sample when it runs.  This allows JMeter to simulate human actions more closely.  
  -Timer element's are leaves in the test tree they can not contain 
  +sample when it runs.  This allows JMeter to simulate human actions more closely.
  +Timer element's are leaves in the test tree they can not contain
   sub-elements.
   </li>
   <li>A <b>listener</b> receives information about response data while JMeter runs.  
For instance, during testing
  @@ -70,16 +70,16 @@
   to file).  Listeners are also leaves in a test configuration tree.
   </li>
   <li>A <b>controller</b> is an element that controls the flow of test samples.  It 
also controls the process by which
  -test samples are created.  Controllers implement JMeter's various testing 
  +test samples are created.  Controllers implement JMeter's various testing
   protocols. They may have other controllers and/or config elements as
   sub-elements.
   </li>
  -<li>A <b>Config Element</b> represents a coherent set of information that is 
  +<li>A <b>Config Element</b> represents a coherent set of information that is
   usually specifically targeted to a particular
  -protocol or controller.  For instance, setting up a database test requires three 
  -config elements - one to configure the basic information about the database (what 
host, 
  +protocol or controller.  For instance, setting up a database test requires three
  +config elements - one to configure the basic information about the database (what 
host,
   what driver, login and password to use), one to configure the SQL query
  -to be tested, and one to configure the pool of database connections (how many 
connections 
  +to be tested, and one to configure the pool of database connections (how many 
connections
   to store in pool, etc).
   Config Elements are leaves in the test configuration tree.
   </li>
  @@ -93,9 +93,9 @@
   iterates through the test cases.
   </p>
   <p>
  -When a test is run, every element in the tree receives every element that 
  -is above it in the test configuration.  That is a Timer inserted into the 
  -test configuration tree at the highest level will apply to every element 
  +When a test is run, every element in the tree receives every element that
  +is above it in the test configuration.  That is a Timer inserted into the
  +test configuration tree at the highest level will apply to every element
   that lies below it.  This is why it makes sense to add a URL Config Element to the
   ThreadGroup in addition to a Web Test Controller with multiple test samples.  If the
   top level config element has only a host name, the host name will be applied to all
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to