mirror of https://github.com/apache/jmeter.git
PR:
Obtained from: Submitted by: Reviewed by: git-svn-id: https://svn.apache.org/repos/asf/jakarta/jmeter/trunk@322630 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
cee0c1d100
commit
9713a90204
|
|
@ -18,8 +18,8 @@ to make this task easier.
|
|||
<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>
|
||||
|
|
@ -95,6 +95,313 @@ on their own), and you may have to do casting and parsing. Example: an Integer
|
|||
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
|
||||
<tag-name>TagStart(Attributes atts), and for every end tag, JMeter calls
|
||||
<tag-name>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 <property> 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 <property>
|
||||
* tag is encountered.
|
||||
*/
|
||||
public void property(String data)
|
||||
{
|
||||
|
||||
if(data != null && data.trim().length() > 0)
|
||||
{
|
||||
config.putProperty(currentProperty,data);
|
||||
currentProperty = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by reflection when the <property> 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("<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(</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("\">\n");
|
||||
JMeterHandler.writeObject(value,out);
|
||||
out.write("\n</property>\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>
|
||||
|
|
|
|||
|
|
@ -28,39 +28,39 @@ web applications, although JMeter is capable of testing most any type of server-
|
|||
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 @@ are visualizers (represent the data visually in the main window), or reporters (
|
|||
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 @@ all of its ThreadGroups to the engine. The engine creates threads, and each thr
|
|||
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
|
||||
|
|
|
|||
Loading…
Reference in New Issue