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 se...@apache.org on 2006/04/01 12:22:09 UTC

svn commit: r390640 - in /jakarta/jmeter/branches/rel-2-1: src/components/org/apache/jmeter/extractor/XPathExtractor.java test/src/org/apache/jmeter/extractor/TestXPathExtractor.java xdocs/changes.xml xdocs/usermanual/component_reference.xml

Author: sebb
Date: Sat Apr  1 02:22:07 2006
New Revision: 390640

URL: http://svn.apache.org/viewcvs?rev=390640&view=rev
Log:
Add multiple node XPath extraction

Added:
    jakarta/jmeter/branches/rel-2-1/test/src/org/apache/jmeter/extractor/TestXPathExtractor.java   (with props)
Modified:
    jakarta/jmeter/branches/rel-2-1/src/components/org/apache/jmeter/extractor/XPathExtractor.java
    jakarta/jmeter/branches/rel-2-1/xdocs/changes.xml
    jakarta/jmeter/branches/rel-2-1/xdocs/usermanual/component_reference.xml

Modified: jakarta/jmeter/branches/rel-2-1/src/components/org/apache/jmeter/extractor/XPathExtractor.java
URL: http://svn.apache.org/viewcvs/jakarta/jmeter/branches/rel-2-1/src/components/org/apache/jmeter/extractor/XPathExtractor.java?rev=390640&r1=390639&r2=390640&view=diff
==============================================================================
--- jakarta/jmeter/branches/rel-2-1/src/components/org/apache/jmeter/extractor/XPathExtractor.java (original)
+++ jakarta/jmeter/branches/rel-2-1/src/components/org/apache/jmeter/extractor/XPathExtractor.java Sat Apr  1 02:22:07 2006
@@ -37,8 +37,11 @@
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
 import org.xml.sax.SAXException;
 
