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   
   19   package org.apache.jmeter.extractor;
   20   
   21   import java.io.Serializable;
   22   import java.util.ArrayList;
   23   import java.util.Iterator;
   24   import java.util.LinkedList;
   25   import java.util.List;
   26   
   27   import org.apache.commons.lang.StringEscapeUtils;
   28   import org.apache.jmeter.processor.PostProcessor;
   29   import org.apache.jmeter.samplers.SampleResult;
   30   import org.apache.jmeter.testelement.AbstractTestElement;
   31   import org.apache.jmeter.testelement.property.IntegerProperty;
   32   import org.apache.jmeter.threads.JMeterContext;
   33   import org.apache.jmeter.threads.JMeterVariables;
   34   import org.apache.jmeter.util.JMeterUtils;
   35   import org.apache.jorphan.logging.LoggingManager;
   36   import org.apache.log.Logger;
   37   import org.apache.oro.text.MalformedCachePatternException;
   38   import org.apache.oro.text.regex.MatchResult;
   39   import org.apache.oro.text.regex.Pattern;
   40   import org.apache.oro.text.regex.PatternMatcher;
   41   import org.apache.oro.text.regex.PatternMatcherInput;
   42   import org.apache.oro.text.regex.Perl5Compiler;
   43   import org.apache.oro.text.regex.Perl5Matcher;
   44   import org.apache.oro.text.regex.Util;
   45   
   46   // @see org.apache.jmeter.extractor.TestRegexExtractor for unit tests
   47   
   48   public class RegexExtractor extends AbstractTestElement implements PostProcessor, Serializable {
   49   
   50   
   51       private static final Logger log = LoggingManager.getLoggerForClass();
   52   
   53       // What to match against. N.B. do not change the string value or test plans will break!
   54       private static final String MATCH_AGAINST = "RegexExtractor.useHeaders"; // $NON-NLS-1$
   55       /*
   56        * Permissible values:
   57        *  true - match against headers
   58        *  false or absent - match against body (this was the original default)
   59        *  URL - match against URL
   60        *  These are passed to the setUseField() method
   61        *
   62        *  Do not change these values!
   63       */
   64       public static final String USE_HDRS = "true"; // $NON-NLS-1$
   65       public static final String USE_BODY = "false"; // $NON-NLS-1$
   66       public static final String USE_BODY_UNESCAPED = "unescaped"; // $NON-NLS-1$
   67       public static final String USE_URL = "URL"; // $NON-NLS-1$
   68       public static final String USE_CODE = "code"; // $NON-NLS-1$
   69       public static final String USE_MESSAGE = "message"; // $NON-NLS-1$
   70   
   71   
   72       private static final String REGEX = "RegexExtractor.regex"; // $NON-NLS-1$
   73   
   74       private static final String REFNAME = "RegexExtractor.refname"; // $NON-NLS-1$
   75   
   76       private static final String MATCH_NUMBER = "RegexExtractor.match_number"; // $NON-NLS-1$
   77   
   78       private static final String DEFAULT = "RegexExtractor.default"; // $NON-NLS-1$
   79   
   80       private static final String TEMPLATE = "RegexExtractor.template"; // $NON-NLS-1$
   81   
   82       private static final String REF_MATCH_NR = "_matchNr"; // $NON-NLS-1$
   83   
   84       private static final String UNDERSCORE = "_";  // $NON-NLS-1$
   85   
   86       private Object[] template = null;
   87   
   88       /**
   89        * Parses the response data using regular expressions and saving the results
   90        * into variables for use later in the test.
   91        *
   92        * @see org.apache.jmeter.processor.PostProcessor#process()
   93        */
   94       public void process() {
   95           initTemplate();
   96           JMeterContext context = getThreadContext();
   97           SampleResult previousResult = context.getPreviousResult();
   98           if (previousResult == null) {
   99               return;
  100           }
  101           log.debug("RegexExtractor processing result");
  102   
  103           // Fetch some variables
  104           JMeterVariables vars = context.getVariables();
  105           String refName = getRefName();
  106           int matchNumber = getMatchNumber();
  107   
  108           final String defaultValue = getDefaultValue();
  109           if (defaultValue.length() > 0){// Only replace default if it is provided
  110               vars.put(refName, defaultValue);
  111           }
  112   
  113           Perl5Matcher matcher = JMeterUtils.getMatcher();
  114           String inputString =
  115                 useUrl() ? previousResult.getUrlAsString() // Bug 39707
  116               : useHeaders() ? previousResult.getResponseHeaders()
  117               : useCode() ? previousResult.getResponseCode() //Bug 43451
  118               : useMessage() ? previousResult.getResponseMessage() //Bug 43451
  119               : useUnescapedBody() ? StringEscapeUtils.unescapeHtml(previousResult.getResponseDataAsString())
  120               : previousResult.getResponseDataAsString() // Bug 36898
  121               ;
  122              if (log.isDebugEnabled()) {
  123                  log.debug("Input = " + inputString);
  124              }
  125           PatternMatcherInput input = new PatternMatcherInput(inputString);
  126              String regex = getRegex();
  127           if (log.isDebugEnabled()) {
  128               log.debug("Regex = " + regex);
  129              }
  130           try {
  131               Pattern pattern = JMeterUtils.getPatternCache().getPattern(regex, Perl5Compiler.READ_ONLY_MASK);
  132               List matches = new ArrayList();
  133               int x = 0;
  134               boolean done = false;
  135               do {
  136                   if (matcher.contains(input, pattern)) {
  137                       log.debug("RegexExtractor: Match found!");
  138                       matches.add(matcher.getMatch());
  139                   } else {
  140                       done = true;
  141                   }
  142                   x++;
  143               } while (x != matchNumber && !done);
  144   
  145               int prevCount = 0;
  146               String prevString = vars.get(refName + REF_MATCH_NR);
  147               if (prevString != null) {
  148                   vars.remove(refName + REF_MATCH_NR);// ensure old value is not left defined
  149                   try {
  150                       prevCount = Integer.parseInt(prevString);
  151                   } catch (NumberFormatException e1) {
  152                       log.warn("Could not parse "+prevString+" "+e1);
  153                   }
  154               }
  155               int matchCount=0;// Number of refName_n variable sets to keep
  156               try {
  157                   MatchResult match;
  158                   if (matchNumber >= 0) {// Original match behaviour
  159                       match = getCorrectMatch(matches, matchNumber);
  160                       if (match != null) {
  161                           vars.put(refName, generateResult(match));
  162                           saveGroups(vars, refName, match);
  163                       } else {
  164                           // refname has already been set to the default (if present)
  165                           removeGroups(vars, refName);
  166                       }
  167                   } else // < 0 means we save all the matches
  168                   {
  169                       removeGroups(vars, refName); // remove any single matches
  170                       matchCount = matches.size();
  171                       vars.put(refName + REF_MATCH_NR, Integer.toString(matchCount));// Save the count
  172                       for (int i = 1; i <= matchCount; i++) {
  173                           match = getCorrectMatch(matches, i);
  174                           if (match != null) {
  175                               final String refName_n = new StringBuffer(refName).append(UNDERSCORE).append(i).toString();
  176                               vars.put(refName_n, generateResult(match));
  177                               saveGroups(vars, refName_n, match);
  178                           }
  179                       }
  180                   }
  181                   // Remove any left-over variables
  182                   for (int i = matchCount + 1; i <= prevCount; i++) {
  183                       final String refName_n = new StringBuffer(refName).append(UNDERSCORE).append(i).toString();
  184                       vars.remove(refName_n);
  185                       removeGroups(vars, refName_n);
  186                   }
  187               } catch (RuntimeException e) {
  188                   log.warn("Error while generating result");
  189               }
  190           } catch (MalformedCachePatternException e) {
  191               log.warn("Error in pattern: " + regex);
  192           }
  193       }
  194   
  195       /**
  196        * Creates the variables:<br/>
  197        * basename_gn, where n=0...# of groups<br/>
  198        * basename_g = number of groups (apart from g0)
  199        */
  200       private void saveGroups(JMeterVariables vars, String basename, MatchResult match) {
  201           StringBuffer buf = new StringBuffer();
  202           buf.append(basename);
  203           buf.append("_g"); // $NON-NLS-1$
  204           int pfxlen=buf.length();
  205           String prevString=vars.get(buf.toString());
  206           int previous=0;
  207           if (prevString!=null){
  208               try {
  209                   previous=Integer.parseInt(prevString);
  210               } catch (NumberFormatException e) {
  211                   log.warn("Could not parse "+prevString+" "+e);
  212               }
  213           }
  214           //Note: match.groups() includes group 0
  215           final int groups = match.groups();
  216           for (int x = 0; x < groups; x++) {
  217               buf.append(x);
  218               vars.put(buf.toString(), match.group(x));
  219               buf.setLength(pfxlen);
  220           }
  221           vars.put(buf.toString(), Integer.toString(groups-1));
  222           for (int i = groups; i <= previous; i++){
  223               buf.append(i);
  224               vars.remove(buf.toString());// remove the remaining _gn vars
  225               buf.setLength(pfxlen);
  226           }
  227       }
  228   
  229       /**
  230        * Removes the variables:<br/>
  231        * basename_gn, where n=0...# of groups<br/>
  232        * basename_g = number of groups (apart from g0)
  233        */
  234       private void removeGroups(JMeterVariables vars, String basename) {
  235           StringBuffer buf = new StringBuffer();
  236           buf.append(basename);
  237           buf.append("_g"); // $NON-NLS-1$
  238           int pfxlen=buf.length();
  239           // How many groups are there?
  240           int groups;
  241           try {
  242               groups=Integer.parseInt(vars.get(buf.toString()));
  243           } catch (NumberFormatException e) {
  244               groups=0;
  245           }
  246           vars.remove(buf.toString());// Remove the group count
  247           for (int i = 0; i <= groups; i++) {
  248               buf.append(i);
  249               vars.remove(buf.toString());// remove the g0,g1...gn vars
  250               buf.setLength(pfxlen);
  251           }
  252       }
  253   
  254       public Object clone() {
  255           RegexExtractor cloned = (RegexExtractor) super.clone();
  256           cloned.template = this.template;
  257           return cloned;
  258       }
  259   
  260       private String generateResult(MatchResult match) {
  261           StringBuffer result = new StringBuffer();
  262           for (int a = 0; a < template.length; a++) {
  263               log.debug("RegexExtractor: Template piece #" + a + " = " + template[a]);
  264               if (template[a] instanceof String) {
  265                   result.append(template[a]);
  266               } else {
  267                   result.append(match.group(((Integer) template[a]).intValue()));
  268               }
  269           }
  270           log.debug("Regex Extractor result = " + result.toString());
  271           return result.toString();
  272       }
  273   
  274       private void initTemplate() {
  275           if (template != null) {
  276               return;
  277           }
  278           List pieces = new ArrayList();
  279           List combined = new LinkedList();
  280           String rawTemplate = getTemplate();
  281           PatternMatcher matcher = JMeterUtils.getMatcher();
  282           Pattern templatePattern = JMeterUtils.getPatternCache().getPattern("\\$(\\d+)\\$"  // $NON-NLS-1$
  283                   , Perl5Compiler.READ_ONLY_MASK
  284                   & Perl5Compiler.SINGLELINE_MASK);
  285           log.debug("Pattern = " + templatePattern);
  286           log.debug("template = " + rawTemplate);
  287           Util.split(pieces, matcher, templatePattern, rawTemplate);
  288           PatternMatcherInput input = new PatternMatcherInput(rawTemplate);
  289           boolean startsWith = isFirstElementGroup(rawTemplate);
  290           log.debug("template split into " + pieces.size() + " pieces, starts with = " + startsWith);
  291           if (startsWith) {
  292               pieces.remove(0);// Remove initial empty entry
  293           }
  294           Iterator iter = pieces.iterator();
  295           while (iter.hasNext()) {
  296               boolean matchExists = matcher.contains(input, templatePattern);
  297               if (startsWith) {
  298                   if (matchExists) {
  299                       combined.add(new Integer(matcher.getMatch().group(1)));
  300                   }
  301                   combined.add(iter.next());
  302               } else {
  303                   combined.add(iter.next());
  304                   if (matchExists) {
  305                       combined.add(new Integer(matcher.getMatch().group(1)));
  306                   }
  307               }
  308           }
  309           if (matcher.contains(input, templatePattern)) {
  310               log.debug("Template does end with template pattern");
  311               combined.add(new Integer(matcher.getMatch().group(1)));
  312           }
  313           template = combined.toArray();
  314       }
  315   
  316       private boolean isFirstElementGroup(String rawData) {
  317           try {
  318               Pattern pattern = JMeterUtils.getPatternCache().getPattern("^\\$\\d+\\$" // $NON-NLS-1$
  319                       , Perl5Compiler.READ_ONLY_MASK
  320                       & Perl5Compiler.SINGLELINE_MASK);
  321               return (JMeterUtils.getMatcher()).contains(rawData, pattern);
  322           } catch (RuntimeException e) {
  323               log.error("", e);
  324               return false;
  325           }
  326       }
  327   
  328       /**
  329        * Grab the appropriate result from the list.
  330        *
  331        * @param matches
  332        *            list of matches
  333        * @param entry
  334        *            the entry number in the list
  335        * @return MatchResult
  336        */
  337       private MatchResult getCorrectMatch(List matches, int entry) {
  338           int matchSize = matches.size();
  339   
  340           if (matchSize <= 0 || entry > matchSize){
  341               return null;
  342           }
  343   
  344           if (entry == 0) // Random match
  345           {
  346               return (MatchResult) matches.get(JMeterUtils.getRandomInt(matchSize));
  347           }
  348   
  349           return (MatchResult) matches.get(entry - 1);
  350       }
  351   
  352       public void setRegex(String regex) {
  353           setProperty(REGEX, regex);
  354       }
  355   
  356       public String getRegex() {
  357           return getPropertyAsString(REGEX);
  358       }
  359   
  360       public void setRefName(String refName) {
  361           setProperty(REFNAME, refName);
  362       }
  363   
  364       public String getRefName() {
  365           return getPropertyAsString(REFNAME);
  366       }
  367   
  368       /**
  369        * Set which Match to use. This can be any positive number, indicating the
  370        * exact match to use, or 0, which is interpreted as meaning random.
  371        *
  372        * @param matchNumber
  373        */
  374       public void setMatchNumber(int matchNumber) {
  375           setProperty(new IntegerProperty(MATCH_NUMBER, matchNumber));
  376       }
  377   
  378       public void setMatchNumber(String matchNumber) {
  379           setProperty(MATCH_NUMBER, matchNumber);
  380       }
  381   
  382       public int getMatchNumber() {
  383           return getPropertyAsInt(MATCH_NUMBER);
  384       }
  385   
  386       public String getMatchNumberAsString() {
  387           return getPropertyAsString(MATCH_NUMBER);
  388       }
  389   
  390       /**
  391        * Sets the value of the variable if no matches are found
  392        *
  393        * @param defaultValue
  394        */
  395       public void setDefaultValue(String defaultValue) {
  396           setProperty(DEFAULT, defaultValue);
  397       }
  398   
  399       public String getDefaultValue() {
  400           return getPropertyAsString(DEFAULT);
  401       }
  402   
  403       public void setTemplate(String template) {
  404           setProperty(TEMPLATE, template);
  405       }
  406   
  407       public String getTemplate() {
  408           return getPropertyAsString(TEMPLATE);
  409       }
  410   
  411       public boolean useHeaders() {
  412           return USE_HDRS.equalsIgnoreCase( getPropertyAsString(MATCH_AGAINST));
  413       }
  414   
  415       // Allow for property not yet being set (probably only applies to Test cases)
  416       public boolean useBody() {
  417           String prop = getPropertyAsString(MATCH_AGAINST);
  418           return prop.length()==0 || USE_BODY.equalsIgnoreCase(prop);// $NON-NLS-1$
  419       }
  420   
  421       public boolean useUnescapedBody() {
  422           String prop = getPropertyAsString(MATCH_AGAINST);
  423           return USE_BODY_UNESCAPED.equalsIgnoreCase(prop);// $NON-NLS-1$
  424       }
  425   
  426       public boolean useUrl() {
  427           String prop = getPropertyAsString(MATCH_AGAINST);
  428           return USE_URL.equalsIgnoreCase(prop);
  429       }
  430   
  431       public boolean useCode() {
  432           String prop = getPropertyAsString(MATCH_AGAINST);
  433           return USE_CODE.equalsIgnoreCase(prop);
  434       }
  435   
  436       public boolean useMessage() {
  437           String prop = getPropertyAsString(MATCH_AGAINST);
  438           return USE_MESSAGE.equalsIgnoreCase(prop);
  439       }
  440   
  441       public void setUseField(String actionCommand) {
  442           setProperty(MATCH_AGAINST,actionCommand);
  443       }
  444   }

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