This is an automated email from the ASF dual-hosted git repository.
chengzhang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git
The following commit(s) were added to refs/heads/master by this push:
new 9f41f124ec1 Refactor EspressoInlineExpressionParser to use Truffle
Context with life cycle (#30398)
9f41f124ec1 is described below
commit 9f41f124ec1470fb3ee9423cdb70a54660a47db3
Author: Ling Hengqian <[email protected]>
AuthorDate: Wed Mar 6 06:20:04 2024 +0800
Refactor EspressoInlineExpressionParser to use Truffle Context with life
cycle (#30398)
---
distribution/proxy/src/main/release-docs/LICENSE | 2 +-
infra/expr/type/espresso/pom.xml | 6 ---
.../espresso/EspressoInlineExpressionParser.java | 39 +++++++++--------
.../infra/expr/espresso/ReflectContext.java | 10 ++++-
.../infra/expr/espresso/ReflectValue.java | 4 +-
.../EspressoInlineExpressionParserTest.java | 49 ++++++++++------------
pom.xml | 2 +-
7 files changed, 56 insertions(+), 56 deletions(-)
diff --git a/distribution/proxy/src/main/release-docs/LICENSE
b/distribution/proxy/src/main/release-docs/LICENSE
index 8ce7cb4f8a9..a729c926daa 100644
--- a/distribution/proxy/src/main/release-docs/LICENSE
+++ b/distribution/proxy/src/main/release-docs/LICENSE
@@ -240,7 +240,7 @@ The text of each license is the standard Apache 2.0 license.
failsafe 2.4.4: https://github.com/jhalterman/failsafe, Apache 2.0
failureaccess 1.0.1: https://github.com/google/guava, Apache 2.0
freemarker 2.3.31: https://freemarker.apache.org/, Apache 2.0
- groovy 4.0.10: https://groovy.apache.org/, Apache 2.0
+ groovy 4.0.19: https://groovy.apache.org/, Apache 2.0
grpc-api 1.58.0: https://github.com/grpc/grpc-java, Apache 2.0
grpc-context 1.58.0: https://github.com/grpc/grpc-java, Apache 2.0
grpc-core 1.58.0: https://github.com/grpc/grpc-java, Apache 2.0
diff --git a/infra/expr/type/espresso/pom.xml b/infra/expr/type/espresso/pom.xml
index d3681f30bbd..84c9d15da70 100644
--- a/infra/expr/type/espresso/pom.xml
+++ b/infra/expr/type/espresso/pom.xml
@@ -42,12 +42,6 @@
<artifactId>shardingsphere-infra-util</artifactId>
<version>${project.version}</version>
</dependency>
- <dependency>
- <groupId>org.apache.shardingsphere</groupId>
- <artifactId>shardingsphere-test-util</artifactId>
- <version>${project.version}</version>
- <scope>test</scope>
- </dependency>
<dependency>
<groupId>org.graalvm.polyglot</groupId>
diff --git
a/infra/expr/type/espresso/src/main/java/org/apache/shardingsphere/infra/expr/espresso/EspressoInlineExpressionParser.java
b/infra/expr/type/espresso/src/main/java/org/apache/shardingsphere/infra/expr/espresso/EspressoInlineExpressionParser.java
index 44b91ba9018..b95837b97b7 100644
---
a/infra/expr/type/espresso/src/main/java/org/apache/shardingsphere/infra/expr/espresso/EspressoInlineExpressionParser.java
+++
b/infra/expr/type/espresso/src/main/java/org/apache/shardingsphere/infra/expr/espresso/EspressoInlineExpressionParser.java
@@ -19,10 +19,10 @@ package org.apache.shardingsphere.infra.expr.espresso;
import com.google.common.base.Strings;
import com.google.common.collect.Sets;
+import
org.apache.shardingsphere.infra.exception.core.ShardingSpherePreconditions;
import org.apache.shardingsphere.infra.expr.spi.InlineExpressionParser;
import org.apache.shardingsphere.infra.util.groovy.GroovyUtils;
-import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
@@ -42,12 +42,10 @@ public final class EspressoInlineExpressionParser
implements InlineExpressionPar
private String inlineExpression;
- private final ReflectContext context = new ReflectContext(JAVA_CLASSPATH);
-
static {
- URL resource = ClassLoader.getSystemResource("build" + File.separator
+ "libs");
- String dir = null == resource ? null : resource.getPath();
- JAVA_CLASSPATH = dir + File.separator + "groovy.jar";
+ URL groovyJarUrl =
ClassLoader.getSystemResource("build/libs/groovy.jar");
+ ShardingSpherePreconditions.checkNotNull(groovyJarUrl,
NullPointerException::new);
+ JAVA_CLASSPATH = groovyJarUrl.getPath();
}
@Override
@@ -72,10 +70,15 @@ public final class EspressoInlineExpressionParser
implements InlineExpressionPar
@Override
public List<String> splitAndEvaluate() {
- return Strings.isNullOrEmpty(inlineExpression) ?
Collections.emptyList() :
flatten(evaluate(GroovyUtils.split(handlePlaceHolder(inlineExpression))));
+ try (ReflectContext context = new ReflectContext(JAVA_CLASSPATH)) {
+ if (Strings.isNullOrEmpty(inlineExpression)) {
+ return Collections.emptyList();
+ }
+ return flatten(evaluate(context,
GroovyUtils.split(handlePlaceHolder(inlineExpression))));
+ }
}
- private List<ReflectValue> evaluate(final List<String> inlineExpressions) {
+ private List<ReflectValue> evaluate(final ReflectContext context, final
List<String> inlineExpressions) {
List<ReflectValue> result = new ArrayList<>(inlineExpressions.size());
for (String each : inlineExpressions) {
StringBuilder expression = new
StringBuilder(handlePlaceHolder(each));
@@ -85,17 +88,17 @@ public final class EspressoInlineExpressionParser
implements InlineExpressionPar
if (!each.endsWith("\"")) {
expression.append('"');
}
- result.add(evaluate(expression.toString()));
+ result.add(evaluate(context, expression.toString()));
}
return result;
}
- private ReflectValue evaluate(final String expression) {
+ private ReflectValue evaluate(final ReflectContext context, final String
expression) {
return context.getBindings("java")
.getMember("groovy.lang.GroovyShell")
.newInstance()
- .invokeMember("parse", expression)
- .invokeMember("run");
+
.invokeMember("parse/(Ljava/lang/String;)Lgroovy/lang/Script;", expression)
+ .invokeMember("run/()Ljava/lang/Object;");
}
/**
@@ -140,9 +143,9 @@ public final class EspressoInlineExpressionParser
implements InlineExpressionPar
*/
@SuppressWarnings("unchecked")
private Set<List<String>> getCartesianValues(final ReflectValue segment) {
- Object[] temp = segment.invokeMember("getValues").as(Object[].class);
- List<Set<String>> result = new ArrayList<>(temp.length);
- for (Object each : temp) {
+ Object[] segmentAsObjectArray =
segment.invokeMember("getValues/()[Ljava/lang/Object;").as(Object[].class);
+ List<Set<String>> result = new
ArrayList<>(segmentAsObjectArray.length);
+ for (Object each : segmentAsObjectArray) {
if (null == each) {
continue;
}
@@ -163,10 +166,10 @@ public final class EspressoInlineExpressionParser
implements InlineExpressionPar
* @return {@link java.lang.String}
*/
private String assemblySegment(final List<String> cartesianValue, final
ReflectValue segment) {
- String[] temp = segment.invokeMember("getStrings").as(String[].class);
+ String[] segmentAsStringArray =
segment.invokeMember("getStrings/()[Ljava/lang/String;").as(String[].class);
StringBuilder result = new StringBuilder();
- for (int i = 0; i < temp.length; i++) {
- result.append(temp[i]);
+ for (int i = 0; i < segmentAsStringArray.length; i++) {
+ result.append(segmentAsStringArray[i]);
if (i < cartesianValue.size()) {
result.append(cartesianValue.get(i));
}
diff --git
a/infra/expr/type/espresso/src/main/java/org/apache/shardingsphere/infra/expr/espresso/ReflectContext.java
b/infra/expr/type/espresso/src/main/java/org/apache/shardingsphere/infra/expr/espresso/ReflectContext.java
index a187382d943..be7209d69c7 100644
---
a/infra/expr/type/espresso/src/main/java/org/apache/shardingsphere/infra/expr/espresso/ReflectContext.java
+++
b/infra/expr/type/espresso/src/main/java/org/apache/shardingsphere/infra/expr/espresso/ReflectContext.java
@@ -23,7 +23,7 @@ import lombok.SneakyThrows;
* Reflect Context.
* Avoid using JDK21 bytecode during compilation. Refer to
`org.graalvm.polyglot.Context`.
*/
-public final class ReflectContext {
+public final class ReflectContext implements AutoCloseable {
private static final String CONTEXT_CLASS_NAME =
"org.graalvm.polyglot.Context";
@@ -48,7 +48,7 @@ public final class ReflectContext {
public ReflectContext(final String javaClassPath) {
Object builderInstance = Class.forName(CONTEXT_CLASS_NAME)
.getMethod("newBuilder", String[].class)
- .invoke(null, (Object) new String[]{});
+ .invoke(null, (Object) new String[0]);
builderInstance = builderInstance.getClass()
.getMethod("allowAllAccess", boolean.class)
.invoke(builderInstance, true);
@@ -73,4 +73,10 @@ public final class ReflectContext {
.invoke(contextInstance, languageId);
return new ReflectValue(valueInstance);
}
+
+ @Override
+ @SneakyThrows
+ public void close() {
+
Class.forName(CONTEXT_CLASS_NAME).getMethod("close").invoke(contextInstance);
+ }
}
diff --git
a/infra/expr/type/espresso/src/main/java/org/apache/shardingsphere/infra/expr/espresso/ReflectValue.java
b/infra/expr/type/espresso/src/main/java/org/apache/shardingsphere/infra/expr/espresso/ReflectValue.java
index 8f0c20d7b13..aa46ad95d8a 100644
---
a/infra/expr/type/espresso/src/main/java/org/apache/shardingsphere/infra/expr/espresso/ReflectValue.java
+++
b/infra/expr/type/espresso/src/main/java/org/apache/shardingsphere/infra/expr/espresso/ReflectValue.java
@@ -19,6 +19,8 @@ package org.apache.shardingsphere.infra.expr.espresso;
import lombok.SneakyThrows;
+import java.util.stream.Stream;
+
/**
* Reflect Value.
* Avoid using JDK21 bytecode during compilation. Refer to
`org.graalvm.polyglot.Value`.
@@ -55,7 +57,7 @@ public class ReflectValue {
public ReflectValue newInstance(final Object... arguments) {
Object resultValueInstance = Class.forName(VALUE_CLASS_NAME)
.getMethod("newInstance", Object[].class)
- .invoke(valueInstance, (Object) arguments);
+ .invoke(valueInstance, new
Object[]{Stream.of(arguments).toArray()});
return new ReflectValue(resultValueInstance);
}
diff --git
a/infra/expr/type/espresso/src/test/java/org/apache/shardingsphere/infra/expr/espresso/EspressoInlineExpressionParserTest.java
b/infra/expr/type/espresso/src/test/java/org/apache/shardingsphere/infra/expr/espresso/EspressoInlineExpressionParserTest.java
index 55694206969..16930098d8c 100644
---
a/infra/expr/type/espresso/src/test/java/org/apache/shardingsphere/infra/expr/espresso/EspressoInlineExpressionParserTest.java
+++
b/infra/expr/type/espresso/src/test/java/org/apache/shardingsphere/infra/expr/espresso/EspressoInlineExpressionParserTest.java
@@ -19,15 +19,14 @@ package org.apache.shardingsphere.infra.expr.espresso;
import org.apache.shardingsphere.infra.expr.spi.InlineExpressionParser;
import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
-import org.apache.shardingsphere.test.util.PropertiesBuilder;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledForJreRange;
+import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
import org.junit.jupiter.api.condition.EnabledOnOs;
import org.junit.jupiter.api.condition.JRE;
import org.junit.jupiter.api.condition.OS;
import java.util.Collections;
-import java.util.LinkedHashMap;
import java.util.List;
import java.util.Properties;
@@ -38,6 +37,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
@EnabledForJreRange(min = JRE.JAVA_21)
@EnabledOnOs(value = OS.LINUX, disabledReason = "Refer to
https://www.graalvm.org/jdk21/reference-manual/java-on-truffle/faq/#does-java-running-on-truffle-run-on-hotspot-too
.")
+@EnabledIfSystemProperty(named = "java.vm.vendor", matches = "GraalVM",
+ disabledReason = "Executing unit tests of this type in batches will
result in a timeout, while executing unit tests individually works fine.")
class EspressoInlineExpressionParserTest {
@Test
@@ -49,64 +50,56 @@ class EspressoInlineExpressionParserTest {
@Test
void assertEvaluateForSimpleString() {
- List<String> expected =
TypedSPILoader.getService(InlineExpressionParser.class, "ESPRESSO",
PropertiesBuilder.build(
- new
PropertiesBuilder.Property(InlineExpressionParser.INLINE_EXPRESSION_KEY, "
t_order_0, t_order_1 "))).splitAndEvaluate();
+ List<String> expected = createInlineExpressionParser(" t_order_0,
t_order_1 ").splitAndEvaluate();
assertThat(expected.size(), is(2));
assertThat(expected, hasItems("t_order_0", "t_order_1"));
}
@Test
void assertEvaluateForNull() {
- List<String> expected =
TypedSPILoader.getService(InlineExpressionParser.class, "ESPRESSO",
PropertiesBuilder.build(
- new
PropertiesBuilder.Property(InlineExpressionParser.INLINE_EXPRESSION_KEY,
"t_order_${null}"))).splitAndEvaluate();
+ List<String> expected =
createInlineExpressionParser("t_order_${null}").splitAndEvaluate();
assertThat(expected.size(), is(1));
assertThat(expected, hasItems("t_order_"));
}
@Test
void assertEvaluateForLiteral() {
- List<String> expected =
TypedSPILoader.getService(InlineExpressionParser.class, "ESPRESSO",
PropertiesBuilder.build(
- new
PropertiesBuilder.Property(InlineExpressionParser.INLINE_EXPRESSION_KEY,
"t_order_${'xx'}"))).splitAndEvaluate();
+ List<String> expected =
createInlineExpressionParser("t_order_${'xx'}").splitAndEvaluate();
assertThat(expected.size(), is(1));
assertThat(expected, hasItems("t_order_xx"));
}
@Test
void assertEvaluateForArray() {
- List<String> expected =
TypedSPILoader.getService(InlineExpressionParser.class, "ESPRESSO",
PropertiesBuilder.build(
- new
PropertiesBuilder.Property(InlineExpressionParser.INLINE_EXPRESSION_KEY,
"t_order_${[0, 1, 2]},t_order_item_${[0, 2]}"))).splitAndEvaluate();
+ List<String> expected = createInlineExpressionParser("t_order_${[0, 1,
2]},t_order_item_${[0, 2]}").splitAndEvaluate();
assertThat(expected.size(), is(5));
assertThat(expected, hasItems("t_order_0", "t_order_1", "t_order_2",
"t_order_item_0", "t_order_item_2"));
}
@Test
void assertEvaluateForRange() {
- List<String> expected =
TypedSPILoader.getService(InlineExpressionParser.class, "ESPRESSO",
PropertiesBuilder.build(
- new
PropertiesBuilder.Property(InlineExpressionParser.INLINE_EXPRESSION_KEY,
"t_order_${0..2},t_order_item_${0..1}"))).splitAndEvaluate();
+ List<String> expected =
createInlineExpressionParser("t_order_${0..2},t_order_item_${0..1}").splitAndEvaluate();
assertThat(expected.size(), is(5));
assertThat(expected, hasItems("t_order_0", "t_order_1", "t_order_2",
"t_order_item_0", "t_order_item_1"));
}
@Test
void assertEvaluateForComplex() {
- List<String> expected =
TypedSPILoader.getService(InlineExpressionParser.class, "ESPRESSO",
PropertiesBuilder.build(
- new
PropertiesBuilder.Property(InlineExpressionParser.INLINE_EXPRESSION_KEY,
"t_${['new','old']}_order_${1..2}, t_config"))).splitAndEvaluate();
+ List<String> expected =
createInlineExpressionParser("t_${['new','old']}_order_${1..2},
t_config").splitAndEvaluate();
assertThat(expected.size(), is(5));
assertThat(expected, hasItems("t_new_order_1", "t_new_order_2",
"t_old_order_1", "t_old_order_2", "t_config"));
}
@Test
void assertEvaluateForCalculate() {
- List<String> expected =
TypedSPILoader.getService(InlineExpressionParser.class, "ESPRESSO",
PropertiesBuilder.build(
- new
PropertiesBuilder.Property(InlineExpressionParser.INLINE_EXPRESSION_KEY,
"t_${[\"new${1+2}\",'old']}_order_${1..2}"))).splitAndEvaluate();
+ List<String> expected =
createInlineExpressionParser("t_${[\"new${1+2}\",'old']}_order_${1..2}").splitAndEvaluate();
assertThat(expected.size(), is(4));
assertThat(expected, hasItems("t_new3_order_1", "t_new3_order_2",
"t_old_order_1", "t_old_order_2"));
}
@Test
void assertEvaluateForExpressionPlaceHolder() {
- List<String> expected =
TypedSPILoader.getService(InlineExpressionParser.class, "ESPRESSO",
PropertiesBuilder.build(
- new
PropertiesBuilder.Property(InlineExpressionParser.INLINE_EXPRESSION_KEY,
"t_$->{[\"new$->{1+2}\",'old']}_order_$->{1..2}"))).splitAndEvaluate();
+ List<String> expected =
createInlineExpressionParser("t_$->{[\"new$->{1+2}\",'old']}_order_$->{1..2}").splitAndEvaluate();
assertThat(expected.size(), is(4));
assertThat(expected, hasItems("t_new3_order_1", "t_new3_order_2",
"t_old_order_1", "t_old_order_2"));
}
@@ -123,24 +116,26 @@ class EspressoInlineExpressionParserTest {
expression.append(",");
}
}
- List<String> expected =
TypedSPILoader.getService(InlineExpressionParser.class, "ESPRESSO",
PropertiesBuilder.build(
- new
PropertiesBuilder.Property(InlineExpressionParser.INLINE_EXPRESSION_KEY,
expression.toString()))).splitAndEvaluate();
+ List<String> expected =
createInlineExpressionParser(expression.toString()).splitAndEvaluate();
assertThat(expected.size(), is(1024));
assertThat(expected, hasItems("ds_0.t_user_0", "ds_15.t_user_1023"));
}
@Test
void assertHandlePlaceHolder() {
- assertThat(TypedSPILoader.getService(InlineExpressionParser.class,
"ESPRESSO", PropertiesBuilder.build(
- new
PropertiesBuilder.Property(InlineExpressionParser.INLINE_EXPRESSION_KEY,
"t_$->{[\"new$->{1+2}\"]}"))).handlePlaceHolder(), is("t_${[\"new${1+2}\"]}"));
- assertThat(TypedSPILoader.getService(InlineExpressionParser.class,
"ESPRESSO", PropertiesBuilder.build(
- new
PropertiesBuilder.Property(InlineExpressionParser.INLINE_EXPRESSION_KEY,
"t_${[\"new$->{1+2}\"]}"))).handlePlaceHolder(), is("t_${[\"new${1+2}\"]}"));
+ String expectdString = "t_${[\"new${1+2}\"]}";
+
assertThat(createInlineExpressionParser("t_$->{[\"new$->{1+2}\"]}").handlePlaceHolder(),
is(expectdString));
+
assertThat(createInlineExpressionParser("t_${[\"new$->{1+2}\"]}").handlePlaceHolder(),
is(expectdString));
}
@Test
void assertEvaluateWithArgs() {
- assertThrows(UnsupportedOperationException.class, () ->
TypedSPILoader.getService(
- InlineExpressionParser.class, "ESPRESSO",
- PropertiesBuilder.build(new
PropertiesBuilder.Property(InlineExpressionParser.INLINE_EXPRESSION_KEY,
"${1+2}"))).evaluateWithArgs(new LinkedHashMap<>()));
+ assertThrows(UnsupportedOperationException.class, () ->
createInlineExpressionParser("${1+2}").evaluateWithArgs(Collections.emptyMap()));
+ }
+
+ private InlineExpressionParser createInlineExpressionParser(final String
expression) {
+ Properties props = new Properties();
+ props.put(InlineExpressionParser.INLINE_EXPRESSION_KEY, expression);
+ return TypedSPILoader.getService(InlineExpressionParser.class,
"ESPRESSO", props);
}
}
diff --git a/pom.xml b/pom.xml
index 7ce3705bfdb..228aa37cfa2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -83,7 +83,7 @@
<json-smart.version>2.4.10</json-smart.version>
<accessors-smart.version>2.4.9</accessors-smart.version>
<asm.version>9.3</asm.version>
- <groovy.version>4.0.10</groovy.version>
+ <groovy.version>4.0.19</groovy.version>
<freemarker.version>2.3.31</freemarker.version>
<bytebuddy.version>1.14.8</bytebuddy.version>