From 1fc4d2c1b5995daf2a40fb19f03d732a90c6e16f Mon Sep 17 00:00:00 2001 From: BobSong <2434005232@qq.com> Date: Wed, 9 Sep 2026 16:11:43 +0800 Subject: [PATCH] fix: isolate mock expression evaluation contexts --- .../mock/generator/ExpressionGenerator.java | 11 ++--- .../StandardExpressionGenerator.java | 11 ++--- .../generator/ExpressionGeneratorTest.java | 47 +++++++++++++++++++ 3 files changed, 57 insertions(+), 12 deletions(-) diff --git a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/ExpressionGenerator.java b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/ExpressionGenerator.java index 89b4f2a9e8a9..9cd4f6d884fc 100644 --- a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/ExpressionGenerator.java +++ b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/ExpressionGenerator.java @@ -41,8 +41,6 @@ public class ExpressionGenerator implements Generator { private static final ExpressionParser PARSER = new SpelExpressionParser(); - private static final EvaluationContext CONTEXT = initContext(); - @Override public String getName() { return "expression"; @@ -52,10 +50,11 @@ public String getName() { public String doGenerate(final List params, final String rule, final MockRequest mockRequest) { String expression = params.get(0); - - CONTEXT.setVariable("req", mockRequest); - - Object val = PARSER.parseExpression(expression).getValue(CONTEXT); + + EvaluationContext context = initContext(); + context.setVariable("req", mockRequest); + + Object val = PARSER.parseExpression(expression).getValue(context); return JsonUtils.toJson(val); } diff --git a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/StandardExpressionGenerator.java b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/StandardExpressionGenerator.java index 0d44137303d1..c20902a5d8e8 100644 --- a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/StandardExpressionGenerator.java +++ b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/StandardExpressionGenerator.java @@ -40,8 +40,6 @@ public class StandardExpressionGenerator implements Generator { private static final ExpressionParser PARSER = new SpelExpressionParser(); - private static final EvaluationContext CONTEXT = initContext(); - @Override public String getName() { return "standardSPELExpression"; @@ -51,10 +49,11 @@ public String getName() { public String doGenerate(final List params, final String rule, final MockRequest mockRequest) { String expression = params.get(0); - - CONTEXT.setVariable("req", mockRequest); - - Object val = PARSER.parseExpression(expression).getValue(CONTEXT); + + EvaluationContext context = initContext(); + context.setVariable("req", mockRequest); + + Object val = PARSER.parseExpression(expression).getValue(context); return JsonUtils.toJson(val); } diff --git a/shenyu-plugin/shenyu-plugin-mock/src/test/java/org/apache/shenyu/plugin/mock/generator/ExpressionGeneratorTest.java b/shenyu-plugin/shenyu-plugin-mock/src/test/java/org/apache/shenyu/plugin/mock/generator/ExpressionGeneratorTest.java index af4cbfcd20cc..2412c44461c9 100644 --- a/shenyu-plugin/shenyu-plugin-mock/src/test/java/org/apache/shenyu/plugin/mock/generator/ExpressionGeneratorTest.java +++ b/shenyu-plugin/shenyu-plugin-mock/src/test/java/org/apache/shenyu/plugin/mock/generator/ExpressionGeneratorTest.java @@ -29,6 +29,11 @@ import java.nio.charset.StandardCharsets; import java.time.LocalDate; import java.util.Arrays; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; @@ -39,6 +44,7 @@ import static org.hamcrest.Matchers.matchesRegex; import static org.hamcrest.Matchers.oneOf; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrowsExactly; @@ -182,6 +188,47 @@ public void testGenerateDataFromReq() { assertThat(generator.generate("expression|#req.json.address.country", mockRequest), is("\"CHINA\"")); } + + @Test + public void testGenerateDataFromReqConcurrently() throws Exception { + ExecutorService executor = Executors.newFixedThreadPool(2); + try { + assertConcurrentRequestValues(executor, generator, "expression|#req.json.value"); + assertConcurrentRequestValues(executor, new StandardExpressionGenerator(), "standardSPELExpression|#req.json.value"); + } finally { + executor.shutdownNow(); + executor.awaitTermination(5, TimeUnit.SECONDS); + } + } + + private void assertConcurrentRequestValues(final ExecutorService executor, final Generator testGenerator, + final String rule) throws Exception { + for (int i = 0; i < 100; i++) { + MockRequest firstRequest = createRequest("first"); + MockRequest secondRequest = createRequest("second"); + CyclicBarrier barrier = new CyclicBarrier(2); + + Future firstResult = submitRequest(executor, testGenerator, rule, firstRequest, barrier); + Future secondResult = submitRequest(executor, testGenerator, rule, secondRequest, barrier); + + assertEquals("\"first\"", firstResult.get(5, TimeUnit.SECONDS)); + assertEquals("\"second\"", secondResult.get(5, TimeUnit.SECONDS)); + } + } + + private Future submitRequest(final ExecutorService executor, final Generator testGenerator, + final String rule, + final MockRequest mockRequest, final CyclicBarrier barrier) { + return executor.submit(() -> { + barrier.await(); + return testGenerator.generate(rule, mockRequest); + }); + } + + private MockRequest createRequest(final String value) { + byte[] body = ("{\"value\":\"" + value + "\"}").getBytes(StandardCharsets.UTF_8); + return MockRequest.Builder.builder().body(body).build(); + } @Test public void testZhDataGenerate() {