Skip to content

Commit fe628e2

Browse files
committed
chore: deprecate usage of Oro and switch default regex usage to JDK internal
1 parent 9754b04 commit fe628e2

8 files changed

Lines changed: 85 additions & 57 deletions

File tree

bin/jmeter.properties

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1136,7 +1136,8 @@ cookies=cookies
11361136

11371137
# Ability to switch out the old Oro Regex implementation with the JDK built-in implementation
11381138
# Any value different to 'oro' will disable the Oro implementation and enable the JDK based.
1139-
#jmeter.regex.engine=oro
1139+
# Usage of Oro is deprecated now in JMeter >=6.0
1140+
jmeter.regex.engine=java
11401141

11411142
# We assist the JDK based Regex implementation by caching Pattern objects. The size of the
11421143
# cache can be set with this setting. It can be disabled by setting it to '0'.

src/functions/src/main/java/org/apache/jmeter/functions/EscapeOroRegexpChars.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.ArrayList;
2121
import java.util.Collection;
2222
import java.util.List;
23+
import java.util.regex.Pattern;
2324

2425
import org.apache.jmeter.engine.util.CompoundVariable;
2526
import org.apache.jmeter.samplers.SampleResult;
@@ -33,7 +34,8 @@
3334
import com.google.auto.service.AutoService;
3435

