001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.jmeter.functions;
020:
021: import java.io.ObjectStreamException;
022: import java.io.Serializable;
023: import java.util.ArrayList;
024: import java.util.Collection;
025: import java.util.Iterator;
026: import java.util.LinkedList;
027: import java.util.List;
028: import java.util.Random;
029:
030: import org.apache.jmeter.engine.util.CompoundVariable;
031: import org.apache.jmeter.samplers.SampleResult;
032: import org.apache.jmeter.samplers.Sampler;
033: import org.apache.jmeter.threads.JMeterVariables;
034: import org.apache.jmeter.util.JMeterUtils;
035: import org.apache.jorphan.logging.LoggingManager;
036: import org.apache.log.Logger;
037: import org.apache.oro.text.MalformedCachePatternException;
038: import org.apache.oro.text.regex.MatchResult;
039: import org.apache.oro.text.regex.Pattern;
040: import org.apache.oro.text.regex.PatternMatcher;
041: import org.apache.oro.text.regex.PatternMatcherInput;
042: import org.apache.oro.text.regex.Perl5Compiler;
043: import org.apache.oro.text.regex.Util;
044:
045: public class RegexFunction extends AbstractFunction implements
046: Serializable {
047: private static final Logger log = LoggingManager
048: .getLoggerForClass();
049:
050: private static final long serialVersionUID = 1L;
051:
052: public static final String ALL = "ALL"; //$NON-NLS-1$
053:
054: public static final String RAND = "RAND"; //$NON-NLS-1$
055:
056: public static final String KEY = "__regexFunction"; //$NON-NLS-1$
057:
058: private Object[] values;// Parameters are stored here
059:
060: private static Random rand = new Random();
061:
062: private static final List desc = new LinkedList();
063:
064: private transient Pattern templatePattern;// initialised to the regex \$(\d+)\$
065:
066: // Number of parameters expected - used to reject invalid calls
067: private static final int MIN_PARAMETER_COUNT = 2;
068:
069: private static final int MAX_PARAMETER_COUNT = 6;
070: static {
071: desc.add(JMeterUtils.getResString("regexfunc_param_1"));// regex //$NON-NLS-1$
072: desc.add(JMeterUtils.getResString("regexfunc_param_2"));// template //$NON-NLS-1$
073: desc.add(JMeterUtils.getResString("regexfunc_param_3"));// which match //$NON-NLS-1$
074: desc.add(JMeterUtils.getResString("regexfunc_param_4"));// between text //$NON-NLS-1$
075: desc.add(JMeterUtils.getResString("regexfunc_param_5"));// default text //$NON-NLS-1$
076: desc.add(JMeterUtils.getResString("function_name_param")); //$NON-NLS-1$
077: }
078:
079: public RegexFunction() {
080: initPattern();
081: }
082:
083: private void initPattern() {
084: templatePattern = JMeterUtils.getPatternCache().getPattern(
085: "\\$(\\d+)\\$", //$NON-NLS-1$
086: Perl5Compiler.READ_ONLY_MASK);
087: }
088:
089: // For serialised objects, do the same work as the constructor:
090: private Object readResolve() throws ObjectStreamException {
091: initPattern();
092: return this ;
093: }
094:
095: public synchronized String execute(SampleResult previousResult,
096: Sampler currentSampler) throws InvalidVariableException {
097: String valueIndex = "", defaultValue = "", between = ""; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
098: String name = ""; //$NON-NLS-1$
099: Pattern searchPattern;
100: Object[] tmplt;
101: try {
102: searchPattern = JMeterUtils.getPatternCache().getPattern(
103: ((CompoundVariable) values[0]).execute(),
104: Perl5Compiler.READ_ONLY_MASK);
105: tmplt = generateTemplate(((CompoundVariable) values[1])
106: .execute());
107:
108: if (values.length > 2) {
109: valueIndex = ((CompoundVariable) values[2]).execute();
110: }
111: if (valueIndex.equals("")) { //$NON-NLS-1$
112: valueIndex = "1"; //$NON-NLS-1$
113: }
114:
115: if (values.length > 3) {
116: between = ((CompoundVariable) values[3]).execute();
117: }
118:
119: if (values.length > 4) {
120: String dv = ((CompoundVariable) values[4]).execute();
121: if (!dv.equals("")) { //$NON-NLS-1$
122: defaultValue = dv;
123: }
124: }
125:
126: if (values.length > 5) {
127: name = ((CompoundVariable) values[values.length - 1])
128: .execute();
129: }
130: } catch (MalformedCachePatternException e) {
131: throw new InvalidVariableException(e.toString());
132: }
133:
134: JMeterVariables vars = getVariables();// Relatively expensive
135: // operation, so do it once
136: vars.put(name, defaultValue);
137: if (previousResult == null
138: || previousResult.getResponseData().length == 0) {
139: return defaultValue;
140: }
141:
142: List collectAllMatches = new ArrayList();
143: try {
144: PatternMatcher matcher = JMeterUtils.getMatcher();
145: String responseText = previousResult
146: .getResponseDataAsString();
147: PatternMatcherInput input = new PatternMatcherInput(
148: responseText);
149: while (matcher.contains(input, searchPattern)) {
150: MatchResult match = matcher.getMatch();
151: collectAllMatches.add(match);
152: }
153: } catch (NumberFormatException e) {//TODO: can this occur?
154: log.error("", e); //$NON-NLS-1$
155: return defaultValue;
156: } finally {
157: vars.put(name + "_matchNr", "" + collectAllMatches.size()); //$NON-NLS-1$ //$NON-NLS-2$
158: }
159:
160: if (collectAllMatches.size() == 0) {
161: return defaultValue;
162: }
163:
164: if (valueIndex.equals(ALL)) {
165: StringBuffer value = new StringBuffer();
166: Iterator it = collectAllMatches.iterator();
167: boolean first = true;
168: while (it.hasNext()) {
169: if (!first) {
170: value.append(between);
171: } else {
172: first = false;
173: }
174: value.append(generateResult((MatchResult) it.next(),
175: name, tmplt, vars));
176: }
177: return value.toString();
178: } else if (valueIndex.equals(RAND)) {
179: MatchResult result = (MatchResult) collectAllMatches
180: .get(rand.nextInt(collectAllMatches.size()));
181: return generateResult(result, name, tmplt, vars);
182: } else {
183: try {
184: int index = Integer.parseInt(valueIndex) - 1;
185: MatchResult result = (MatchResult) collectAllMatches
186: .get(index);
187: return generateResult(result, name, tmplt, vars);
188: } catch (NumberFormatException e) {
189: float ratio = Float.parseFloat(valueIndex);
190: MatchResult result = (MatchResult) collectAllMatches
191: .get((int) (collectAllMatches.size() * ratio + .5) - 1);
192: return generateResult(result, name, tmplt, vars);
193: } catch (IndexOutOfBoundsException e) {
194: return defaultValue;
195: }
196: }
197:
198: }
199:
200: private void saveGroups(MatchResult result, String namep,
201: JMeterVariables vars) {
202: if (result != null) {
203: for (int x = 0; x < result.groups(); x++) {
204: vars.put(namep + "_g" + x, result.group(x)); //$NON-NLS-1$
205: }
206: }
207: }
208:
209: public List getArgumentDesc() {
210: return desc;
211: }
212:
213: private String generateResult(MatchResult match, String namep,
214: Object[] template, JMeterVariables vars) {
215: saveGroups(match, namep, vars);
216: StringBuffer result = new StringBuffer();
217: for (int a = 0; a < template.length; a++) {
218: if (template[a] instanceof String) {
219: result.append(template[a]);
220: } else {
221: result.append(match.group(((Integer) template[a])
222: .intValue()));
223: }
224: }
225: vars.put(namep, result.toString());
226: return result.toString();
227: }
228:
229: public String getReferenceKey() {
230: return KEY;
231: }
232:
233: public synchronized void setParameters(Collection parameters)
234: throws InvalidVariableException {
235: values = parameters.toArray();
236:
237: if ((values.length < MIN_PARAMETER_COUNT)
238: || (values.length > MAX_PARAMETER_COUNT)) {
239: throw new InvalidVariableException("Parameter Count " //$NON-NLS-1$
240: + values.length + " not between " //$NON-NLS-1$
241: + MIN_PARAMETER_COUNT + " & " //$NON-NLS-1$
242: + MAX_PARAMETER_COUNT);
243: }
244: }
245:
246: private Object[] generateTemplate(String rawTemplate) {
247: List pieces = new ArrayList();
248: List combined = new LinkedList();
249: PatternMatcher matcher = JMeterUtils.getMatcher();
250: Util.split(pieces, matcher, templatePattern, rawTemplate);
251: PatternMatcherInput input = new PatternMatcherInput(rawTemplate);
252: Iterator iter = pieces.iterator();
253: boolean startsWith = isFirstElementGroup(rawTemplate);
254: while (iter.hasNext()) {
255: boolean matchExists = matcher.contains(input,
256: templatePattern);
257: if (startsWith) {
258: if (matchExists) {
259: combined.add(new Integer(matcher.getMatch()
260: .group(1)));
261: }
262: combined.add(iter.next());
263: } else {
264: combined.add(iter.next());
265: if (matchExists) {
266: combined.add(new Integer(matcher.getMatch()
267: .group(1)));
268: }
269: }
270: }
271: if (matcher.contains(input, templatePattern)) {
272: combined.add(new Integer(matcher.getMatch().group(1)));
273: }
274: return combined.toArray();
275: }
276:
277: private boolean isFirstElementGroup(String rawData) {
278: Pattern pattern = JMeterUtils.getPatternCache().getPattern(
279: "^\\$\\d+\\$", //$NON-NLS-1$
280: Perl5Compiler.READ_ONLY_MASK);
281: return JMeterUtils.getMatcher().contains(rawData, pattern);
282: }
283:
284: }
|