+//@see org.apache.jmeter.extractor.TestXPathExtractor for unit tests
+
 /**
  * Extracts text from (X)HTML response using XPath query language
  * Example XPath queries:
@@ -59,7 +62,8 @@
  */
 public class XPathExtractor extends AbstractTestElement implements
         PostProcessor, Serializable {
-	transient private static Logger log = LoggingManager.getLoggerForClass();
+	private static final String MATCH_NR = "matchNr";
+    transient private static Logger log = LoggingManager.getLoggerForClass();
 	protected static final String KEY_PREFIX = "XPathExtractor.";
 	public static final String XPATH_QUERY = KEY_PREFIX +"xpathQuery";
 	public static final String REFNAME = KEY_PREFIX +"refname";
@@ -67,6 +71,10 @@
 	public static final String TOLERANT = KEY_PREFIX +"tolerant";
 
 
+    private String concat(String s1,String s2){
+        return new StringBuffer(s1).append("_").append(s2).toString();
+    }
+    
 	/**
 	 * Do the job - extract value from (X)HTML response using XPath Query.
 	 * Return value as variable defined by REFNAME. Returns DEFAULT value
@@ -77,25 +85,22 @@
 		JMeterVariables vars = context.getVariables();
 		String refName = getRefName();
 		vars.put(refName, getDefaultValue());
+        vars.put(concat(refName,MATCH_NR), "0"); // In case parse fails
+        vars.remove(concat(refName,"1")); // In case parse fails
 
 		try{			
 			Document d = parseResponse(context.getPreviousResult());		
-			String val = getValueForXPath(d,getXPathQuery());
-			if ( val!=null){
-			    vars.put(getRefName(),val);
-			}
-		}catch(IOException e){
+			getValuesForXPath(d,getXPathQuery(),vars, refName);
+		}catch(IOException e){// Should not happen
 			log.error("error on "+XPATH_QUERY+"("+getXPathQuery()+")",e);
 			throw new RuntimeException(e);
-		} catch (ParserConfigurationException e) {
-			log.error("error on "+XPATH_QUERY+"("+getXPathQuery()+")",e);
-			throw new RuntimeException(e);
-		} catch (SAXException e) {
-			log.error("error on "+XPATH_QUERY+"("+getXPathQuery()+")",e);
-			throw new RuntimeException(e);
-		} catch (TransformerException e) {
+		} catch (ParserConfigurationException e) {// Should not happen
 			log.error("error on "+XPATH_QUERY+"("+getXPathQuery()+")",e);
 			throw new RuntimeException(e);
+		} catch (SAXException e) {// Can happen for bad input document
+			log.warn("error on "+XPATH_QUERY+"("+getXPathQuery()+")"+e.getLocalizedMessage());
+		} catch (TransformerException e) {// Can happen for incorrect XPath expression
+			log.warn("error on "+XPATH_QUERY+"("+getXPathQuery()+")"+e.getLocalizedMessage());
 		}
     }    
             
@@ -173,12 +178,15 @@
      *  data were not found
      * @throws TransformerException
      */
-    private String getValueForXPath(Document d,String query)
+    private void getValuesForXPath(Document d,String query, JMeterVariables vars, String refName)
      throws TransformerException
     {
-		String val = null;
-		Node match = XPathAPI.selectSingleNode(d,query);
-		if ( match!=null){
+     	String val = null;
+		NodeList matches = XPathAPI.selectNodeList(d,query);
+		int length = matches.getLength();
+        vars.put(concat(refName,MATCH_NR), String.valueOf(length));
+        for (int i = 0 ; i < length; i++) {
+            Node match = matches.item(i);
 			if ( match instanceof Element){
 			   // elements have empty nodeValue, but we are usually
 			   // interested in their content
@@ -186,11 +194,14 @@
 			} else {				
 			   val = match.getNodeValue();
 			}
+            if ( val!=null){
+                if (i==0) {// Treat 1st match specially
+                    vars.put(refName,val);                    
+                }
+                vars.put(concat(refName,String.valueOf(i+1)),val);
+            }
 		}
-		if ( log.isDebugEnabled()){
-			log.debug(XPATH_QUERY+"("+query+") is '"+val+"'");
-		}
-		return val;
+        vars.remove(concat(refName,String.valueOf(length+1)));
     }
     
 }

Added: jakarta/jmeter/branches/rel-2-1/test/src/org/apache/jmeter/extractor/TestXPathExtractor.java
URL: http://svn.apache.org/viewcvs/jakarta/jmeter/branches/rel-2-1/test/src/org/apache/jmeter/extractor/TestXPathExtractor.java?rev=390640&view=auto
==============================================================================
--- jakarta/jmeter/branches/rel-2-1/test/src/org/apache/jmeter/extractor/TestXPathExtractor.java (added)
+++ jakarta/jmeter/branches/rel-2-1/test/src/org/apache/jmeter/extractor/TestXPathExtractor.java Sat Apr  1 02:22:07 2006
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2003-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+
+package org.apache.jmeter.extractor;
+
+
+import junit.framework.TestCase;
+
+import org.apache.jmeter.samplers.SampleResult;
+import org.apache.jmeter.threads.JMeterContext;
+import org.apache.jmeter.threads.JMeterContextService;
+import org.apache.jmeter.threads.JMeterVariables;
+
+/**
+ * @version $Revision$
+ */
+public class TestXPathExtractor extends TestCase {
+		XPathExtractor extractor;
+
+		SampleResult result;
+
+		JMeterVariables vars;
+
+		public TestXPathExtractor(String name) {
+			super(name);
+		}
+
+		private JMeterContext jmctx = null;
+
+        private final static String VAL_NAME = "value";
+        private final static String VAL_NAME_NR = "value_matchNr";
+		public void setUp() {
+			jmctx = JMeterContextService.getContext();
+			extractor = new XPathExtractor();
+			extractor.setThreadContext(jmctx);// This would be done by the run command
+			extractor.setRefName(VAL_NAME);
+            extractor.setDefaultValue("Default");
+			result = new SampleResult();
+			String data = "<book><preface>zero</preface><page>one</page><page>two</page></book>";
+			result.setResponseData(data.getBytes());
+			vars = new JMeterVariables();
+			jmctx.setVariables(vars);
+			jmctx.setPreviousResult(result);
+		}
+
+        public void testVariableExtraction() throws Exception {
+			extractor.setXPathQuery("/book/preface");
+			extractor.process();
+			assertEquals("zero", vars.get(VAL_NAME));
+            assertEquals("1", vars.get(VAL_NAME_NR));
+            assertEquals("zero", vars.get(VAL_NAME+"_1"));
+            assertNull(vars.get(VAL_NAME+"_2"));
+            
+            extractor.setXPathQuery("/book/page");
+            extractor.process();
+            assertEquals("one", vars.get(VAL_NAME));
+            assertEquals("2", vars.get(VAL_NAME_NR));
+            assertEquals("one", vars.get(VAL_NAME+"_1"));
+            assertEquals("two", vars.get(VAL_NAME+"_2"));
+            assertNull(vars.get(VAL_NAME+"_3"));
+            
+            extractor.setXPathQuery("/book/page[2]");
+            extractor.process();
+            assertEquals("two", vars.get(VAL_NAME));
+            assertEquals("1", vars.get(VAL_NAME_NR));
+            assertEquals("two", vars.get(VAL_NAME+"_1"));
+            assertNull(vars.get(VAL_NAME+"_2"));
+            assertNull(vars.get(VAL_NAME+"_3"));
+
+            extractor.setXPathQuery("/book/index");
+            extractor.process();
+            assertEquals("Default", vars.get(VAL_NAME));
+            assertEquals("0", vars.get(VAL_NAME_NR));
+            assertNull(vars.get(VAL_NAME+"_1"));
+
+        }
+
+        public void testInvalidXpath() throws Exception {
+            extractor.setXPathQuery("<");
+            extractor.process();
+            assertEquals("Default", vars.get(VAL_NAME));
+            assertEquals("0", vars.get(VAL_NAME_NR));
+		}
+
+        public void testInvalidDocument() throws Exception {
+            result.setResponseData("<z>".getBytes());
+            extractor.setXPathQuery("<");
+            extractor.process();
+            assertEquals("Default", vars.get(VAL_NAME));
+            assertEquals("0", vars.get(VAL_NAME_NR));
+        }
+}

Propchange: jakarta/jmeter/branches/rel-2-1/test/src/org/apache/jmeter/extractor/TestXPathExtractor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/jmeter/branches/rel-2-1/test/src/org/apache/jmeter/extractor/TestXPathExtractor.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision

Propchange: jakarta/jmeter/branches/rel-2-1/test/src/org/apache/jmeter/extractor/TestXPathExtractor.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: jakarta/jmeter/branches/rel-2-1/xdocs/changes.xml
URL: http://svn.apache.org/viewcvs/jakarta/jmeter/branches/rel-2-1/xdocs/changes.xml?rev=390640&r1=390639&r2=390640&view=diff
==============================================================================
--- jakarta/jmeter/branches/rel-2-1/xdocs/changes.xml (original)
+++ jakarta/jmeter/branches/rel-2-1/xdocs/changes.xml Sat Apr  1 02:22:07 2006
@@ -48,7 +48,7 @@
 <h4>New functionality:</h4>
 <ul>
 <li>Report function</li>
-<li>XPath Extractor</li>
+<li>XPath Extractor Post-Processor. Handles single and multiple matches.</li>
 <li>Simpler JMX file format (2.2)</li>
 <li>BeanshellSampler code can update ResponseData directly</li>
 <li>Bug 37490 - Allow UDV as delay in Duration Assertion</li>

Modified: jakarta/jmeter/branches/rel-2-1/xdocs/usermanual/component_reference.xml
URL: http://svn.apache.org/viewcvs/jakarta/jmeter/branches/rel-2-1/xdocs/usermanual/component_reference.xml?rev=390640&r1=390639&r2=390640&view=diff
==============================================================================
--- jakarta/jmeter/branches/rel-2-1/xdocs/usermanual/component_reference.xml (original)
+++ jakarta/jmeter/branches/rel-2-1/xdocs/usermanual/component_reference.xml Sat Apr  1 02:22:07 2006
@@ -2699,15 +2699,29 @@
 	   </property>
         <property name="Name" required="">Descriptive name for this element that is shown in the tree.</property>
         <property name="Reference Name" required="Yes">The name of the JMeter variable in which to store the result.</property>
-	<property name="XPath Query" required="Yes">Element query in XPath language. </property>
+	<property name="XPath Query" required="Yes">Element query in XPath language. Can return more than one match. </property>
 	<property name="Default Value" required="">Default value returned when no match found</property>
    </properties>
+   <p>To allow for use in a ForEach Controller, the following variables are set on return:</p>
+   <ul>
+   <li>refName - set to first (or only) match; if no match, then set to default</li>
+   <li>refName_matchNr - set to number of matches (may be 0)</li>
+   <li>refName_n - n=1,2,3 etc. Set to the 1st, 2nd 3rd match etc. 
+   </li>
+   </ul>
+   <p>Note: The next refName_n variable is set to null - e.g. if there are 2 matches, then refName_3 is set to null,
+   and if there are no matches, then refName_1 is set to null.
+   </p>
    <p>XPath is query language targeted primarily for XSLT transformations. However it is usefull as generic query language for structured data too. See 
 	   <a href="http://www.topxml.com/xsl/xpathref.asp">XPath Reference</a> or <a href="http://www.w3.org/TR/xpath">XPath specification</a> for more information. Here are few examples:
    </p>
   <dl> 
    <dt>/html/head/title</dt>
      <dd>extracts title element from HTML response</dd>
+   <dt>/book/page[2]</dt>
+     <dd>extracts 2nd page from a book</dd>
+   <dt>/book/page</dt>
+     <dd>extracts all pages from a book</dd>
      <dt>//form[@name='countryForm']//select[@name='country']/option[text()='Czech Republic'])/@value</dt>
     <dd>extracts value attribute of option element that match text 'Czech Republic'
         inside of select element with name attribute  'country' inside of



---------------------------------------------------------------------
To unsubscribe, e-mail: jmeter-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: jmeter-dev-help@jakarta.apache.org