3536
/**
36-
* Escape ORO meta characters
37+
* Escape Regex meta characters
38+
*
3739
* @since 2.9
3840
*/
3941
@AutoService(Function.class)
@@ -44,6 +46,8 @@ public class EscapeOroRegexpChars extends AbstractFunction {
4446

4547
private static final String KEY = "__escapeOroRegexpChars"; //$NON-NLS-1$
4648

49+
private final boolean USE_JAVA_REGEX;
50+
4751
static {
4852
desc.add(JMeterUtils.getResString("value_to_quote_meta")); //$NON-NLS-1$
4953
desc.add(JMeterUtils.getResString("function_name_paropt")); //$NON-NLS-1$
@@ -62,6 +66,8 @@ public class EscapeOroRegexpChars extends AbstractFunction {
6266
*/
6367
public EscapeOroRegexpChars() {
6468
super();
69+
USE_JAVA_REGEX = !JMeterUtils.getPropDefault(
70+
"jmeter.regex.engine", "oro").equalsIgnoreCase("oro");
6571
}
6672

6773
/** {@inheritDoc} */
@@ -76,7 +82,7 @@ public String execute(SampleResult previousResult, Sampler currentSampler)
7682
varName = values[PARAM_NAME - 1].execute().trim();
7783
}
7884

79-
String escapedValue = Perl5Compiler.quotemeta(valueToEscape);
85+
String escapedValue = USE_JAVA_REGEX ? Pattern.quote(valueToEscape) : Perl5Compiler.quotemeta(valueToEscape);
8086

8187
if (!varName.isEmpty()) {
8288
JMeterVariables vars = getVariables();

src/functions/src/test/java/org/apache/jmeter/functions/TestEscapeOroRegexpChars.java

Lines changed: 55 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -18,31 +18,46 @@
1818
package org.apache.jmeter.functions;
1919

2020
import java.util.ArrayList;
21+
import java.util.Arrays;
2122
import java.util.Collection;
23+
import java.util.Map;
24+
import java.util.stream.Stream;
2225

2326
import org.apache.jmeter.engine.util.CompoundVariable;
27+
import org.apache.jmeter.gui.action.AbstractAction;
2428
import org.apache.jmeter.junit.JMeterTestCase;
2529
import org.apache.jmeter.samplers.SampleResult;
2630
import org.apache.jmeter.threads.JMeterContext;
2731
import org.apache.jmeter.threads.JMeterContextService;
2832
import org.apache.jmeter.threads.JMeterVariables;
33+
import org.apache.jmeter.util.JMeterUtils;
2934
import org.junit.jupiter.api.Assertions;
3035
import org.junit.jupiter.api.BeforeEach;
3136
import org.junit.jupiter.api.Test;
37+
import org.junit.jupiter.params.ParameterizedTest;
38+
import org.junit.jupiter.params.provider.Arguments;
39+
import org.junit.jupiter.params.provider.MethodSource;
3240

41+
/**
42+
* Test the function __escapeOroRegexpCars
43+
*
44+
* To prepare for removal of Oro, we changed the behavior of the function
45+
* to use Oro or JDKs internal Regex implementation based on the JMeter property
46+
* {@code jmeter.regex.engine}
47+
*
48+
* Those two implementations have a slightly different way of escaping, so
49+
* test both here, until we got rid of Oro.
50+
*/
3351
public class TestEscapeOroRegexpChars extends JMeterTestCase {
3452

35-
private AbstractFunction function;
3653
private SampleResult result;
3754
private Collection<CompoundVariable> params;
3855
private JMeterVariables vars;
39-
private JMeterContext jmctx;
4056

4157
@BeforeEach
4258
void setUp() {
43-
function = new EscapeOroRegexpChars();
4459
result = new SampleResult();
45-
jmctx = JMeterContextService.getContext();
60+
JMeterContext jmctx = JMeterContextService.getContext();
4661
String data = "The quick brown fox";
4762
result.setResponseData(data, null);
4863
vars = new JMeterVariables();
@@ -53,48 +68,53 @@ void setUp() {
5368

5469
@Test
5570
void testParameterCount() throws Exception {
56-
checkInvalidParameterCounts(function, 1, 2);
71+
checkInvalidParameterCounts(new EscapeOroRegexpChars(), 1, 2);
5772
}
5873

59-
@Test
60-
void testNOEscape() throws Exception {
61-
params.add(new CompoundVariable("toto1titi"));
62-
function.setParameters(params);
63-
String ret = function.execute(result, null);
64-
Assertions.assertEquals("toto1titi", ret);
74+
static Collection<Arguments> functionAndParams() {
75+
var testValuesPerImplementation = Map.of(
76+
"oro", Map.of(
77+
"toto1titi", "toto1titi",
78+
"toto titi", "toto\\ titi",
79+
"toto(.+?)titi", "toto\\(\\.\\+\\?\\)titi",
80+
"[^\"].+?","\\[\\^\\\"\\]\\.\\+\\?"
81+
),
82+
"java", Map.of(
83+
"toto1titi", "\\Qtoto1titi\\E",
84+
"toto titi", "\\Qtoto titi\\E",
85+
"toto(.+?)titi", "\\Qtoto(.+?)titi\\E",
86+
"[^\"].+?", "\\Q[^\"].+?\\E"
87+
)
88+
);
89+
Collection<Arguments> args = new ArrayList<>();
90+
for (var implementation: testValuesPerImplementation.entrySet()) {
91+
JMeterUtils.setProperty("jmeter.regex.engine", implementation.getKey());
92+
AbstractFunction function = new EscapeOroRegexpChars();
93+
for (var testValues: implementation.getValue().entrySet()) {
94+
args.add(Arguments.of(function, testValues.getKey(), testValues.getValue()));
95+
}
96+
}
97+
return args;
6598
}
6699

67-
@Test
68-
void testEscapeSpace() throws Exception {
69-
params.add(new CompoundVariable("toto1 titi"));
100+
@ParameterizedTest
101+
@MethodSource("functionAndParams")
102+
void testEscaping(AbstractFunction function, String value, String expected) throws Exception {
103+
params.add(new CompoundVariable(value));
70104
function.setParameters(params);
71105
String ret = function.execute(result, null);
72-
Assertions.assertEquals("toto1\\ titi", ret);
106+
Assertions.assertEquals(expected, ret);
73107
}
74108

75-
@Test
76-
void testEscape() throws Exception {
77-
params.add(new CompoundVariable("toto(.+?)titi"));
78-
function.setParameters(params);
79-
String ret = function.execute(result, null);
80-
Assertions.assertEquals("toto\\(\\.\\+\\?\\)titi", ret);
81-
}
82-
83-
@Test
84-
void testEscapeWithVars() throws Exception {
85-
params.add(new CompoundVariable("toto(.+?)titi"));
109+
@ParameterizedTest
110+
@MethodSource("functionAndParams")
111+
void testEscapingWithVar(AbstractFunction function, String value, String expected) throws Exception {
112+
params.add(new CompoundVariable(value));
86113
params.add(new CompoundVariable("exportedVar"));
87114
function.setParameters(params);
88115
String ret = function.execute(result, null);
89-
Assertions.assertEquals("toto\\(\\.\\+\\?\\)titi", ret);
90-
Assertions.assertEquals("toto\\(\\.\\+\\?\\)titi", vars.get("exportedVar"));
116+
Assertions.assertEquals(expected, ret);
117+
Assertions.assertEquals(expected, vars.get("exportedVar"));
91118
}
92119

93-
@Test
94-
void testEscape2() throws Exception {
95-
params.add(new CompoundVariable("[^\"].+?"));
96-
function.setParameters(params);
97-
String ret = function.execute(result, null);
98-
Assertions.assertEquals("\\[\\^\\\"\\]\\.\\+\\?", ret);
99-
}
100120
}

xdocs/changes.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ Summary
6969
<li><issue>6352</issue> Calculate delays in Open Model Thread Group and Precise Throughput
7070
Timer relative to start of Thread Group instead of the start of the test.</li>
7171
<li><issue>6357</issue><pr>6358</pr> Ensure writable directories when copying template files while report generation.</li>
72+
<li><issue>6645</issue><pr>6661</pr>Deprecate usage of Oro Regex implementation and switch default Regex implementation to JDK built-in</li>
7273
</ul>
7374

7475
<h3>HTTP Samplers and Test Script Recorder</h3>

xdocs/usermanual/component_reference.xml

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4471,11 +4471,11 @@ GUI that they can use while developing new JMeter components.</p>
44714471
The pattern strings are:
44724472
</p>
44734473
<ul>
4474-
<li><code>Contains</code>, <code>Matches</code>: Perl5-style regular expressions</li>
4474+
<li><code>Contains</code>, <code>Matches</code>: Java regular expressions</li>
44754475
<li><code>Equals</code>, <code>Substring</code>: plain text, case-sensitive</li>
44764476
</ul>
44774477
<p>
4478-
A summary of the pattern matching characters can be found at <a href="http://jakarta.apache.org/oro/api/org/apache/oro/text/regex/package-summary.html">ORO Perl5 regular expressions.</a>
4478+
A summary of the pattern matching characters can be found at <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/regex/Pattern.html">Java regular expressions.</a>
44794479
</p>
44804480
<p>You can also choose whether the strings will be expected
44814481
to <b>match</b> the entire response, or if the response is only expected to <b>contain</b> the
@@ -6902,7 +6902,7 @@ To disable the redirect detection, set the property <code>proxy.redirect.disabli
69026902
</p>
69036903

69046904
<h4>Includes and Excludes</h4>
6905-
<p>The <b>include and exclude patterns</b> are treated as regular expressions (using Jakarta ORO).
6905+
<p>The <b>include and exclude patterns</b> are treated as regular expressions.
69066906
They will be matched against the host name, port (actual or implied), path and query (if any) of each browser request.
69076907
If the URL you are browsing is <br></br>
69086908
"<code>http://localhost/jmeter/index.html?username=xxxx</code>",<br></br>
@@ -6992,7 +6992,7 @@ place User Defined Variables directly within the HTTP(S) Test Script Recorder to
69926992
If you define the variable <code>WEB</code> with the value <code>www</code>, for example,
69936993
the string <code>www</code> will be replaced by <code>${WEB}</code> wherever it is found.
69946994
To avoid this happening everywhere, set the "<code>Regex Matching</code>" check-box.
6995-
This tells the proxy server to treat values as Regexes (using the perl5 compatible regex matchers provided by ORO).</p>
6995+
This tells the proxy server to treat values as Regexes.</p>
69966996

69976997
<p>If "<code>Regex Matching</code>" is selected every variable will be compiled into a perl compatible regex enclosed in
69986998
<code>\b(</code> and <code>)\b</code>. That way each match will start and end at a word boundary.</p>
@@ -7015,13 +7015,9 @@ The parens are necessary, since the normally added boundary characters will prev
70157015
<code>$</code> to match.</p>
70167016

70177017
<p>If you want to match <code>/images</code> at the start of a string only, use the value <code>(^/images)</code>.
7018-
Jakarta ORO also supports zero-width look-ahead, so one can match <code>/images/&hellip;</code>
7018+
Javas Regex implementation also supports zero-width look-ahead, so one can match <code>/images/&hellip;</code>
70197019
but retain the trailing <code>/</code> in the output by using <code>(^/images(?=/))</code>.</p>
70207020

7021-
<note>
7022-
Note that the current version of Jakarta ORO does not support look-behind - i.e. <code>(?&lt;=&hellip;)</code> or <code>(?&lt;!&hellip;)</code>.
7023-
</note>
7024-
70257021
<p>Look out for overlapping matchers. For example the value <code>.*</code> as a regex in a variable named
70267022
<code>regex</code> will partly match a previous replaced variable, which will result in something like
70277023
<code>${{regex}</code>, which is most probably not the desired result.</p>

xdocs/usermanual/functions.xml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ Alternatively, just use <code>/</code> instead for the path separator - e.g. <co
148148
<tr><td>String</td><td> <a href="#__char">char</a></td><td>generate Unicode char values from a list of numbers</td><td>2.3.3</td></tr>
149149
<tr><td>String</td><td> <a href="#__changeCase">changeCase</a></td><td>Change case following different modes</td><td>4.0</td></tr>
150150
<tr><td>String</td><td> <a href="#__escapeHtml">escapeHtml</a></td><td>Encode strings using HTML encoding</td><td>2.3.3</td></tr>
151-
<tr><td>String</td><td> <a href="#__escapeOroRegexpChars">escapeOroRegexpChars</a></td><td>quote meta chars used by ORO regular expression</td><td>2.9</td></tr>
151+
<tr><td>String</td><td> <a href="#__escapeOroRegexpChars">escapeOroRegexpChars</a></td><td>quote meta chars used by Java regular expression</td><td>2.9</td></tr>
152152
<tr><td>String</td><td> <a href="#__escapeXml">escapeXml</a></td><td>Encode strings using XMl encoding</td><td>3.2</td></tr>
153153
<tr><td>String</td><td> <a href="#__regexFunction">regexFunction</a></td><td>parse previous response using a regular expression</td><td>1.X</td></tr>
154154
<tr><td>String</td><td> <a href="#__unescape">unescape</a></td><td>Process strings containing Java escapes (e.g. \n &amp; \t)</td><td>2.3.3</td></tr>
@@ -1530,15 +1530,16 @@ A reference name - <code>refName</code> - for reusing the value created by this
15301530
<component index="&sect-num;.5.32" name="__escapeOroRegexpChars">
15311531
<description>
15321532
<p>
1533-
Function which escapes the ORO Regexp meta characters, it is the equivalent of <code>\Q</code> <code>\E</code> in Java Regexp Engine.
1533+
Function which escapes the Java Regexp meta characters, it is the equivalent of <code>\Q</code> <code>\E</code> in Java Regexp Engine.
1534+
<note>With JMeter 6.0 the quoting changed from Oro to Java internal.</note>
15341535
</p>
15351536
<p>
15361537
For example,<source>${__escapeOroRegexpChars([^&quot;].+?,)}</source>
15371538
returns:
15381539
<code>\[\^\&quot;\]\.\+\?</code>.
15391540
</p>
15401541
<p>
1541-
Uses Perl5Compiler#quotemeta(String) from ORO.
1542+
Uses java.util.regex.Pattern#quote(String) from JDK.
15421543
</p>
15431544
</description>
15441545

xdocs/usermanual/properties_reference.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1434,8 +1434,8 @@ JMETER-SERVER</source>
14341434
<property name="jmeter.regex.engine">
14351435
Ability to switch out the old Oro Regex implementation with the JDK built-in implementation.
14361436
Any value different to <code>oro</code> will disable the Oro implementation and enable the JDK based.
1437-
<note>We intend to switch the default to the JDK based one in a later version of JMeter.</note>
1438-
Defaults to: <code>oro</code>
1437+
<note>We switched the default to the JDK based one in a JMeter 6.0.</note>
1438+
Defaults to: <code>java</code>
14391439
</property>
14401440
<property name="jmeter.regex.patterncache.size">
14411441
We assist the JDK based Regex implementation by caching Pattern objects. The size of the

xdocs/usermanual/regular_expressions.xml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ a summary of the pattern matching characters</a>
4141
There is also documentation on an older incarnation of the product at
4242
<a href="http://www.savarese.org/oro/docs/OROMatcher/index.html">OROMatcher User's guide</a>, which might prove useful.
4343
</p>
44-
<note>With JMeter version 5.5 the Regex implementation can be switched from Oro to the JDK based one by setting
45-
the JMeter property <code>jmeter.regex.engine</code> to some value different than <code>oro</code>.</note>
44+
<note>With JMeter version 6.0 the Regex implementation switched from Oro to the JDK based one by setting
45+
the JMeter property <code>jmeter.regex.engine</code>. You can switch it back by setting the value to <code>oro</code>.</note>
4646
<p>
4747
The pattern matching is very similar to the pattern matching in Perl.
4848
A full installation of Perl will include plenty of documentation on regular expressions - look for <code>perlrequick</code>,
@@ -193,7 +193,8 @@ Here is a list of the meta characters and their meaning (please check the ORO do
193193
<note>
194194
Please note that ORO does not support the <code>\Q</code> and <code>\E</code> meta-characters.
195195
[In other RE engines, these can be used to quote a portion of an RE so that the meta-characters stand for themselves.]
196-
You can use function to do the equivalent, see <a href="functions.html#__escapeOroRegexpChars">${__escapeOroRegexpChars(valueToEscape)}</a>.
196+
You can use a function to do the equivalent, see <a href="functions.html#__escapeOroRegexpChars">${__escapeOroRegexpChars(valueToEscape)}</a>.
197+
Only applies, if you switch back to Oro.
197198
</note>
198199
<p>
199200
The following Perl5 extended regular expressions are supported by ORO.
@@ -222,8 +223,10 @@ of the input, <code>s</code> enables single line treatment of the input, and <co
222223
<subsection name="&sect-num;.5 Placement of modifiers" anchor="placement">
223224
<p>
224225
Modifiers can be placed anywhere in the regex, and apply from that point onwards.
225-
[A bug in ORO means that they cannot be used at the very end of the regex.
226-
However they would have no effect there anyway.]
226+
<note>
227+
A bug in ORO means that they cannot be used at the very end of the regex.
228+
However they would have no effect there anyway. Only applies, when you switch back to Oro
229+
</note>
227230
</p>
228231
<p>
229232
The single-line <code>(?s)</code> and multi-line <code>(?m)</code> modifiers are normally placed at the start of the regex.

0 commit comments

Comments
 (0)