PageRenderTime 25ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/jmeter-2.5.1/src/components/org/apache/jmeter/extractor/RegexExtractor.java

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