From d44cf7c8044a79f9d899ffe7322adf587738bf29 Mon Sep 17 00:00:00 2001
From: Felix Schumacher
Date: Wed, 18 Mar 2026 20:52:15 +0100
Subject: [PATCH] chore: deprecate usage of Oro and switch default regex usage
to JDK internal
---
bin/jmeter.properties | 3 +-
.../functions/EscapeOroRegexpChars.java | 10 ++-
.../functions/TestEscapeOroRegexpChars.java | 90 +++++++++++--------
xdocs/changes.xml | 1 +
xdocs/usermanual/component_reference.xml | 14 ++-
xdocs/usermanual/functions.xml | 7 +-
xdocs/usermanual/properties_reference.xml | 4 +-
xdocs/usermanual/regular_expressions.xml | 13 +--
8 files changed, 85 insertions(+), 57 deletions(-)
diff --git a/bin/jmeter.properties b/bin/jmeter.properties
index 58c540cfd9c..3b0f95c6450 100644
--- a/bin/jmeter.properties
+++ b/bin/jmeter.properties
@@ -1136,7 +1136,8 @@ cookies=cookies
# Ability to switch out the old Oro Regex implementation with the JDK built-in implementation
# Any value different to 'oro' will disable the Oro implementation and enable the JDK based.
-#jmeter.regex.engine=oro
+# Usage of Oro is deprecated now in JMeter >=6.0
+jmeter.regex.engine=java
# We assist the JDK based Regex implementation by caching Pattern objects. The size of the
# cache can be set with this setting. It can be disabled by setting it to '0'.
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/EscapeOroRegexpChars.java b/src/functions/src/main/java/org/apache/jmeter/functions/EscapeOroRegexpChars.java
index a3fbf62aa6c..6b3e4193dbc 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/EscapeOroRegexpChars.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/EscapeOroRegexpChars.java
@@ -20,6 +20,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.regex.Pattern;
import org.apache.jmeter.engine.util.CompoundVariable;
import org.apache.jmeter.samplers.SampleResult;
@@ -33,7 +34,8 @@
import com.google.auto.service.AutoService;
/**
- * Escape ORO meta characters
+ * Escape Regex meta characters
+ *
* @since 2.9
*/
@AutoService(Function.class)
@@ -44,6 +46,8 @@ public class EscapeOroRegexpChars extends AbstractFunction {
private static final String KEY = "__escapeOroRegexpChars"; //$NON-NLS-1$
+ private final boolean USE_JAVA_REGEX;
+
static {
desc.add(JMeterUtils.getResString("value_to_quote_meta")); //$NON-NLS-1$
desc.add(JMeterUtils.getResString("function_name_paropt")); //$NON-NLS-1$
@@ -62,6 +66,8 @@ public class EscapeOroRegexpChars extends AbstractFunction {
*/
public EscapeOroRegexpChars() {
super();
+ USE_JAVA_REGEX = !JMeterUtils.getPropDefault(
+ "jmeter.regex.engine", "oro").equalsIgnoreCase("oro");
}
/** {@inheritDoc} */
@@ -76,7 +82,7 @@ public String execute(SampleResult previousResult, Sampler currentSampler)
varName = values[PARAM_NAME - 1].execute().trim();
}
- String escapedValue = Perl5Compiler.quotemeta(valueToEscape);
+ String escapedValue = USE_JAVA_REGEX ? Pattern.quote(valueToEscape) : Perl5Compiler.quotemeta(valueToEscape);
if (!varName.isEmpty()) {
JMeterVariables vars = getVariables();
diff --git a/src/functions/src/test/java/org/apache/jmeter/functions/TestEscapeOroRegexpChars.java b/src/functions/src/test/java/org/apache/jmeter/functions/TestEscapeOroRegexpChars.java
index 1722c309e9e..ae8f2353ddb 100644
--- a/src/functions/src/test/java/org/apache/jmeter/functions/TestEscapeOroRegexpChars.java
+++ b/src/functions/src/test/java/org/apache/jmeter/functions/TestEscapeOroRegexpChars.java
@@ -18,31 +18,46 @@
package org.apache.jmeter.functions;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.Map;
+import java.util.stream.Stream;
import org.apache.jmeter.engine.util.CompoundVariable;
+import org.apache.jmeter.gui.action.AbstractAction;
import org.apache.jmeter.junit.JMeterTestCase;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.threads.JMeterContext;
import org.apache.jmeter.threads.JMeterContextService;
import org.apache.jmeter.threads.JMeterVariables;
+import org.apache.jmeter.util.JMeterUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+/**
+ * Test the function __escapeOroRegexpCars
+ *
+ * To prepare for removal of Oro, we changed the behavior of the function
+ * to use Oro or JDKs internal Regex implementation based on the JMeter property
+ * {@code jmeter.regex.engine}
+ *
+ * Those two implementations have a slightly different way of escaping, so
+ * test both here, until we got rid of Oro.
+ */
public class TestEscapeOroRegexpChars extends JMeterTestCase {
- private AbstractFunction function;
private SampleResult result;
private Collection params;
private JMeterVariables vars;
- private JMeterContext jmctx;
@BeforeEach
void setUp() {
- function = new EscapeOroRegexpChars();
result = new SampleResult();
- jmctx = JMeterContextService.getContext();
+ JMeterContext jmctx = JMeterContextService.getContext();
String data = "The quick brown fox";
result.setResponseData(data, null);
vars = new JMeterVariables();
@@ -53,48 +68,53 @@ void setUp() {
@Test
void testParameterCount() throws Exception {
- checkInvalidParameterCounts(function, 1, 2);
+ checkInvalidParameterCounts(new EscapeOroRegexpChars(), 1, 2);
}
- @Test
- void testNOEscape() throws Exception {
- params.add(new CompoundVariable("toto1titi"));
- function.setParameters(params);
- String ret = function.execute(result, null);
- Assertions.assertEquals("toto1titi", ret);
+ static Collection functionAndParams() {
+ var testValuesPerImplementation = Map.of(
+ "oro", Map.of(
+ "toto1titi", "toto1titi",
+ "toto titi", "toto\\ titi",
+ "toto(.+?)titi", "toto\\(\\.\\+\\?\\)titi",
+ "[^\"].+?","\\[\\^\\\"\\]\\.\\+\\?"
+ ),
+ "java", Map.of(
+ "toto1titi", "\\Qtoto1titi\\E",
+ "toto titi", "\\Qtoto titi\\E",
+ "toto(.+?)titi", "\\Qtoto(.+?)titi\\E",
+ "[^\"].+?", "\\Q[^\"].+?\\E"
+ )
+ );
+ Collection args = new ArrayList<>();
+ for (var implementation: testValuesPerImplementation.entrySet()) {
+ JMeterUtils.setProperty("jmeter.regex.engine", implementation.getKey());
+ AbstractFunction function = new EscapeOroRegexpChars();
+ for (var testValues: implementation.getValue().entrySet()) {
+ args.add(Arguments.of(function, testValues.getKey(), testValues.getValue()));
+ }
+ }
+ return args;
}
- @Test
- void testEscapeSpace() throws Exception {
- params.add(new CompoundVariable("toto1 titi"));
+ @ParameterizedTest
+ @MethodSource("functionAndParams")
+ void testEscaping(AbstractFunction function, String value, String expected) throws Exception {
+ params.add(new CompoundVariable(value));
function.setParameters(params);
String ret = function.execute(result, null);
- Assertions.assertEquals("toto1\\ titi", ret);
+ Assertions.assertEquals(expected, ret);
}
- @Test
- void testEscape() throws Exception {
- params.add(new CompoundVariable("toto(.+?)titi"));
- function.setParameters(params);
- String ret = function.execute(result, null);
- Assertions.assertEquals("toto\\(\\.\\+\\?\\)titi", ret);
- }
-
- @Test
- void testEscapeWithVars() throws Exception {
- params.add(new CompoundVariable("toto(.+?)titi"));
+ @ParameterizedTest
+ @MethodSource("functionAndParams")
+ void testEscapingWithVar(AbstractFunction function, String value, String expected) throws Exception {
+ params.add(new CompoundVariable(value));
params.add(new CompoundVariable("exportedVar"));
function.setParameters(params);
String ret = function.execute(result, null);
- Assertions.assertEquals("toto\\(\\.\\+\\?\\)titi", ret);
- Assertions.assertEquals("toto\\(\\.\\+\\?\\)titi", vars.get("exportedVar"));
+ Assertions.assertEquals(expected, ret);
+ Assertions.assertEquals(expected, vars.get("exportedVar"));
}
- @Test
- void testEscape2() throws Exception {
- params.add(new CompoundVariable("[^\"].+?"));
- function.setParameters(params);
- String ret = function.execute(result, null);
- Assertions.assertEquals("\\[\\^\\\"\\]\\.\\+\\?", ret);
- }
}
diff --git a/xdocs/changes.xml b/xdocs/changes.xml
index 17d8185c337..80233a59204 100644
--- a/xdocs/changes.xml
+++ b/xdocs/changes.xml
@@ -69,6 +69,7 @@ Summary
6352 Calculate delays in Open Model Thread Group and Precise Throughput
Timer relative to start of Thread Group instead of the start of the test.
63576358 Ensure writable directories when copying template files while report generation.
+
66456661Deprecate usage of Oro Regex implementation and switch default Regex implementation to JDK built-in
HTTP Samplers and Test Script Recorder
diff --git a/xdocs/usermanual/component_reference.xml b/xdocs/usermanual/component_reference.xml
index 063fed59a65..adc671fd334 100644
--- a/xdocs/usermanual/component_reference.xml
+++ b/xdocs/usermanual/component_reference.xml
@@ -4471,11 +4471,11 @@ GUI that they can use while developing new JMeter components.
You can also choose whether the strings will be expected
to match the entire response, or if the response is only expected to contain the
@@ -6902,7 +6902,7 @@ To disable the redirect detection, set the property proxy.redirect.disabli
Includes and Excludes
-
The include and exclude patterns are treated as regular expressions (using Jakarta ORO).
+
The include and exclude patterns are treated as regular expressions.
They will be matched against the host name, port (actual or implied), path and query (if any) of each browser request.
If the URL you are browsing is
"http://localhost/jmeter/index.html?username=xxxx",
@@ -6992,7 +6992,7 @@ place User Defined Variables directly within the HTTP(S) Test Script Recorder to
If you define the variable WEB with the value www, for example,
the string www will be replaced by ${WEB} wherever it is found.
To avoid this happening everywhere, set the "Regex Matching" check-box.
-This tells the proxy server to treat values as Regexes (using the perl5 compatible regex matchers provided by ORO).
+This tells the proxy server to treat values as Regexes.
If "Regex Matching" is selected every variable will be compiled into a perl compatible regex enclosed in
\b( and )\b. That way each match will start and end at a word boundary.
@@ -7015,13 +7015,9 @@ The parens are necessary, since the normally added boundary characters will prev
$ to match.
If you want to match /images at the start of a string only, use the value (^/images).
-Jakarta ORO also supports zero-width look-ahead, so one can match /images/…
+Javas Regex implementation also supports zero-width look-ahead, so one can match /images/…
but retain the trailing / in the output by using (^/images(?=/)).
-
-Note that the current version of Jakarta ORO does not support look-behind - i.e. (?<=…) or (?<!…).
-
-
Look out for overlapping matchers. For example the value .* as a regex in a variable named
regex will partly match a previous replaced variable, which will result in something like
${{regex}, which is most probably not the desired result.
diff --git a/xdocs/usermanual/functions.xml b/xdocs/usermanual/functions.xml
index 5e4078ea48c..0d437c791c8 100644
--- a/xdocs/usermanual/functions.xml
+++ b/xdocs/usermanual/functions.xml
@@ -148,7 +148,7 @@ Alternatively, just use / instead for the path separator - e.g.
Process strings containing Java escapes (e.g. \n & \t)
2.3.3
@@ -1530,7 +1530,8 @@ A reference name - refName - for reusing the value created by this
-Function which escapes the ORO Regexp meta characters, it is the equivalent of \Q\E in Java Regexp Engine.
+Function which escapes the Java Regexp meta characters, it is the equivalent of \Q\E in Java Regexp Engine.
+With JMeter 6.0 the quoting changed from Oro to Java internal.
For example,${__escapeOroRegexpChars([^"].+?,)}
@@ -1538,7 +1539,7 @@ returns:
\[\^\"\]\.\+\?.
- Uses Perl5Compiler#quotemeta(String) from ORO.
+ Uses java.util.regex.Pattern#quote(String) from JDK.
diff --git a/xdocs/usermanual/properties_reference.xml b/xdocs/usermanual/properties_reference.xml
index a40ae5027a6..a6af766b388 100644
--- a/xdocs/usermanual/properties_reference.xml
+++ b/xdocs/usermanual/properties_reference.xml
@@ -1434,8 +1434,8 @@ JMETER-SERVER
Ability to switch out the old Oro Regex implementation with the JDK built-in implementation.
Any value different to oro will disable the Oro implementation and enable the JDK based.
- We intend to switch the default to the JDK based one in a later version of JMeter.
- Defaults to: oro
+ We switched the default to the JDK based one in a JMeter 6.0.
+ Defaults to: java
We assist the JDK based Regex implementation by caching Pattern objects. The size of the
diff --git a/xdocs/usermanual/regular_expressions.xml b/xdocs/usermanual/regular_expressions.xml
index 2862f3ab36c..2502f126ad8 100644
--- a/xdocs/usermanual/regular_expressions.xml
+++ b/xdocs/usermanual/regular_expressions.xml
@@ -41,8 +41,8 @@ a summary of the pattern matching characters
There is also documentation on an older incarnation of the product at
OROMatcher User's guide, which might prove useful.
-With JMeter version 5.5 the Regex implementation can be switched from Oro to the JDK based one by setting
-the JMeter property jmeter.regex.engine to some value different than oro.
+With JMeter version 6.0 the Regex implementation switched from Oro to the JDK based one by setting
+the JMeter property jmeter.regex.engine. You can switch it back by setting the value to oro.
The pattern matching is very similar to the pattern matching in Perl.
A full installation of Perl will include plenty of documentation on regular expressions - look for perlrequick,
@@ -193,7 +193,8 @@ Here is a list of the meta characters and their meaning (please check the ORO do
Please note that ORO does not support the \Q and \E meta-characters.
[In other RE engines, these can be used to quote a portion of an RE so that the meta-characters stand for themselves.]
-You can use function to do the equivalent, see ${__escapeOroRegexpChars(valueToEscape)}.
+You can use a function to do the equivalent, see ${__escapeOroRegexpChars(valueToEscape)}.
+Only applies, if you switch back to Oro.
The following Perl5 extended regular expressions are supported by ORO.
@@ -222,8 +223,10 @@ of the input, s enables single line treatment of the input, and
Modifiers can be placed anywhere in the regex, and apply from that point onwards.
-[A bug in ORO means that they cannot be used at the very end of the regex.
-However they would have no effect there anyway.]
+
+A bug in ORO means that they cannot be used at the very end of the regex.
+However they would have no effect there anyway. Only applies, when you switch back to Oro
+
The single-line (?s) and multi-line (?m) modifiers are normally placed at the start of the regex.