Home » jakarta-jmeter-2.3.4_src » org.apache.jmeter.extractor » [javadoc | source]

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *   http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    *
   17    */
   18   package org.apache.jmeter.extractor;
   19   
   20   import java.io.ByteArrayInputStream;
   21   import java.io.IOException;
   22   import java.io.Serializable;
   23   import java.io.UnsupportedEncodingException;
   24   
   25   import javax.xml.parsers.ParserConfigurationException;
   26   import javax.xml.transform.TransformerException;
   27   
   28   import org.apache.jmeter.assertions.AssertionResult;
   29   import org.apache.jmeter.processor.PostProcessor;
   30   import org.apache.jmeter.samplers.SampleResult;
   31   import org.apache.jmeter.testelement.AbstractTestElement;
   32   import org.apache.jmeter.testelement.property.BooleanProperty;
   33   import org.apache.jmeter.threads.JMeterContext;
   34   import org.apache.jmeter.threads.JMeterVariables;
   35   import org.apache.jmeter.util.TidyException;
   36   import org.apache.jmeter.util.XPathUtil;
   37   import org.apache.jorphan.logging.LoggingManager;
   38   import org.apache.jorphan.util.JMeterError;
   39   import org.apache.jorphan.util.JOrphanUtils;
   40   import org.apache.log.Logger;
   41   import org.apache.xpath.XPathAPI;
   42   import org.apache.xpath.objects.XObject;
   43   import org.w3c.dom.Document;
   44   import org.w3c.dom.Element;
   45   import org.w3c.dom.Node;
   46   import org.w3c.dom.NodeList;
   47   import org.xml.sax.SAXException;
   48   
   49   //@see org.apache.jmeter.extractor.TestXPathExtractor for unit tests
   50   
   51   /**
   52    * Extracts text from (X)HTML response using XPath query language
   53    * Example XPath queries:
   54    * <dl>
   55    * <dt>/html/head/title</dt>
   56    *     <dd>extracts Title from HTML response</dd>
   57    * <dt>//form[@name='countryForm']//select[@name='country']/option[text()='Czech Republic'])/@value
   58    *     <dd>extracts value attribute of option element that match text 'Czech Republic'
   59    *                 inside of select element with name attribute  'country' inside of
   60    *                 form with name attribute 'countryForm'</dd>
   61    * </dl>
   62    */
   63    /* This file is inspired by RegexExtractor.
   64    * author <a href="mailto:hpaluch@gitus.cz">Henryk Paluch</a>
   65    *            of <a href="http://www.gitus.com">Gitus a.s.</a>
   66    *
   67    * See Bugzilla: 37183
   68    */
   69   public class XPathExtractor extends AbstractTestElement implements
   70           PostProcessor, Serializable {
   71       private static final Logger log = LoggingManager.getLoggerForClass();
   72       private static final String MATCH_NR = "matchNr"; // $NON-NLS-1$
   73   
   74       //+ JMX file attributes
   75       private static final String XPATH_QUERY     = "XPathExtractor.xpathQuery"; // $NON-NLS-1$
   76       private static final String REFNAME         = "XPathExtractor.refname"; // $NON-NLS-1$
   77       private static final String DEFAULT         = "XPathExtractor.default"; // $NON-NLS-1$
   78       private static final String TOLERANT        = "XPathExtractor.tolerant"; // $NON-NLS-1$
   79       private static final String NAMESPACE       = "XPathExtractor.namespace"; // $NON-NLS-1$
   80       private static final String QUIET           = "XPathExtractor.quiet"; // $NON-NLS-1$
   81       private static final String REPORT_ERRORS   = "XPathExtractor.report_errors"; // $NON-NLS-1$
   82       private static final String SHOW_WARNINGS   = "XPathExtractor.show_warnings"; // $NON-NLS-1$
   83       //- JMX file attributes
   84   
   85   
   86       private String concat(String s1,String s2){
   87           return new StringBuffer(s1).append("_").append(s2).toString(); // $NON-NLS-1$
   88       }
   89   
   90       /**
   91        * Do the job - extract value from (X)HTML response using XPath Query.
   92        * Return value as variable defined by REFNAME. Returns DEFAULT value
   93        * if not found.
   94        */
   95       public void process() {
   96           JMeterContext context = getThreadContext();
   97           JMeterVariables vars = context.getVariables();
   98           String refName = getRefName();
   99           vars.put(refName, getDefaultValue());
  100           vars.put(concat(refName,MATCH_NR), "0"); // In case parse fails // $NON-NLS-1$
  101           vars.remove(concat(refName,"1")); // In case parse fails // $NON-NLS-1$
  102           final SampleResult previousResult = context.getPreviousResult();
  103   
  104           try{
  105               Document d = parseResponse(previousResult);
  106               getValuesForXPath(d,getXPathQuery(),vars, refName);
  107           }catch(IOException e){// Should not happen
  108               final String errorMessage = "error on ("+getXPathQuery()+")";
  109               log.error(errorMessage,e);
  110               throw new JMeterError(errorMessage,e);
  111           } catch (ParserConfigurationException e) {// Should not happen
  112               final String errrorMessage = "error on ("+getXPathQuery()+")";
  113               log.error(errrorMessage,e);
  114               throw new JMeterError(errrorMessage,e);
  115           } catch (SAXException e) {// Can happen for bad input document
  116               log.warn("error on ("+getXPathQuery()+")"+e.getLocalizedMessage());
  117           } catch (TransformerException e) {// Can happen for incorrect XPath expression
  118               log.warn("error on ("+getXPathQuery()+")"+e.getLocalizedMessage());
  119           } catch (TidyException e) {
  120               AssertionResult ass = new AssertionResult("TidyException"); // $NON-NLS-1$
  121               ass.setFailure(true);
  122               ass.setFailureMessage(e.getMessage());
  123               previousResult.addAssertionResult(ass);
  124               previousResult.setSuccessful(false);
  125           }
  126       }
  127   
  128      public Object clone() {
  129           XPathExtractor cloned = (XPathExtractor) super.clone();
  130           return cloned;
  131       }
  132   
  133       /*============= object properties ================*/
  134       public void setXPathQuery(String val){
  135           setProperty(XPATH_QUERY,val);
  136       }
  137   
  138       public String getXPathQuery(){
  139           return getPropertyAsString(XPATH_QUERY);
  140       }
  141   
  142       public void setRefName(String refName) {
  143           setProperty(REFNAME, refName);
  144       }
  145   
  146       public String getRefName() {
  147           return getPropertyAsString(REFNAME);
  148       }
  149   
  150       public void setDefaultValue(String val) {
  151           setProperty(DEFAULT, val);
  152       }
  153   
  154       public String getDefaultValue() {
  155           return getPropertyAsString(DEFAULT);
  156       }
  157   
  158       public void setTolerant(boolean val) {
  159           setProperty(new BooleanProperty(TOLERANT, val));
  160       }
  161   
  162       public boolean isTolerant() {
  163           return getPropertyAsBoolean(TOLERANT);
  164       }
  165   
  166       public void setNameSpace(boolean val) {
  167           setProperty(new BooleanProperty(NAMESPACE, val));
  168       }
  169   
  170       public boolean useNameSpace() {
  171           return getPropertyAsBoolean(NAMESPACE);
  172       }
  173   
  174       public void setReportErrors(boolean val) {
  175               setProperty(REPORT_ERRORS, val, false);
  176       }
  177   
  178       public boolean reportErrors() {
  179           return getPropertyAsBoolean(REPORT_ERRORS, false);
  180       }
  181   
  182       public void setShowWarnings(boolean val) {
  183           setProperty(SHOW_WARNINGS, val, false);
  184       }
  185   
  186       public boolean showWarnings() {
  187           return getPropertyAsBoolean(SHOW_WARNINGS, false);
  188       }
  189   
  190       public void setQuiet(boolean val) {
  191           setProperty(QUIET, val, true);
  192       }
  193   
  194       public boolean isQuiet() {
  195           return getPropertyAsBoolean(QUIET, true);
  196       }
  197   
  198       /*================= internal business =================*/
  199       /**
  200        * Converts (X)HTML response to DOM object Tree.
  201        * This version cares of charset of response.
  202        * @param result
  203        * @return
  204        *
  205        */
  206       private Document parseResponse(SampleResult result)
  207         throws UnsupportedEncodingException, IOException, ParserConfigurationException,SAXException,TidyException
  208       {
  209         //TODO: validate contentType for reasonable types?
  210   
  211         //TODO: is it really necessary to recode the data?
  212         // NOTE: responseData encoding is server specific
  213         //       Therefore we do byte -> unicode -> byte conversion
  214         //       to ensure UTF-8 encoding as required by XPathUtil
  215         String unicodeData = result.getResponseDataAsString();
  216         // convert unicode String -> UTF-8 bytes
  217         byte[] utf8data = unicodeData.getBytes("UTF-8"); // $NON-NLS-1$
  218         ByteArrayInputStream in = new ByteArrayInputStream(utf8data);
  219         boolean isXML = JOrphanUtils.isXML(utf8data);
  220         // this method assumes UTF-8 input data
  221         return XPathUtil.makeDocument(in,false,false,useNameSpace(),isTolerant(),isQuiet(),showWarnings(),reportErrors(),isXML);
  222       }
  223   
  224       /**
  225        * Extract value from Document d by XPath query.
  226        * @param d
  227        * @param query
  228        * @throws TransformerException
  229        */
  230       private void getValuesForXPath(Document d,String query, JMeterVariables vars, String refName)
  231        throws TransformerException
  232       {
  233           String val = null;
  234            XObject xObject = XPathAPI.eval(d, query);
  235           final int objectType = xObject.getType();
  236           if (objectType == XObject.CLASS_NODESET) {
  237               NodeList matches = xObject.nodelist();
  238               int length = matches.getLength();
  239               vars.put(concat(refName,MATCH_NR), String.valueOf(length));
  240               for (int i = 0 ; i < length; i++) {
  241                   Node match = matches.item(i);
  242                   if ( match instanceof Element){
  243                   // elements have empty nodeValue, but we are usually interested in their content
  244                      final Node firstChild = match.getFirstChild();
  245                      if (firstChild != null) {
  246                          val = firstChild.getNodeValue();
  247                      } else {
  248                          val = match.getNodeValue(); // TODO is this correct?
  249                      }
  250                   } else {
  251                      val = match.getNodeValue();
  252                   }
  253                   if ( val!=null){
  254                       if (i==0) {// Treat 1st match specially
  255                           vars.put(refName,val);
  256                       }
  257                       vars.put(concat(refName,String.valueOf(i+1)),val);
  258                   }
  259               }
  260               vars.remove(concat(refName,String.valueOf(length+1)));
  261           } else if (objectType == XObject.CLASS_NULL
  262                   || objectType == XObject.CLASS_UNKNOWN
  263                   || objectType == XObject.CLASS_UNRESOLVEDVARIABLE) {
  264               log.warn("Unexpected object type: "+xObject.getTypeString()+" returned for: "+getXPathQuery());
  265            } else {
  266               val = xObject.toString();
  267               vars.put(concat(refName, MATCH_NR), "1");
  268               vars.put(refName, val);
  269               vars.put(concat(refName, "1"), val);
  270               vars.remove(concat(refName, "2"));
  271           }
  272       }
  273   }

Home » jakarta-jmeter-2.3.4_src » org.apache.jmeter.extractor » [javadoc | source]