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.

    The pattern strings are:

      -
    • Contains, Matches: Perl5-style regular expressions
    • +
    • Contains, Matches: Java regular expressions
    • Equals, Substring: plain text, case-sensitive

    - A summary of the pattern matching characters can be found at ORO Perl5 regular expressions. + A summary of the pattern matching characters can be found at Java regular expressions.

    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. String chargenerate Unicode char values from a list of numbers2.3.3 String changeCaseChange case following different modes4.0 String escapeHtmlEncode strings using HTML encoding2.3.3 - String escapeOroRegexpCharsquote meta chars used by ORO regular expression2.9 + String escapeOroRegexpCharsquote meta chars used by Java regular expression2.9 String escapeXmlEncode strings using XMl encoding3.2 String regexFunctionparse previous response using a regular expression1.X String unescapeProcess 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.