This is an automated email from the ASF dual-hosted git repository.
jianglongtao 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 b317330722f Bump GraalVM SDK version to 23.1.2 to allow
`infra-expr-espresso` to be used under Hotspot JDK 21 (#30026)
b317330722f is described below
commit b317330722f92b18702f7e46f4749fbebe62b859
Author: Ling Hengqian <[email protected]>
AuthorDate: Tue Feb 6 22:26:55 2024 +0800
Bump GraalVM SDK version to 23.1.2 to allow `infra-expr-espresso` to be
used under Hotspot JDK 21 (#30026)
---
.../common-config/builtin-algorithm/expr.cn.md | 19 ++--
.../common-config/builtin-algorithm/expr.en.md | 21 ++--
infra/expr/type/espresso/pom.xml | 14 ++-
.../espresso/EspressoInlineExpressionParser.java | 63 +++++++-----
.../infra/expr/espresso/ReflectContext.java | 76 ++++++++++++++
.../infra/expr/espresso/ReflectValue.java | 111 +++++++++++++++++++++
.../EspressoInlineExpressionParserTest.java | 5 +-
pom.xml | 2 +-
8 files changed, 266 insertions(+), 45 deletions(-)
diff --git
a/docs/document/content/user-manual/common-config/builtin-algorithm/expr.cn.md
b/docs/document/content/user-manual/common-config/builtin-algorithm/expr.cn.md
index 8afb23e8319..ddc72556c96 100644
---
a/docs/document/content/user-manual/common-config/builtin-algorithm/expr.cn.md
+++
b/docs/document/content/user-manual/common-config/builtin-algorithm/expr.cn.md
@@ -73,7 +73,7 @@ weight = 10
## 基于 GraalVM Truffle 的 Espresso 实现的使用 Groovy 语法的行表达式
-此为可选实现,你需要在自有项目的 `pom.xml` 主动声明如下依赖。并且请确保自有项目通过 GraalVM CE 23.0.1 For JDK
17.0.9 编译。
+此为可选实现,你需要在自有项目的 `pom.xml` 主动声明如下依赖。并且请确保自有项目通过 OpenJDK 21+ 或其下游发行版编译。
```xml
<dependencies>
@@ -82,15 +82,20 @@ weight = 10
<artifactId>shardingsphere-infra-expr-espresso</artifactId>
<version>${shardingsphere.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.graalvm.polyglot</groupId>
+ <artifactId>polyglot</artifactId>
+ <version>23.1.2</version>
+ </dependency>
+ <dependency>
+ <groupId>org.graalvm.polyglot</groupId>
+ <artifactId>java-community</artifactId>
+ <version>23.1.2</version>
+ <type>pom</type>
+ </dependency>
</dependencies>
```
-用户必须通过 GraalVM Updater 安装 Espresso 组件,即在 bash 执行如下命令
-
-```bash
-gu install espresso
-```
-
`ESPRESSO` 仍为实验性模块,其允许在 GraalVM Native Image 下通过 GraalVM Truffle 的 Espresso
实现来使用带 Groovy 语法的行表达式。
语法部分与 `GROOVY` 实现规则相同。
diff --git
a/docs/document/content/user-manual/common-config/builtin-algorithm/expr.en.md
b/docs/document/content/user-manual/common-config/builtin-algorithm/expr.en.md
index 16dc74a236f..4b9bf2fa800 100644
---
a/docs/document/content/user-manual/common-config/builtin-algorithm/expr.en.md
+++
b/docs/document/content/user-manual/common-config/builtin-algorithm/expr.en.md
@@ -82,8 +82,8 @@ Example:
## Row Value Expressions that uses the Groovy syntax based on GraalVM
Truffle's Espresso implementation
-This is an optional implementation, and you need to actively declare the
following dependencies in the `pom.xml` of your own project.
-And make sure your own project is compiled with GraalVM CE 23.0.1 For JDK
17.0.9.
+This is an optional implementation. You need to actively declare the following
dependencies in the `pom.xml` of your own project.
+And please make sure your own projects are compiled with OpenJDK 21+ or its
downstream distribution.
```xml
<dependencies>
@@ -92,15 +92,20 @@ And make sure your own project is compiled with GraalVM CE
23.0.1 For JDK 17.0.9
<artifactId>shardingsphere-infra-expr-espresso</artifactId>
<version>${shardingsphere.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.graalvm.polyglot</groupId>
+ <artifactId>polyglot</artifactId>
+ <version>23.1.2</version>
+ </dependency>
+ <dependency>
+ <groupId>org.graalvm.polyglot</groupId>
+ <artifactId>java-community</artifactId>
+ <version>23.1.2</version>
+ <type>pom</type>
+ </dependency>
</dependencies>
```
-The user must install the Espresso component via GraalVM Updater, i.e. execute
the following command in bash
-
-```bash
-gu install espresso
-```
-
`ESPRESSO` is still an experimental module that allows the use of Row Value
Expressions with Groovy syntax under GraalVM
Native Image through the Espresso implementation of GraalVM Truffle.
diff --git a/infra/expr/type/espresso/pom.xml b/infra/expr/type/espresso/pom.xml
index 4b04bfdb79f..d3681f30bbd 100644
--- a/infra/expr/type/espresso/pom.xml
+++ b/infra/expr/type/espresso/pom.xml
@@ -50,9 +50,17 @@
</dependency>
<dependency>
- <groupId>org.graalvm.truffle</groupId>
- <artifactId>truffle-api</artifactId>
+ <groupId>org.graalvm.polyglot</groupId>
+ <artifactId>polyglot</artifactId>
<version>${graal-sdk.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.graalvm.polyglot</groupId>
+ <artifactId>java-community</artifactId>
+ <version>${graal-sdk.version}</version>
+ <type>pom</type>
+ <scope>test</scope>
</dependency>
</dependencies>
@@ -69,7 +77,7 @@
</goals>
<phase>process-resources</phase>
<configuration>
-
<outputDirectory>${project.build.directory}/classes/espresso-need-libs</outputDirectory>
+
<outputDirectory>${project.build.directory}/classes/build/libs/</outputDirectory>
<artifactItems>
<artifactItem>
<groupId>org.apache.groovy</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 4aa6871d354..748d79d151a 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
@@ -22,8 +22,6 @@ import com.google.common.collect.Sets;
import groovy.lang.GroovyShell;
import org.apache.shardingsphere.infra.expr.spi.InlineExpressionParser;
import org.apache.shardingsphere.infra.util.groovy.GroovyUtils;
-import org.graalvm.polyglot.Context;
-import org.graalvm.polyglot.Value;
import java.io.File;
import java.net.URL;
@@ -45,19 +43,10 @@ public final class EspressoInlineExpressionParser
implements InlineExpressionPar
private String inlineExpression;
- /**
- * TODO <a href="https://github.com/oracle/graal/issues/4555">espressoHome
not defined</a> not yet closed.
- * Maybe sometimes we need `.option("java.Properties.org.graalvm.home",
System.getenv("JAVA_HOME"))`
- *
- * @see org.graalvm.polyglot.Context
- */
- private final Context context = Context.newBuilder()
- .allowAllAccess(true)
- .option("java.Classpath", JAVA_CLASSPATH)
- .build();
+ private final ReflectContext context = new ReflectContext(JAVA_CLASSPATH);
static {
- URL resource =
EspressoInlineExpressionParser.class.getClassLoader().getResource("espresso-need-libs");
+ URL resource = ClassLoader.getSystemResource("build" + File.separator
+ "libs");
String dir = null == resource ? null : resource.getPath();
JAVA_CLASSPATH = dir + File.separator + "groovy.jar";
}
@@ -84,11 +73,11 @@ public final class EspressoInlineExpressionParser
implements InlineExpressionPar
@Override
public List<String> splitAndEvaluate() {
- return Strings.isNullOrEmpty(inlineExpression) ?
Collections.emptyList() :
flatten(evaluate(GroovyUtils.split(handlePlaceHolder(inlineExpression)),
context));
+ return Strings.isNullOrEmpty(inlineExpression) ?
Collections.emptyList() :
flatten(evaluate(GroovyUtils.split(handlePlaceHolder(inlineExpression))));
}
- private List<Value> evaluate(final List<String> inlineExpressions, final
Context context) {
- List<Value> result = new ArrayList<>(inlineExpressions.size());
+ private List<ReflectValue> evaluate(final List<String> inlineExpressions) {
+ List<ReflectValue> result = new ArrayList<>(inlineExpressions.size());
for (String each : inlineExpressions) {
StringBuilder expression = new
StringBuilder(handlePlaceHolder(each));
if (!each.startsWith("\"")) {
@@ -97,12 +86,12 @@ public final class EspressoInlineExpressionParser
implements InlineExpressionPar
if (!each.endsWith("\"")) {
expression.append('"');
}
- result.add(evaluate(expression.toString(), context));
+ result.add(evaluate(expression.toString()));
}
return result;
}
- private Value evaluate(final String expression, final Context context) {
+ private ReflectValue evaluate(final String expression) {
return context.getBindings("java")
.getMember(GroovyShell.class.getName())
.newInstance()
@@ -110,19 +99,32 @@ public final class EspressoInlineExpressionParser
implements InlineExpressionPar
.invokeMember("run");
}
- private List<String> flatten(final List<Value> segments) {
+ /**
+ * Flatten.
+ *
+ * @param segments Actually corresponds to some class instance of {@link
java.lang.Object}.
+ * This Object may or may not correspond to a class
instance of {@link groovy.lang.GString}.
+ * @return List of String
+ */
+ private List<String> flatten(final List<ReflectValue> segments) {
List<String> result = new ArrayList<>();
- for (Value each : segments) {
+ for (ReflectValue each : segments) {
if (!each.isString()) {
result.addAll(assemblyCartesianSegments(each));
} else {
- result.add(each.toString());
+ result.add(each.toStringForValue());
}
}
return result;
}
- private List<String> assemblyCartesianSegments(final Value segment) {
+ /**
+ * Assembly cartesian segments.
+ *
+ * @param segment Actually corresponds to a class instance of {@link
groovy.lang.GString}.
+ * @return List of String
+ */
+ private List<String> assemblyCartesianSegments(final ReflectValue segment)
{
Set<List<String>> cartesianValues = getCartesianValues(segment);
List<String> result = new ArrayList<>(cartesianValues.size());
for (List<String> each : cartesianValues) {
@@ -131,8 +133,14 @@ public final class EspressoInlineExpressionParser
implements InlineExpressionPar
return result;
}
+ /**
+ * Get cartesian values.
+ *
+ * @param segment Actually corresponds to a class instance of {@link
groovy.lang.GString}.
+ * @return A Set consisting of a List of Strings
+ */
@SuppressWarnings("unchecked")
- private Set<List<String>> getCartesianValues(final Value segment) {
+ 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) {
@@ -148,7 +156,14 @@ public final class EspressoInlineExpressionParser
implements InlineExpressionPar
return Sets.cartesianProduct(result);
}
- private String assemblySegment(final List<String> cartesianValue, final
Value segment) {
+ /**
+ * Assembly segment.
+ *
+ * @param cartesianValue List of String
+ * @param segment Actually corresponds to a class instance of
{@link groovy.lang.GString}.
+ * @return {@link java.lang.String}
+ */
+ private String assemblySegment(final List<String> cartesianValue, final
ReflectValue segment) {
String[] temp = segment.invokeMember("getStrings").as(String[].class);
StringBuilder result = new StringBuilder();
for (int i = 0; i < temp.length; 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
new file mode 100644
index 00000000000..a187382d943
--- /dev/null
+++
b/infra/expr/type/espresso/src/main/java/org/apache/shardingsphere/infra/expr/espresso/ReflectContext.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.infra.expr.espresso;
+
+import lombok.SneakyThrows;
+
+/**
+ * Reflect Context.
+ * Avoid using JDK21 bytecode during compilation. Refer to
`org.graalvm.polyglot.Context`.
+ */
+public final class ReflectContext {
+
+ private static final String CONTEXT_CLASS_NAME =
"org.graalvm.polyglot.Context";
+
+ private final Object contextInstance;
+
+ /**
+ * This method is a simulation of the following operation.
+ * // CHECKSTYLE:OFF
+ * <pre class="code">
+ * private final Context context = Context.newBuilder()
+ * .allowAllAccess(true)
+ * .option("java.Classpath", JAVA_CLASSPATH)
+ * .build();
+ * </pre>
+ * // CHECKSTYLE:ON
+ * TODO <a
href="https://github.com/oracle/graal/issues/4555">oracle/graal#4555</a> not
yet closed.
+ * Maybe sometimes shardingsphere need
`.option("java.Properties.org.graalvm.home", System.getenv("JAVA_HOME")).
+ *
+ * @param javaClassPath java class path
+ */
+ @SneakyThrows
+ public ReflectContext(final String javaClassPath) {
+ Object builderInstance = Class.forName(CONTEXT_CLASS_NAME)
+ .getMethod("newBuilder", String[].class)
+ .invoke(null, (Object) new String[]{});
+ builderInstance = builderInstance.getClass()
+ .getMethod("allowAllAccess", boolean.class)
+ .invoke(builderInstance, true);
+ builderInstance = builderInstance.getClass()
+ .getMethod("option", String.class, String.class)
+ .invoke(builderInstance, "java.Classpath", javaClassPath);
+ contextInstance = builderInstance.getClass()
+ .getMethod("build")
+ .invoke(builderInstance);
+ }
+
+ /**
+ * Returns a value that represents the top-most bindings of a language.
+ *
+ * @param languageId languageId
+ * @return {@link
org.apache.shardingsphere.infra.expr.espresso.ReflectValue}
+ */
+ @SneakyThrows
+ public ReflectValue getBindings(final String languageId) {
+ Object valueInstance = Class.forName(CONTEXT_CLASS_NAME)
+ .getMethod("getBindings", String.class)
+ .invoke(contextInstance, languageId);
+ return new ReflectValue(valueInstance);
+ }
+}
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
new file mode 100644
index 00000000000..e2290e6e598
--- /dev/null
+++
b/infra/expr/type/espresso/src/main/java/org/apache/shardingsphere/infra/expr/espresso/ReflectValue.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.infra.expr.espresso;
+
+import lombok.SneakyThrows;
+
+/**
+ * Reflect Value.
+ * Avoid using JDK21 bytecode during compilation. Refer to
`org.graalvm.polyglot.Value`.
+ */
+public class ReflectValue {
+
+ private static final String VALUE_CLASS_NAME =
"org.graalvm.polyglot.Value";
+
+ private final Object valueInstance;
+
+ public ReflectValue(final Object valueInstance) {
+ this.valueInstance = valueInstance;
+ }
+
+ /**
+ * Returns the member with a given identifier or null if the member does
not exist.
+ * @param identifier the member identifier
+ * @return {@link
org.apache.shardingsphere.infra.expr.espresso.ReflectValue}
+ */
+ @SneakyThrows
+ public ReflectValue getMember(final String identifier) {
+ Object resultValueInstance = Class.forName(VALUE_CLASS_NAME)
+ .getMethod("getMember", String.class)
+ .invoke(valueInstance, identifier);
+ return new ReflectValue(resultValueInstance);
+ }
+
+ /**
+ * Instantiates this value if it can be instantiated.
+ * @param arguments the arguments
+ * @return {@link
org.apache.shardingsphere.infra.expr.espresso.ReflectValue}
+ */
+ @SneakyThrows
+ public ReflectValue newInstance(final Object... arguments) {
+ Object resultValueInstance = Class.forName(VALUE_CLASS_NAME)
+ .getMethod("newInstance", Object[].class)
+ .invoke(valueInstance, (Object) arguments);
+ return new ReflectValue(resultValueInstance);
+ }
+
+ /**
+ * Invokes the given member of this value.
+ * @param identifier the member identifier to invoke
+ * @param arguments the invocation arguments
+ * @return {@link
org.apache.shardingsphere.infra.expr.espresso.ReflectValue}
+ */
+ @SneakyThrows
+ public ReflectValue invokeMember(final String identifier, final Object...
arguments) {
+ Object resultValueInstance = Class.forName(VALUE_CLASS_NAME)
+ .getMethod("invokeMember", String.class, Object[].class)
+ .invoke(valueInstance, identifier, arguments);
+ return new ReflectValue(resultValueInstance);
+ }
+
+ /**
+ * Returns true if this value represents a string.
+ * @return Returns true if this value represents a string.
+ */
+ @SneakyThrows
+ public boolean isString() {
+ return (boolean) Class.forName(VALUE_CLASS_NAME)
+ .getMethod("isString")
+ .invoke(valueInstance);
+ }
+
+ /**
+ * Maps a polyglot value to a value with a given Java target type.
+ * @param targetType the target Java type to map
+ * @param <T> target type
+ * @return target type
+ */
+ @SneakyThrows
+ @SuppressWarnings("unchecked")
+ public <T> T as(final Class<T> targetType) {
+ return (T) Class.forName(VALUE_CLASS_NAME)
+ .getMethod("as", Class.class)
+ .invoke(valueInstance, targetType);
+ }
+
+ /**
+ * Converts this value to a human-readable string.
+ * @return {@link String}
+ */
+ @SneakyThrows
+ public String toStringForValue() {
+ return (String) Class.forName(VALUE_CLASS_NAME)
+ .getMethod("toString")
+ .invoke(valueInstance);
+ }
+}
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 594e58b51da..325bdbf7f00 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
@@ -20,8 +20,9 @@ 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.Disabled;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledForJreRange;
+import org.junit.jupiter.api.condition.JRE;
import java.util.Collections;
import java.util.List;
@@ -32,7 +33,7 @@ import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
-@Disabled("Unit tests for this class only run on GraalVM CE 23.0.1 For JDK17.
Wait for https://github.com/oracle/graal/issues/7500 .")
+@EnabledForJreRange(min = JRE.JAVA_21)
class EspressoInlineExpressionParserTest {
@Test
diff --git a/pom.xml b/pom.xml
index 14523adb04a..a6978378cf0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -146,7 +146,7 @@
<testcontainers.version>1.19.3</testcontainers.version>
<commons-csv.version>1.9.0</commons-csv.version>
- <graal-sdk.version>21.2.0</graal-sdk.version>
+ <graal-sdk.version>23.1.2</graal-sdk.version>
<!-- 3rd party library plugin versions -->
<protobuf-maven-plugin.version>0.6.1</protobuf-maven-plugin.version>