You are viewing a plain text version of this content. The canonical link for it is here.
Posted to jmeter-dev@jakarta.apache.org by ms...@apache.org on 2002/07/13 05:17:49 UTC

cvs commit: jakarta-jmeter/xdocs/usermanual functions.xml glossary.xml index.xml

mstover1    2002/07/12 20:17:49

  Modified:    docs     running.html
               docs/usermanual glossary.html index.html
               src_1/org/apache/jmeter/protocol/all/modifier
                        CompoundFunction.java Function.java
                        RegexFunction.java
               src_1/org/apache/jmeter/threads TestCompiler.java
               xdocs/usermanual glossary.xml index.xml
  Added:       docs/usermanual functions.html
               src_1/org/apache/jmeter/protocol/all/modifier
                        AbstractFunction.java
               xdocs/usermanual functions.xml
  Log:
  New Function implementations plus a quick draft of documentation for functions
  
  Revision  Changes    Path
  1.46      +0 -0      jakarta-jmeter/docs/running.html
  
  Index: running.html
  ===================================================================
  RCS file: /home/cvs/jakarta-jmeter/docs/running.html,v
  retrieving revision 1.45
  retrieving revision 1.46
  diff -u -r1.45 -r1.46
  
  
  
  1.11      +1 -1      jakarta-jmeter/docs/usermanual/glossary.html
  
  Index: glossary.html
  ===================================================================
  RCS file: /home/cvs/jakarta-jmeter/docs/usermanual/glossary.html,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- glossary.html	7 Mar 2002 23:03:12 -0000	1.10
  +++ glossary.html	13 Jul 2002 03:17:48 -0000	1.11
  @@ -68,7 +68,7 @@
   						  						  									 <table border="0" cellspacing="0" cellpadding="2" width="100%">
   		<tr><td bgcolor="#525D76">
   		  <font color="#ffffff" face="arial,helvetica,sanserif">
  -			 <a name="glossary"><strong>14. Glossary</strong></a>
  +			 <a name="glossary"><strong>15. Glossary</strong></a>
   		  </font>
   		</td></tr>
   		<tr><td>
  
  
  
  1.24      +8 -1      jakarta-jmeter/docs/usermanual/index.html
  
  Index: index.html
  ===================================================================
  RCS file: /home/cvs/jakarta-jmeter/docs/usermanual/index.html,v
  retrieving revision 1.23
  retrieving revision 1.24
  diff -u -r1.23 -r1.24
  --- index.html	26 Jun 2002 00:59:49 -0000	1.23
  +++ index.html	13 Jul 2002 03:17:48 -0000	1.24
  @@ -645,8 +645,15 @@
   									
     
   												<li	>
  +												<a	 href="functions.html">
  +								14. Functions
  +						</a>
  +							</li>
  +									
  +  
  +												<li	>
   												<a	 href="glossary.html">
  -								14. Glossary
  +								15. Glossary
   						</a>
   							</li>
   									
  
  
  
  1.1                  jakarta-jmeter/docs/usermanual/functions.html
  
  Index: functions.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"/>
  
  								
  				<title>JMeter - User's Manual: Introduction</title>
  		  </head>
  
  		  <body bgcolor="#ffffff" text="#000000" link="#525D76">
  				<table border="0" 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 width="20%" valign="top" nowrap="true">
  						  	 		  <p><strong>About</strong></p>
  		  <ul>
  		  				<li>	 <a href="../index.html">Overview</a>
  </li>
  		  				<li>	 <a href="http://jakarta.apache.org/builds/jakarta-jmeter/">Download</a>
  </li>
  		  				<li>	 <a href="../changes.html">Changes</a>
  </li>
  		  				<li>	 <a href="http://nagoya.apache.org/bugzilla/buglist.cgi?bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&email1=&emailtype1=substring&emailassigned_to1=1&email2=&emailtype2=substring&emailreporter2=1&bugidtype=include&bug_id=&changedin=&votes=&chfieldfrom=&chfieldto=Now&chfieldvalue=&product=JMeter&short_desc=&short_desc_type=substring&long_desc=&long_desc_type=substring&bug_file_loc=&bug_file_loc_type=substring&keywords=&keywords_type=anywords&field0-0-0=noop&type0-0-0=noop&value0-0-0=&cmdtype=doit&order=Reuse+same+sort+as+last+time">Known Bugs</a>
  </li>
  		  				<li>	 <a href="../license.html">License</a>
  </li>
  		  				<li>	 <a href="../todo.html">TODO</a>
  </li>
  		  		  </ul>
  	 		  <p><strong>Documentation</strong></p>
  		  <ul>
  		  				<li>	 <a href="../usermanual/index.html">User Manual</a>
  </li>
  		  				<li>	 <a href="../extending/index.html">Developer Manual</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 width="80%" align="left" valign="top">
  						  						  									 <table border="0" cellspacing="0" cellpadding="2" width="100%">
  		<tr><td bgcolor="#525D76">
  		  <font color="#ffffff" face="arial,helvetica,sanserif">
  			 <a name="functions"><strong>14. Functions</strong></a>
  		  </font>
  		</td></tr>
  		<tr><td>
  		  <blockquote>
  		  									 				<p	>
  								
  JMeter functions are special values that can populate fields of any Sampler or other configuration
  element in a test tree.  A function looks like this:
  						</p>
  							  									 				<p	>
  												<code	>
  								${__functionName(var1,var2,var3)}
  						</code>
  							</p>
  							  									 				<p	>
  								Where "__functionName" matches the name of an existing built-in or user-defined function.
  												<br	>
  						</br>
  									
  Parentheses surround the parameters sent to the function.  The actual parameters vary from function 
  to function.  Functions that require no parameters can leave off the parentheses.  The function itself
  is wrapped in ${}.
  						</p>
  							  									 			 	 <table border="0" cellspacing="0" cellpadding="2" width="100%">
  		<tr><td bgcolor="#828DA6">
  		  <font color="#ffffff" face="arial,helvetica,sanserif">
  			 <a name="what_can_do"><strong>14.1 What can functions do</strong></a>
  		  </font>
  		</td></tr>
  		<tr><td>
  		  <blockquote>
  		  									 				<p	>
  								There are two kinds of functions: user-defined static values, and built-in functions.
  												<br	>
  						</br>
  									
  User-defined static values allows the user to define variables to be replaced with their static value when
  a test tree is compiled and submitted to be run.  This replacement happens once at the beginning of the test
  run.  This could be used to replace the DOMAIN field of all HTTP requests, for example - making it a simple 
  matter to change a test to target a different server with the same test.
  
  						</p>
  							  									 				<p	>
  								This type of replacement is possible without functions, but was less convenient and less intuitive.
  It required users to create default config elements that would fill in blank values of Samplers.  User-defined
  functions allows one to replace only part of any given value, not just filling in blank values.
  						</p>
  							  									 				<p	>
  								
  With built-in functions users can compute new values at run-time based on previous response data, which
  thread the function is in, the time, and many other sources.  These values are generated fresh for every
  request throughout the course of the test. 
  						</p>
  							  		  </blockquote>
  		</td></tr>
  		<tr><td><br/></td></tr>
  	 </table>
  							  									 			 	 <table border="0" cellspacing="0" cellpadding="2" width="100%">
  		<tr><td bgcolor="#828DA6">
  		  <font color="#ffffff" face="arial,helvetica,sanserif">
  			 <a name="where"><strong>14.2 Where can functions be used?</strong></a>
  		  </font>
  		</td></tr>
  		<tr><td>
  		  <blockquote>
  		  									 				<p	>
  								A user-defined function can be written into any field of any test component.  Some fields do not allow random strings 
  because they are expecting numbers, and thus will not accept a function.  However, most fields will allow
  functions.
  						</p>
  							  									 				<p	>
  								Built-in functions can be written into any field of non-controller test components. This includes
  Samplers, Timers, Listeners, Modifiers, Assertions, and Config Elements.
  						</p>
  							  		  </blockquote>
  		</td></tr>
  		<tr><td><br/></td></tr>
  	 </table>
  							  									 			 	 <table border="0" cellspacing="0" cellpadding="2" width="100%">
  		<tr><td bgcolor="#828DA6">
  		  <font color="#ffffff" face="arial,helvetica,sanserif">
  			 <a name="how"><strong>14.3 Writing the function string</strong></a>
  		  </font>
  		</td></tr>
  		<tr><td>
  		  <blockquote>
  		  									 				<p	>
  								User-defined functions take the form: 
  												<code	>
  								${varName}
  						</code>
  									.  In the TestPlan tree element, a two-column table
  of user-defined values is kept, matching up variable names with static values.  Referencing the
  variable in a test element is done by bracketing the variable name with '${' and '}'.
  						</p>
  							  									 				<p	>
  								Built-in functions are written in the same manner, but by convention, the names of built-in
  parameters begin with "__" to avoid conflict with user value names
  												<sup	>
  								*
  						</sup>
  									.  Some functions take arguments to
  configure them, and these go in parentheses, comma-delimited.  If the function takes no arguments, the parentheses can
  be left out.  A further complication for argument values that themselves contain commas is that the value
  should be encoded the way HTTP parameters are encoded.  JMeter provides a tool to help you construct
  function calls for various built-in functions, which you can then copy-paste.  If your argument values
  do not contain commas, encoding is not required.
  						</p>
  							  									 				<p><table border="1" bgcolor="#bbbb00" width="50%" cellspacing="0" cellpadding="2">
  		<tr><td>										<sup	>
  								*
  						</sup>
  									If you define a user-defined static variable with the same name as a built-in function, your static
  variable will override the built-in function.
  			</td></tr>
  	</table></p>
  							  		  </blockquote>
  		</td></tr>
  		<tr><td><br/></td></tr>
  	 </table>
  							  		  </blockquote>
  		  </p>
  		</td></tr>
  		<tr><td><br/></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.2       +32 -5     jakarta-jmeter/src_1/org/apache/jmeter/protocol/all/modifier/CompoundFunction.java
  
  Index: CompoundFunction.java
  ===================================================================
  RCS file: /home/cvs/jakarta-jmeter/src_1/org/apache/jmeter/protocol/all/modifier/CompoundFunction.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- CompoundFunction.java	12 Jul 2002 02:00:46 -0000	1.1
  +++ CompoundFunction.java	13 Jul 2002 03:17:48 -0000	1.2
  @@ -1,5 +1,6 @@
   package org.apache.jmeter.protocol.all.modifier;
   
  +import java.net.URLEncoder;
   import java.util.HashMap;
   import java.util.Iterator;
   import java.util.LinkedList;
  @@ -11,11 +12,8 @@
   import org.apache.jmeter.samplers.Sampler;
   import org.apache.jmeter.util.ClassFinder;
   import org.apache.oro.text.perl.Perl5Util;
  -import org.apache.oro.text.regex.Pattern;
   import org.apache.oro.text.regex.PatternCompiler;
  -import org.apache.oro.text.regex.PatternMatcher;
   import org.apache.oro.text.regex.Perl5Compiler;
  -import org.apache.oro.text.regex.Perl5Matcher;
   
   /**
    * @author mstover
  @@ -66,7 +64,28 @@
   	 * @see Function#execute(SampleResult)
   	 */
   	public String execute(SampleResult previousResult,Sampler currentSampler) {
  -		return null;
  +		if(compiledComponents == null || compiledComponents.size() == 0)
  +		{
  +			return "";
  +		}
  +		StringBuffer results = new StringBuffer();
  +		Iterator iter = compiledComponents.iterator();
  +		while(iter.hasNext())
  +		{
  +			Object item = iter.next();
  +			if(item instanceof Function)
  +			{
  +				try {
  +					results.append(((Function)item).execute(previousResult,currentSampler));
  +				} catch(InvalidVariableException e) {
  +				}
  +			}
  +			else
  +			{
  +				results.append(item);
  +			}
  +		}
  +		return results.toString();
   	}
   	
   	public CompoundFunction getFunction()
  @@ -249,17 +268,20 @@
   			assertTrue(function.hasFunction());
   			assertTrue(!function.hasStatics());
   			assertEquals("hello world",((Function)function.compiledComponents.getFirst()).execute(result,null));
  +			assertEquals("hello world",function.execute(result,null));
   		}
   		
   		public void testParseExample2() throws Exception
   		{
  -			function.setParameters("It should say:${${__regexFunction(<html>(.*)</html>,$1$)}}");
  +			function.setParameters("It should say:${${__regexFunction("+URLEncoder.encode("<html>(.*)</html>")+",$1$)}}");
   			assertEquals(3,function.compiledComponents.size());
   			assertEquals("It should say:${",function.compiledComponents.getFirst().toString());
   			assertTrue(function.hasFunction());
   			assertTrue(!function.hasStatics());
   			assertEquals("hello world",((Function)function.compiledComponents.get(1)).execute(result,null));
   			assertEquals("}",function.compiledComponents.get(2).toString());
  +			assertEquals("It should say:${hello world}",function.execute(result,null));
  +			assertEquals("It should say:${<html>(.*)</html>,$1$}",function.execute(null,null));
   		}
   		
   		public void testParseExample3() throws Exception
  @@ -270,6 +292,9 @@
   			assertTrue(!function.hasStatics());
   			assertEquals("hello world",((Function)function.compiledComponents.get(0)).execute(result,null));
   			assertEquals("hellorld",((Function)function.compiledComponents.get(1)).execute(result,null));
  +			assertEquals("hello worldhellorld",function.execute(result,null));
  +			assertEquals("<html>(.*)</html>,$1$<html>(.*o)(.*o)(.*)</html>,$1$$3$",
  +					function.execute(null,null));
   		}
   		
   		public void testParseExample4() throws Exception
  @@ -280,6 +305,8 @@
   			assertTrue(!function.hasStatics());
   			assertEquals("${non-existing function}",
   					function.compiledComponents.getFirst().toString());
  +			assertEquals("${non-existing function}",function.execute(result,null));
  +			assertEquals("${non-existing function}",function.execute(null,null));
   		}
   		
   		public void testParseExample5() throws Exception
  
  
  
  1.2       +2 -1      jakarta-jmeter/src_1/org/apache/jmeter/protocol/all/modifier/Function.java
  
  Index: Function.java
  ===================================================================
  RCS file: /home/cvs/jakarta-jmeter/src_1/org/apache/jmeter/protocol/all/modifier/Function.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Function.java	12 Jul 2002 02:00:46 -0000	1.1
  +++ Function.java	13 Jul 2002 03:17:48 -0000	1.2
  @@ -11,7 +11,8 @@
    */
   public interface Function {
   	
  -	public String execute(SampleResult previousResult,Sampler currentSampler);
  +	public String execute(SampleResult previousResult,Sampler currentSampler)
  +			throws InvalidVariableException;
   	
   	public void setParameters(String parameters) throws InvalidVariableException;
   	
  
  
  
  1.2       +26 -38    jakarta-jmeter/src_1/org/apache/jmeter/protocol/all/modifier/RegexFunction.java
  
  Index: RegexFunction.java
  ===================================================================
  RCS file: /home/cvs/jakarta-jmeter/src_1/org/apache/jmeter/protocol/all/modifier/RegexFunction.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- RegexFunction.java	12 Jul 2002 02:00:46 -0000	1.1
  +++ RegexFunction.java	13 Jul 2002 03:17:48 -0000	1.2
  @@ -3,6 +3,7 @@
   import java.net.URLDecoder;
   import java.net.URLEncoder;
   import java.util.ArrayList;
  +import java.util.Collection;
   import java.util.Iterator;
   import java.util.LinkedList;
   import java.util.List;
  @@ -28,13 +29,13 @@
    * To change this generated comment edit the template variable "typecomment":
    * Window>Preferences>Java>Templates.
    */
  -public class RegexFunction implements Function {
  +public class RegexFunction extends AbstractFunction {
   	
   	public static final String ALL = "ALL";
   	public static final String RAND = "RAND";
   	public static final String KEY = "__regexFunction";	
   	
  -	Random rand = new Random();
  +	private static Random rand = new Random();
   	Pattern searchPattern;
   	Object[] template;
   	String valueIndex,defaultValue,between;
  @@ -56,6 +57,10 @@
   	 */
   	public String execute(SampleResult previousResult,Sampler currentSampler) 
   	{
  +		if(previousResult == null || previousResult.getResponseData() == null)
  +		{
  +			return defaultValue;
  +		}
   		List collectAllMatches = new ArrayList();
   		try {
   			PatternMatcher matcher = new Perl5Matcher();
  @@ -131,52 +136,35 @@
   	
   	public void setParameters(String params) throws InvalidVariableException
   	{
  -		StringTokenizer tk = new StringTokenizer(params,",",true);
  -		valueIndex = "1";
  -		between = "";
  -		defaultValue = "";
  -		String temp = null;
  -		try {
  -			searchPattern = compiler.compile(URLDecoder.decode(tk.nextToken()));
  -		} catch(MalformedPatternException e) {
  -			throw new InvalidVariableException(e.getMessage());
  -		}
  -		tk.nextToken();
  -		generateTemplate(URLDecoder.decode(tk.nextToken()));
  -		if(tk.hasMoreTokens())
  +		try
   		{
  -			tk.nextToken();
  -			temp = tk.nextToken();
  -			if(!temp.equals(","))
  +			Iterator tk = parseArguments(params).iterator();
  +			valueIndex = "1";
  +			between = "";
  +			defaultValue = URLDecoder.decode(params);
  +			searchPattern = compiler.compile((String)tk.next());			
  +			generateTemplate((String)tk.next());
  +			if(tk.hasNext())
   			{
  -				valueIndex = temp;
  +				valueIndex = (String)tk.next();
   			}
  -		}
  -		if(tk.hasMoreTokens())
  -		{
  -			if(!temp.equals(","))
  +			if(tk.hasNext())
   			{
  -				tk.nextToken();
  +				between = (String)tk.next();
   			}
  -			temp = tk.nextToken();
  -			if(!temp.equals(","))
  +			if(tk.hasNext())
   			{
  -				between = URLDecoder.decode(temp);
  +				defaultValue = (String)tk.next();
   			}
  +		} catch(MalformedPatternException e) {
  +				e.printStackTrace();
  +				throw new InvalidVariableException("Bad regex pattern");
   		}
  -		if(tk.hasMoreTokens())
  +		catch(Exception e)
   		{
  -			if(!temp.equals(","))
  -			{
  -				tk.nextToken();
  -			}
  -			temp = tk.nextToken();
  -			if(!temp.equals(","))
  -			{
  -				defaultValue = URLDecoder.decode(temp);
  -			}
  +			throw new InvalidVariableException(e.getMessage());
   		}
  -	}	
  +	}
   	
   	private void generateTemplate(String rawTemplate)
   	{
  
  
  
  1.1                  jakarta-jmeter/src_1/org/apache/jmeter/protocol/all/modifier/AbstractFunction.java
  
  Index: AbstractFunction.java
  ===================================================================
  package org.apache.jmeter.protocol.all.modifier;
  
  import java.net.URLDecoder;
  import java.util.Collection;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.StringTokenizer;
  
  import org.apache.jmeter.samplers.SampleResult;
  import org.apache.jmeter.samplers.Sampler;
  
  /**
   * @author mstover
   *
   * To change this generated comment edit the template variable "typecomment":
   * Window>Preferences>Java>Templates.
   */
  public abstract class AbstractFunction implements Function {
  
  	/**
  	 * @see Function#execute(SampleResult, Sampler)
  	 */
  	abstract public String execute(SampleResult previousResult, Sampler currentSampler) 
  			throws InvalidVariableException;
  
  	/**
  	 * @see Function#setParameters(String)
  	 */
  	abstract public void setParameters(String parameters) throws InvalidVariableException;
  
  	/**
  	 * @see Function#getReferenceKey()
  	 */
  	abstract public String getReferenceKey();	
  	
  	/**
  	 * Provides a convenient way to parse the given argument string into a collection of
  	 * individual arguments.  Takes care of splitting the string based on commas, generates
  	 * blank strings for values between adjacent commas, and decodes the string using URLDecoder.
  	 */
  	protected Collection parseArguments(String params)
  	{
  		StringTokenizer tk = new StringTokenizer(params,",",true);
  		List arguments = new LinkedList();
  		String previous = "";
  		while(tk.hasMoreTokens())
  		{
  			String arg = tk.nextToken();
  			if(arg.equals(",") && previous.equals(","))
  			{
  				arguments.add("");
  			}
  			else if(!arg.equals(","))
  			{
  				arguments.add(URLDecoder.decode(arg));
  			}
  			previous = arg;
  		}
  		return arguments;
  	}
  
  }
  
  
  
  1.6       +165 -9    jakarta-jmeter/src_1/org/apache/jmeter/threads/TestCompiler.java
  
  Index: TestCompiler.java
  ===================================================================
  RCS file: /home/cvs/jakarta-jmeter/src_1/org/apache/jmeter/threads/TestCompiler.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- TestCompiler.java	21 May 2002 03:36:44 -0000	1.5
  +++ TestCompiler.java	13 Jul 2002 03:17:48 -0000	1.6
  @@ -1,12 +1,21 @@
   package org.apache.jmeter.threads;
  -import java.util.*;
  -import org.apache.jmeter.config.Arguments;
  +import java.util.Collection;
  +import java.util.HashMap;
  +import java.util.HashSet;
  +import java.util.Iterator;
  +import java.util.LinkedList;
  +import java.util.List;
  +import java.util.Map;
  +import java.util.Set;
  +
   import org.apache.jmeter.assertions.Assertion;
  -import org.apache.jmeter.timers.Timer;
  +import org.apache.jmeter.config.Arguments;
   import org.apache.jmeter.config.ConfigTestElement;
   import org.apache.jmeter.config.Modifier;
   import org.apache.jmeter.config.ResponseBasedModifier;
   import org.apache.jmeter.control.GenericController;
  +import org.apache.jmeter.protocol.all.modifier.Function;
  +import org.apache.jmeter.protocol.all.modifier.InvalidVariableException;
   import org.apache.jmeter.protocol.http.sampler.HTTPSampler;
   import org.apache.jmeter.samplers.SampleEvent;
   import org.apache.jmeter.samplers.SampleListener;
  @@ -14,6 +23,7 @@
   import org.apache.jmeter.samplers.Sampler;
   import org.apache.jmeter.testelement.PerSampleClonable;
   import org.apache.jmeter.testelement.TestElement;
  +import org.apache.jmeter.timers.Timer;
   import org.apache.jmeter.util.ListedHashTree;
   import org.apache.jmeter.util.ListedHashTreeVisitor;
   
  @@ -37,8 +47,10 @@
   {
   	LinkedList stack = new LinkedList();
   	Map samplerConfigs = new HashMap();
  +	Set objectsWithFunctions = new HashSet();
   	ListedHashTree testTree;
   	SampleResult previousResult;
  +	Sampler currentSampler;
   	private static Set pairing = new HashSet();
   
   	/****************************************
  @@ -91,12 +103,17 @@
   	 ***************************************/
   	public SamplePackage configureSampler(Sampler sampler)
   	{
  +		currentSampler = sampler;
   		SamplePackage ret = new SamplePackage();
   		Sampler clonedSampler = sampler;
   		if(sampler instanceof PerSampleClonable)
   		{
   			clonedSampler = (Sampler)((PerSampleClonable)sampler).clone();
   		}
  +		if(objectsWithFunctions.contains(sampler))
  +		{
  +			replaceValues(clonedSampler);
  +		}
   		ret.setSampler(clonedSampler);
   		ret.addSampleListener(this);
   		Iterator iter = ((List)samplerConfigs.get(sampler)).iterator();
  @@ -154,9 +171,18 @@
   			Iterator iter = testTree.list(stack.subList(0, i)).iterator();
   			while(iter.hasNext())
   			{
  -				configs.add(iter.next());
  +				TestElement item = (TestElement)iter.next();
  +				if(hasFunctions(item))
  +				{
  +					objectsWithFunctions.add(item);
  +				}
  +				configs.add(item);
   			}
   		}
  +		if(hasFunctions(sam))
  +		{
  +			objectsWithFunctions.add(sam);
  +		}
   		samplerConfigs.put(sam, configs);
   	}
   
  @@ -259,6 +285,7 @@
   
   	private void layerElement(SamplePackage ret,TestElement config, Sampler clonedSampler)
   	{
  +		boolean replace = objectsWithFunctions.contains(config);
   		if(config instanceof PerSampleClonable)
   		{
   			config = (TestElement)((PerSampleClonable)config).clone();
  @@ -283,6 +310,135 @@
   		{
   			ret.addTimer((Timer)config);
   		}
  +		if(replace && config instanceof PerSampleClonable)
  +		{
  +			replaceValues(config);
  +		}
  +		else if(replace)
  +		{
  +			config = (TestElement)config.clone();
  +			replaceValues(config);
  +		}
   		clonedSampler.addTestElement(config);
   	}
  +	
  +	private boolean hasFunctions(TestElement el)
  +	{
  +		Iterator iter = el.getPropertyNames().iterator();
  +		while(iter.hasNext())
  +		{
  +			String propName = (String)iter.next();
  +			Object propValue = el.getProperty(propName);
  +			if(propValue instanceof Function)
  +			{
  +				return true;
  +			}
  +			else if(propValue instanceof TestElement)
  +			{
  +				if(hasFunctions((TestElement)propValue))
  +				{
  +					return true;
  +				}
  +			}
  +			else if(propValue instanceof Collection)
  +			{
  +				if(hasFunctions((Collection)propValue))
  +				{
  +					return true;
  +				}
  +			}
  +		}
  +		return false;
  +	}
  +	
  +	private boolean hasFunctions(Collection values)
  +	{
  +		Iterator iter = values.iterator();
  +		while(iter.hasNext())
  +		{
  +			Object val = iter.next();
  +			if(val instanceof TestElement)
  +			{
  +				if(hasFunctions((TestElement)val))
  +				{
  +					return true;
  +				}
  +			}
  +			else if(val instanceof Function)
  +			{
  +				return true;
  +			}
  +			else if(val instanceof Collection)
  +			{
  +				if(hasFunctions((Collection)val))
  +				{
  +					return true;
  +				}
  +			}
  +		}
  +		return false;
  +	}	
  +	
  +	private void replaceValues(TestElement el)
  +	{
  +		Iterator iter = el.getPropertyNames().iterator();
  +		while(iter.hasNext())
  +		{
  +			String propName = (String)iter.next();
  +			Object propValue = el.getProperty(propName);
  +			if(propValue instanceof Function)
  +			{
  +				try
  +				{
  +					el.setProperty(propName,((Function)propValue).execute(previousResult,currentSampler));
  +				}
  +				catch(InvalidVariableException e)
  +				{}
  +			}
  +			else if(propValue instanceof TestElement)
  +			{
  +				replaceValues((TestElement)propValue);
  +			}
  +			else if(propValue instanceof Collection)
  +			{
  +				el.setProperty(propName,replaceValues((Collection)propValue));
  +			}
  +		}
  +	}
  +	
  +	private Collection replaceValues(Collection values)
  +	{
  +		Collection newColl = null;
  +		try {
  +			newColl = (Collection)values.getClass().newInstance();
  +		} catch(Exception e) {
  +			e.printStackTrace();
  +			return values;
  +		} 
  +		Iterator iter = values.iterator();
  +		while(iter.hasNext())
  +		{
  +			Object val = iter.next();
  +			if(val instanceof TestElement)
  +			{
  +				replaceValues((TestElement)val);
  +			}
  +			else if(val instanceof Function)
  +			{
  +				try
  +				{
  +					val = ((Function)val).execute(previousResult,currentSampler);
  +				}
  +				catch(InvalidVariableException e)
  +				{}
  +			}
  +			else if(val instanceof Collection)
  +			{
  +				val = replaceValues((Collection)val);
  +			}
  +			newColl.add(val);
  +		}
  +		return newColl;
  +	}
  +
   }
  
  
  
  1.4       +1 -1      jakarta-jmeter/xdocs/usermanual/glossary.xml
  
  Index: glossary.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-jmeter/xdocs/usermanual/glossary.xml,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- glossary.xml	24 Feb 2002 18:17:18 -0000	1.3
  +++ glossary.xml	13 Jul 2002 03:17:49 -0000	1.4
  @@ -7,7 +7,7 @@
   
   <body>
   
  -<section name="14. Glossary" anchor="glossary">
  +<section name="15. Glossary" anchor="glossary">
   </section>
   
   </body>
  
  
  
  1.23      +2 -1      jakarta-jmeter/xdocs/usermanual/index.xml
  
  Index: index.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-jmeter/xdocs/usermanual/index.xml,v
  retrieving revision 1.22
  retrieving revision 1.23
  diff -u -r1.22 -r1.23
  --- index.xml	26 Jun 2002 00:59:49 -0000	1.22
  +++ index.xml	13 Jul 2002 03:17:49 -0000	1.23
  @@ -106,7 +106,8 @@
     <li><a href="boss.html">12. Help! My boss wants me to load test our web app!</a></li>
   
     <li><a href="component_reference.html">13. Component Reference</a></li>
  -  <li><a href="glossary.html">14. Glossary</a></li>
  +  <li><a href="functions.html">14. Functions</a></li>
  +  <li><a href="glossary.html">15. Glossary</a></li>
   </ul>
   
   </section>
  
  
  
  1.1                  jakarta-jmeter/xdocs/usermanual/functions.xml
  
  Index: functions.xml
  ===================================================================
  <?xml version="1.0"?>
  <document>
  
  <properties>
    <title>User's Manual: Introduction</title>
  </properties>
  
  <body>
  
  <section name="14. Functions" anchor="functions">
  <p>
  JMeter functions are special values that can populate fields of any Sampler or other configuration
  element in a test tree.  A function looks like this:</p>
  
  <p><code>${__functionName(var1,var2,var3)}</code></p>
  
  <p>Where "__functionName" matches the name of an existing built-in or user-defined function.<br/>
  Parentheses surround the parameters sent to the function.  The actual parameters vary from function 
  to function.  Functions that require no parameters can leave off the parentheses.  The function itself
  is wrapped in ${}.</p>
  
  
  <subsection name="14.1 What can functions do" anchor="what_can_do">
  <p>There are two kinds of functions: user-defined static values, and built-in functions.<br/>
  User-defined static values allows the user to define variables to be replaced with their static value when
  a test tree is compiled and submitted to be run.  This replacement happens once at the beginning of the test
  run.  This could be used to replace the DOMAIN field of all HTTP requests, for example - making it a simple 
  matter to change a test to target a different server with the same test.
  </p>
  <p>This type of replacement is possible without functions, but was less convenient and less intuitive.
  It required users to create default config elements that would fill in blank values of Samplers.  User-defined
  functions allows one to replace only part of any given value, not just filling in blank values.</p>
  <p>
  With built-in functions users can compute new values at run-time based on previous response data, which
  thread the function is in, the time, and many other sources.  These values are generated fresh for every
  request throughout the course of the test. </p>
  </subsection>
  
  <subsection name="14.2 Where can functions be used?" anchor="where">
  <p>A user-defined function can be written into any field of any test component.  Some fields do not allow random strings 
  because they are expecting numbers, and thus will not accept a function.  However, most fields will allow
  functions.</p>
  <p>Built-in functions can be written into any field of non-controller test components. This includes
  Samplers, Timers, Listeners, Modifiers, Assertions, and Config Elements.</p>
  </subsection>
  
  <subsection name="14.3 Writing the function string" anchor="how">
  <p>User-defined functions take the form: <code>${varName}</code>.  In the TestPlan tree element, a two-column table
  of user-defined values is kept, matching up variable names with static values.  Referencing the
  variable in a test element is done by bracketing the variable name with '${' and '}'.</p>
  <p>Built-in functions are written in the same manner, but by convention, the names of built-in
  parameters begin with "__" to avoid conflict with user value names<sup>*</sup>.  Some functions take arguments to
  configure them, and these go in parentheses, comma-delimited.  If the function takes no arguments, the parentheses can
  be left out.  A further complication for argument values that themselves contain commas is that the value
  should be encoded the way HTTP parameters are encoded.  JMeter provides a tool to help you construct
  function calls for various built-in functions, which you can then copy-paste.  If your argument values
  do not contain commas, encoding is not required.</p>
  <note><sup>*</sup>If you define a user-defined static variable with the same name as a built-in function, your static
  variable will override the built-in function.</note>
  </subsection>
  
  </section>
  
  </body>
  </document>
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>