From 723f869801794e0bcfb2693e576af91468443ac0 Mon Sep 17 00:00:00 2001 From: Oleksandr Porunov Date: Mon, 29 Jun 2026 14:53:54 +0100 Subject: [PATCH] Support Java 21/25 and Spark 4.1, raise minimum to Java 17 Modernizes TinkerPop 4.0 to build and run on Java 21 and Java 25 and upgrades the Spark OLAP integration to Spark 4.1. Because Spark 4 requires Java 17 as a minimum, the project-wide minimum Java version is raised from 11 to 17. Every LTS release from the new minimum upward (17, 21, 25) is covered by CI. Java 21/25 support: - Groovy 4.0.25 -> 4.0.32 (bundled ASM parses Java 25 bytecode) - StringFactory: detect the Java 21+ lambda class name format ($$Lambda/) - LambdaRestrictionStrategy: match "lambda" case-insensitively - ImportGremlinPluginTest: account for Math.TAU added in Java 19 - gremlin-groovy: declare annotationProcessorPaths explicitly (JDK 23+ no longer runs classpath-discovered processors) and widen the javadoc groovy-stubs profile to all supported JDKs - gremlin-console: apply the neo4j-gremlin --add-opens flags for JDK 17+ Dependency upgrades: - Hadoop 3.4.2 -> 3.4.3 (uses the Subject.current()/callAs() replacement APIs) - Spark 3.5.4 -> 4.1.2, which is Scala 2.13 only, the first Spark line to support Java 25, and the second to support Java 21 (SPARK-43831 landed in 4.0): spark-core_2.12 -> _2.13, Scala 2.13 source migration (JavaConversions -> CollectionConverters, WrappedArray -> immutable ArraySeq), and aligned Netty, jackson (via jackson-bom) and other transitive versions that Spark 4 requires - Netty 4.1.125 -> 4.2.7 (required by Spark 4) Java 17 baseline (breaking): - enforcer requireJavaVersion [11,18) -> [17,26); compiler release 11 -> 17 (and gremlin-groovy/gremlin-annotations release 8 -> 17) - build-test.yml: all jobs run on Java 17, plus new Java 21 and Java 25 jobs; the Java 11 job is removed and coverage runs on the Java 17 job - Dockerfiles updated to Java 17; developer, reference, and upgrade docs list Java 17 as the minimum with Java 21 and 25 supported Integration-test fixes for the Java 17 baseline (deep reflection into java.base is denied by default on Java 17, and TLS 1.3 is the default): - gremlin-server.sh: append the jdk17 --add-opens/--add-exports set to JAVA_OPTIONS so the standalone/Docker server can perform the deep reflection Kryo/Gryo needs - GremlinServerSslIntegrateTest: accept the alerts Java 17's TLS 1.3 raises for a missing/untrusted client certificate - gremlin-socket-server and gremlin-console test Docker images use Java 17 Co-Authored-By: Claude Opus 4.8 (1M context) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_014dY2jvS3v5PrLnNEGKjw6y Signed-off-by: Oleksandr Porunov --- .github/workflows/build-test.yml | 80 +++++++++++-------- CHANGELOG.asciidoc | 3 + docker/Dockerfile | 4 +- .../development-environment.asciidoc | 9 ++- docs/src/reference/gremlin-variants.asciidoc | 1 + docs/src/upgrade/release-4.x.x.asciidoc | 14 ++++ gremlin-annotations/pom.xml | 6 +- gremlin-console/Dockerfile | 2 +- gremlin-console/pom.xml | 4 +- gremlin-console/src/main/bin/gremlin.sh | 5 +- .../src/test/python/docker/Dockerfile | 2 +- .../LambdaRestrictionStrategy.java | 2 +- .../gremlin/structure/util/StringFactory.java | 3 +- .../jsr223/ImportGremlinPluginTest.java | 11 ++- gremlin-groovy/pom.xml | 46 +++++++++-- gremlin-server/Dockerfile | 2 +- gremlin-server/src/main/bin/gremlin-server.sh | 19 +++++ .../server/GremlinServerSslIntegrateTest.java | 18 ++++- .../gremlin-socket-server/Dockerfile | 2 +- pom.xml | 24 +++--- spark-gremlin/pom.xml | 49 ++++++++++-- .../gremlin/spark/structure/Spark.java | 4 +- .../structure/io/gryo/GryoRegistrator.java | 4 +- .../structure/io/gryo/GryoSerializer.java | 4 +- .../io/gryo/WrappedArraySerializer.java | 21 ++--- .../gremlin/spark/structure/SparkTest.java | 6 +- 26 files changed, 242 insertions(+), 103 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 2efb1dff5e5..e5f1a0dc296 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -16,10 +16,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v5 with: - java-version: '11' + java-version: '17' distribution: 'temurin' - name: Build with Maven run: mvn clean install -DskipTests -Dci --batch-mode -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn @@ -36,25 +36,39 @@ jobs: java-version: '17' distribution: 'temurin' - name: Build with Maven - run: mvn clean install -pl $EXCLUDE_MODULES -Dci --batch-mode -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn - java-jdk11: - name: mvn clean install - jdk11 + run: mvn clean install -pl $EXCLUDE_MODULES -Dci --batch-mode -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn -Dcoverage + - name: Upload to Codecov + uses: codecov/codecov-action@v7 + with: + directory: ./gremlin-tools/gremlin-coverage/target/site + java-jdk21: + name: mvn clean install - jdk21 timeout-minutes: 45 needs: smoke runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - - name: Set up JDK 11 + - name: Set up JDK 21 uses: actions/setup-java@v5 with: - java-version: '11' + java-version: '21' distribution: 'temurin' - name: Build with Maven - run: mvn clean install -pl $EXCLUDE_MODULES -Dci --batch-mode -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn -Dcoverage - - name: Upload to Codecov - uses: codecov/codecov-action@v7 + run: mvn clean install -pl $EXCLUDE_MODULES -Dci --batch-mode -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn + java-jdk25: + name: mvn clean install - jdk25 + timeout-minutes: 45 + needs: smoke + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - name: Set up JDK 25 + uses: actions/setup-java@v5 with: - directory: ./gremlin-tools/gremlin-coverage/target/site + java-version: '25' + distribution: 'temurin' + - name: Build with Maven + run: mvn clean install -pl $EXCLUDE_MODULES -Dci --batch-mode -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn gremlin-server-default: name: gremlin-server default timeout-minutes: 45 @@ -62,10 +76,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v5 with: - java-version: '11' + java-version: '17' distribution: 'temurin' - name: Build with Maven run: | @@ -78,10 +92,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v5 with: - java-version: '11' + java-version: '17' distribution: 'temurin' - name: Generate Gremlin Server Base working-directory: . @@ -114,10 +128,10 @@ jobs: # runs-on: ubuntu-latest # steps: # - uses: actions/checkout@v7 -# - name: Set up JDK 11 +# - name: Set up JDK 17 # uses: actions/setup-java@v5 # with: -# java-version: '11' +# java-version: '17' # distribution: 'temurin' # - name: Build with Maven # run: | @@ -130,10 +144,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v5 with: - java-version: '11' + java-version: '17' distribution: 'temurin' - name: Build with Maven run: | @@ -149,10 +163,10 @@ jobs: os: [ubuntu-latest, windows-latest] steps: - uses: actions/checkout@v7 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v5 with: - java-version: '11' + java-version: '17' distribution: 'temurin' - name: Build with Maven Windows if: runner.os == 'Windows' @@ -173,10 +187,10 @@ jobs: os: [ubuntu-latest, windows-latest] steps: - uses: actions/checkout@v7 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v5 with: - java-version: '11' + java-version: '17' distribution: 'temurin' - name: Build with Maven Windows if: runner.os == 'Windows' @@ -194,10 +208,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v5 with: - java-version: '11' + java-version: '17' distribution: 'temurin' - name: Build with Maven run: | @@ -211,10 +225,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v5 with: - java-version: '11' + java-version: '17' distribution: 'temurin' - name: Build with Maven run: | @@ -231,10 +245,10 @@ jobs: node-version: [ '22', '24', '26' ] steps: - uses: actions/checkout@v7 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v5 with: - java-version: '11' + java-version: '17' distribution: 'temurin' - name: Get Cached Server Base Image uses: actions/cache@v6 @@ -265,10 +279,10 @@ jobs: PYTHON_VERSION: ${{ matrix.python-version }} steps: - uses: actions/checkout@v7 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v5 with: - java-version: '11' + java-version: '17' distribution: 'temurin' - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v6 @@ -287,10 +301,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - - name: Set up JDK11 + - name: Set up JDK17 uses: actions/setup-java@v5 with: - java-version: '11' + java-version: '17' distribution: 'temurin' - name: Set up .NET 8.0.x uses: actions/setup-dotnet@v5 diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 9632d16aee7..8bb8aacf593 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -25,6 +25,9 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima [[release-4-0-0]] === TinkerPop 4.0.0 (Release Date: NOT OFFICIALLY RELEASED YET) +* Raised the minimum Java version to 17 for building and running *(breaking)*. +* Enabled building and running with Java 21 and Java 25. +* Bumped Groovy to 4.0.32, Hadoop to 3.4.3, Spark to 4.1.2 (Scala 2.13), and Netty to 4.2.7 to support Java 25. * Standardized connection options across all GLVs (Java, Python, .NET, Go, JavaScript) per the TinkerPop 4.x GLV proposal; see the upgrade docs for the full per-driver table. *(breaking)* ** Aligned option names across drivers: `maxConnections` (128), `connectTimeoutMillis` (5000), `readTimeoutMillis` (off), `idleTimeoutMillis` (180000), `keepAliveTimeMillis` (30000), `compression` (on/`deflate`), `batchSize` (64), `bulkResults` (false), `maxResponseHeaderBytes`, `proxy`, and `ssl`. Timeouts use a millisecond-suffixed canonical name with an idiomatic duration companion (Java `Duration`, Go `time.Duration`, .NET `TimeSpan`, Python seconds); JavaScript uses milliseconds only. ** Java: renamed `maxConnectionPoolSize`/`connectionSetupTimeoutMillis`/`idleConnectionTimeoutMillis`/`resultIterationBatchSize` and `RequestOptions.addG`->`traversalSource`; removed `maxResponseContentLength` (responses now stream); added `readTimeoutMillis`, `keepAliveTimeMillis`, `maxResponseHeaderBytes`, `proxy`, `url(String)`, `ssl(SslContext)`. *(breaking)* diff --git a/docker/Dockerfile b/docker/Dockerfile index 66a375384bd..921c80ee689 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -29,7 +29,7 @@ RUN apt-get install apt-transport-https gnupg ca-certificates RUN sh -c 'echo "deb https://download.mono-project.com/repo/ubuntu stable-focal main" | tee /etc/apt/sources.list.d/mono-official-stable.list' RUN apt-get update -RUN apt-get install -y openjdk-11-jdk gawk git maven openssh-server subversion zip +RUN apt-get install -y openjdk-17-jdk gawk git maven openssh-server subversion zip RUN apt-get install -y --force-yes dotnet-sdk-8.0 mono-devel ENV DEBIAN_FRONTEND=noninteractive @@ -43,7 +43,7 @@ RUN python3 -m pip install --upgrade pip RUN pip install virtualenv RUN pip install virtualenvwrapper --no-deps -RUN rm -rf /var/lib/apt/lists/* /var/cache/openjdk-11-jdk +RUN rm -rf /var/lib/apt/lists/* /var/cache/openjdk-17-jdk RUN sed -i 's@PermitRootLogin without-password@PermitRootLogin yes@' /etc/ssh/sshd_config RUN sed -i 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuuid.so@g' /etc/pam.d/sshd diff --git a/docs/src/dev/developer/development-environment.asciidoc b/docs/src/dev/developer/development-environment.asciidoc index e0a95592180..c7748ac7280 100644 --- a/docs/src/dev/developer/development-environment.asciidoc +++ b/docs/src/dev/developer/development-environment.asciidoc @@ -28,8 +28,9 @@ image:conspiracy.png[] [[system-configuration]] == System Configuration -At a minimum, development of TinkerPop requires link:https://openjdk.java.net/projects/jdk/11/[Java 11] or -link:https://openjdk.org/projects/jdk/17/[Java 17]. Note, however, that there are some issues with deep reflection +At a minimum, development of TinkerPop requires link:https://openjdk.org/projects/jdk/17/[Java 17]; builds and tests +also run on link:https://openjdk.org/projects/jdk/21/[Java 21] and link:https://openjdk.org/projects/jdk/25/[Java 25]. +Note, however, that there are some issues with deep reflection so there are `--add-opens` JVM options included in the pom files to enable this to work. Maven (requiring a minimum of link:https://maven.apache.org/download.cgi[Maven 3.5.3+]) is used as the common build system, which even controls the builds of non-JVM link:https://tinkerpop.apache.org/docs/x.y.z/reference/#gremlin-drivers-variants[GLVs] @@ -47,7 +48,7 @@ system. You will need to install both link:https://docs.docker.com/engine/instal link:https://docs.docker.com/compose/install/[Docker Compose], which are included in link:https://docs.docker.com/desktop/[Docker Desktop]. -IMPORTANT: Use Java 11 for documentation generation with `bin/process-docs.sh` and for other build features outside +IMPORTANT: Use Java 17 for documentation generation with `bin/process-docs.sh` and for other build features outside of the basic `mvn clean install` sort of function. IMPORTANT: For those who intend to offer a contribution, building with a minimal configuration may not be sufficient @@ -124,7 +125,7 @@ The core of the documentation generation process is an link:https://asciidoctor. Gremlin Console subprocess, and renders the console output as tabbed HTML. The orchestration script `bin/process-docs.sh` wraps this and acts as the entrypoint: it validates the Gremlin Console and Gremlin Server distributions, installs the required plugins into the console, starts a Gremlin Server (for the `:remote` examples) and -a Gephi mock, then invokes Maven to run the extension. Javadocs should be built using Java 11. +a Gephi mock, then invokes Maven to run the extension. Javadocs should be built using Java 17. Before generating documentation, build the build all of the dependencies that the process consumes: diff --git a/docs/src/reference/gremlin-variants.asciidoc b/docs/src/reference/gremlin-variants.asciidoc index 4e90afdcf61..22e21adabc0 100644 --- a/docs/src/reference/gremlin-variants.asciidoc +++ b/docs/src/reference/gremlin-variants.asciidoc @@ -902,6 +902,7 @@ The following table outlines recommended runtime versions by the release in whic |3.6.0 | Java 8, 11 |3.7.0 | Java 8, 11, 17 |3.8.0 | Java 11, 17 +|4.0.0 | Java 17, 21, 25 |=== [[gremlin-java-connecting]] diff --git a/docs/src/upgrade/release-4.x.x.asciidoc b/docs/src/upgrade/release-4.x.x.asciidoc index 1118cf3fc9e..96ded966ba7 100644 --- a/docs/src/upgrade/release-4.x.x.asciidoc +++ b/docs/src/upgrade/release-4.x.x.asciidoc @@ -106,6 +106,20 @@ These change runtime behavior on upgrade even if you do not change your configur See: link:https://lists.apache.org/thread/yqtr2wnb1kq2pqqq4002cz511q5o0bkg[[DISCUSS] Standardizing GLV connection options in TinkerPop 4]. +==== Java 17 Required; Java 21 and 25 Supported + +TinkerPop 4.0 raises the minimum Java version from 11 to 17 for both building and running, and adds support for running +on Java 21 and Java 25, so that every LTS release from the new minimum upward is covered by TinkerPop's automated +tests. *(breaking)* Supporting Java 25 required upgrading Groovy to 4.0.32 (whose bundled ASM can parse Java 25 +bytecode) and Hadoop to 3.4.3 (which moved off the `Subject.getSubject()` API that was removed along with the Security +Manager). Spark was upgraded to 4.1.x, built on Scala 2.13 — the first Spark line that supports Java 25. Because Spark 4 +requires Java 17 as its minimum, and `spark-gremlin` is part of the build, the project-wide minimum is now Java 17. +Netty was upgraded to 4.2.x as required by Spark 4. + +As with the earlier JDK 17 support, some libraries still rely on deep reflection (most notably the Kryo serialization +library used with OLAP), so it may be necessary to `--add-opens` or `--add-exports` certain modules at runtime. The set +of options used by TinkerPop's own tests is unchanged. + ==== Declarative Pattern Matching Gremlin has always offered both imperative and declarative styles to writing graph queries. While the imperative style diff --git a/gremlin-annotations/pom.xml b/gremlin-annotations/pom.xml index 8a1a83299a1..15214dbd247 100644 --- a/gremlin-annotations/pom.xml +++ b/gremlin-annotations/pom.xml @@ -95,9 +95,9 @@ limitations under the License. org.apache.maven.plugins maven-compiler-plugin - 8 - 8 - 8 + 17 + 17 + 17 -parameters -proc:none diff --git a/gremlin-console/Dockerfile b/gremlin-console/Dockerfile index 44c995ae66c..57527099978 100644 --- a/gremlin-console/Dockerfile +++ b/gremlin-console/Dockerfile @@ -23,7 +23,7 @@ ARG GREMLIN_CONSOLE_DIR RUN apk add --no-cache --update \ bash \ - openjdk11-jdk + openjdk17-jdk COPY src/main/docker/docker-entrypoint.sh / COPY ${GREMLIN_CONSOLE_DIR} /opt/gremlin-console diff --git a/gremlin-console/pom.xml b/gremlin-console/pom.xml index c167c08f79b..0c96d16aa71 100644 --- a/gremlin-console/pom.xml +++ b/gremlin-console/pom.xml @@ -384,12 +384,12 @@ limitations under the License. - + + gremlin-console-test:py3.10jre17"/> diff --git a/gremlin-console/src/main/bin/gremlin.sh b/gremlin-console/src/main/bin/gremlin.sh index 2cd33cbc043..8a245f80ef6 100755 --- a/gremlin-console/src/main/bin/gremlin.sh +++ b/gremlin-console/src/main/bin/gremlin.sh @@ -108,9 +108,10 @@ if [ -n "$SCRIPT_DEBUG" ]; then set -x fi -# Detect JDK version and add specific flags for JDK 17 +# Detect the JDK major version and add specific flags for JDK 17 and newer JAVA_VERSION=$($JAVA -version 2>&1 | awk -F '"' '/version/ {print $2}') -if [[ "$JAVA_VERSION" == 17* ]]; then +JAVA_MAJOR_VERSION=$(echo "$JAVA_VERSION" | awk -F. '{if ($1 == 1) print $2; else print $1}') +if [[ "$JAVA_MAJOR_VERSION" -ge 17 ]]; then JVM_OPTS+=( "--add-opens=java.base/sun.nio.ch=ALL-UNNAMED" ) JVM_OPTS+=( "--add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED" ) fi diff --git a/gremlin-console/src/test/python/docker/Dockerfile b/gremlin-console/src/test/python/docker/Dockerfile index e20de5991b4..f4dec1f6945 100644 --- a/gremlin-console/src/test/python/docker/Dockerfile +++ b/gremlin-console/src/test/python/docker/Dockerfile @@ -20,7 +20,7 @@ FROM python:3.10-bullseye LABEL maintainer="dev@tinkerpop.apache.org" -RUN apt-get update && apt-get install -y --no-install-recommends openjdk-11-jre && apt-get install dos2unix +RUN apt-get update && apt-get install -y --no-install-recommends openjdk-17-jre && apt-get install dos2unix WORKDIR /console_app CMD dos2unix ./gremlin-console/bin/gremlin.sh && python3 setup.py build && python3 setup.py test; \ diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/LambdaRestrictionStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/LambdaRestrictionStrategy.java index d26141e632f..5dc0c25f4c5 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/LambdaRestrictionStrategy.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/LambdaRestrictionStrategy.java @@ -51,7 +51,7 @@ private LambdaRestrictionStrategy() { @Override public void apply(final Traversal.Admin traversal) { for (final Step step : traversal.getSteps()) { - if ((step instanceof LambdaHolder || step instanceof ComparatorHolder) && step.toString().contains("lambda")) + if ((step instanceof LambdaHolder || step instanceof ComparatorHolder) && step.toString().toLowerCase().contains("lambda")) throw new VerificationException("The provided traversal contains a lambda step: " + step, traversal); } } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/StringFactory.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/StringFactory.java index 74ba38437eb..f84d83357bf 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/StringFactory.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/StringFactory.java @@ -237,7 +237,8 @@ else if (o instanceof Map) } private static boolean hasLambda(final String objectString) { - return objectString.contains("$Lambda$") || // JAVA (org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraphPlayTest$$Lambda$1/1711574013@61a52fb) + return objectString.contains("$Lambda$") || // JAVA <21 (org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraphPlayTest$$Lambda$1/1711574013@61a52fb) + objectString.contains("$$Lambda/") || // JAVA 21+ (org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraphPlayTest$$Lambda/0x00007fc001151000@61a52fb) objectString.contains("$_run_closure") || // GROOVY (groovysh_evaluate$_run_closure1@db44aa2) objectString.contains(""); // PYTHON ( at 0x10dfaec80>) } diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinPluginTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinPluginTest.java index 0a1868da747..c628f485787 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinPluginTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinPluginTest.java @@ -148,8 +148,15 @@ public void shouldImportWildcardFields() throws Exception { .fieldsImports(Collections.singletonList(Math.class.getCanonicalName() + "#*")).create(); final DefaultImportCustomizer customizer = (DefaultImportCustomizer) module.getCustomizers().get()[0]; - assertThat(customizer.getFieldImports(), is(new HashSet<>( - Arrays.asList(Math.class.getField("PI"), Math.class.getField("E"))))); + // Math.TAU was added in Java 19 + final HashSet expectedFields = new HashSet<>(Arrays.asList( + Math.class.getField("PI"), Math.class.getField("E"))); + try { + expectedFields.add(Math.class.getField("TAU")); + } catch (NoSuchFieldException ignored) { + // TAU not available in Java < 19 + } + assertThat(customizer.getFieldImports(), is(expectedFields)); } @Test diff --git a/gremlin-groovy/pom.xml b/gremlin-groovy/pom.xml index 5b7af402877..aaafa65750e 100644 --- a/gremlin-groovy/pom.xml +++ b/gremlin-groovy/pom.xml @@ -142,12 +142,44 @@ limitations under the License. org.apache.maven.plugins maven-compiler-plugin - 8 - 8 - 8 + 17 + 17 + 17 -parameters + + + org.apache.tinkerpop + gremlin-annotations + ${project.version} + + + org.apache.tinkerpop + gremlin-core + ${project.version} + + + org.apache.tinkerpop + gremlin-shaded + ${project.version} + + + org.apache.tinkerpop + gremlin-language + ${project.version} + + + com.squareup + javapoet + ${javapoet.version} + + + org.antlr + antlr4-runtime + ${antlr4.version} + + @@ -155,9 +187,9 @@ limitations under the License. - jdk11 + jdk11-and-newer - [11,18) + [11,) @@ -167,8 +199,8 @@ limitations under the License. ${pom.basedir}/src/main/java:${project.build.directory}/generated-sources/annotations:${project.build.directory}/generated-sources/groovy-stubs/main diff --git a/gremlin-server/Dockerfile b/gremlin-server/Dockerfile index 63a494d63b3..7eb7a580035 100644 --- a/gremlin-server/Dockerfile +++ b/gremlin-server/Dockerfile @@ -24,7 +24,7 @@ ARG GREMLIN_SERVER_DIR RUN apk add --no-cache --update \ bash \ perl \ - openjdk11-jdk + openjdk17-jdk COPY src/main/docker/docker-entrypoint.sh / COPY ${GREMLIN_SERVER_DIR} /opt/gremlin-server diff --git a/gremlin-server/src/main/bin/gremlin-server.sh b/gremlin-server/src/main/bin/gremlin-server.sh index 430a18c3984..438daf1ca66 100755 --- a/gremlin-server/src/main/bin/gremlin-server.sh +++ b/gremlin-server/src/main/bin/gremlin-server.sh @@ -94,6 +94,25 @@ if [[ "$JAVA_OPTIONS" = "" ]] ; then JAVA_OPTIONS="-Xms512m -Xmx4096m" fi +# These options are required for libraries that still rely on deep reflection (notably Kryo, used by Gryo) to work on +# Java 17 and newer, where such access into java.base is denied by default. They mirror the jdk17JvmArgs used by the +# build (see the root pom.xml) and are always appended so they apply even when JAVA_OPTIONS is overridden. +JAVA_OPTIONS="$JAVA_OPTIONS \ +-XX:+IgnoreUnrecognizedVMOptions \ +--add-exports=java.base/sun.nio.ch=ALL-UNNAMED \ +--add-exports=java.base/sun.security.x509=ALL-UNNAMED \ +--add-exports=java.base/sun.security.action=ALL-UNNAMED \ +--add-opens=java.base/java.io=ALL-UNNAMED \ +--add-opens=java.base/java.nio=ALL-UNNAMED \ +--add-opens=java.base/sun.nio.cs=ALL-UNNAMED \ +--add-opens=java.base/java.lang=ALL-UNNAMED \ +--add-opens=java.base/java.lang.invoke=ALL-UNNAMED \ +--add-opens=java.base/java.lang.reflect=ALL-UNNAMED \ +--add-opens=java.base/java.util=ALL-UNNAMED \ +--add-opens=java.base/java.util.concurrent=ALL-UNNAMED \ +--add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED \ +--add-opens=java.base/java.net=ALL-UNNAMED" + # Build Java CLASSPATH CP="$GREMLIN_HOME/conf/" CP="$CP":$( find "$GREMLIN_HOME/lib" -name "*.jar" -print | tr '\n' ':' ). diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerSslIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerSslIntegrateTest.java index 076d42f6e15..a2dc317f25c 100644 --- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerSslIntegrateTest.java +++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerSslIntegrateTest.java @@ -260,7 +260,9 @@ public void shouldEnableSslAndClientCertificateAuthAndFailWithoutCert() { client.submit("g.inject('test')").one(); fail("Should throw exception because ssl client auth is enabled on the server but client does not have a cert"); } catch (Exception x) { - assertSslException(x, "bad_certificate"); + // Under TLS 1.3 (default on Java 17+) a client-certificate rejection may reach the client as the specific + // alert or, depending on handshake timing, as a generic engine-closed error. + assertSslException(x, "bad_certificate", "certificate_required", "SSLEngine closed already"); } finally { cluster.close(); } @@ -276,7 +278,9 @@ public void shouldEnableSslAndClientCertificateAuthAndFailWithoutTrustedClientCe client.submit("g.inject('test')").one(); fail("Should throw exception because ssl client auth is enabled on the server but does not trust client's cert"); } catch (Exception x) { - assertSslException(x, "bad_certificate"); + // Under TLS 1.3 (default on Java 17+) a client-certificate rejection may reach the client as the specific + // alert or, depending on handshake timing, as a generic engine-closed error. + assertSslException(x, "bad_certificate", "certificate_required", "SSLEngine closed already"); } finally { cluster.close(); } @@ -342,10 +346,16 @@ public void shouldEnableSslAndClientCertificateAuthWithDifferentStoreType() { } } - private static void assertSslException(Exception x, String expectedSubstring) { + private static void assertSslException(final Exception x, final String... acceptableSubstrings) { logger.warn("Exception caught: {}", x.getMessage(), x); final Throwable root = ExceptionHelper.getRootCause(x); assertThat(root, instanceOf(SSLException.class)); - assertThat(root.getMessage(), containsString(expectedSubstring)); + final String message = root.getMessage(); + // The exact TLS alert can vary by JDK/TLS version (for example a missing client certificate is reported as + // "bad_certificate" under TLS 1.2 but "certificate_required" under the TLS 1.3 that is default on Java 17+), + // so accept any of the provided substrings. + final boolean matched = message != null && Arrays.stream(acceptableSubstrings).anyMatch(message::contains); + assertThat("Expected SSL exception message <" + message + "> to contain one of " + + Arrays.toString(acceptableSubstrings), matched, is(true)); } } diff --git a/gremlin-tools/gremlin-socket-server/Dockerfile b/gremlin-tools/gremlin-socket-server/Dockerfile index 3010f33cd64..f40c91a66d8 100644 --- a/gremlin-tools/gremlin-socket-server/Dockerfile +++ b/gremlin-tools/gremlin-socket-server/Dockerfile @@ -24,7 +24,7 @@ ARG SOCKET_SERVER_VERSION RUN apk add --no-cache --update \ bash \ - openjdk11-jdk + openjdk17-jdk COPY ${SOCKET_SERVER_DIR}/gremlin-socket-server-${SOCKET_SERVER_VERSION}.jar /opt/gremlin-socket-server/gremlin-socket-server.jar COPY ${SOCKET_SERVER_DIR}/libs/ /opt/gremlin-socket-server/libs/ diff --git a/pom.xml b/pom.xml index 9f4b9081276..8f426fc5048 100644 --- a/pom.xml +++ b/pom.xml @@ -159,7 +159,7 @@ limitations under the License. 1.15.0 7.21.1 0.4.8 - 4.0.25 + 4.0.32 6.0.0 - 3.4.2 + 3.4.3 3.0 1.2 3.3.1 @@ -180,10 +180,10 @@ limitations under the License. 1.3.15 3.0.2 5.19.0 - 4.1.125.Final + 4.2.7.Final 2.0.16 2.0 - 3.5.4 + 4.1.2 2.29.3 1.69 @@ -259,7 +259,7 @@ limitations under the License. - [11,18) + [17,26) [3.5.3,) @@ -624,9 +624,9 @@ limitations under the License. maven-compiler-plugin 3.14.0 - 11 - 11 - 11 + 17 + 17 + 17 -parameters @@ -733,7 +733,7 @@ limitations under the License. maven-javadoc-plugin ${javadoc-plugin.version} - 11 + 17 @@ -741,7 +741,7 @@ limitations under the License. gmavenplus-plugin 4.1.1 - 11 + 17 @@ -2203,7 +2203,7 @@ limitations under the License. -Dlog4j.configuration=${log4j-test.properties} -Dslf4j.provider=ch.qos.logback.classic.spi.LogbackServiceProvider -Dbuild.dir=${project.build.directory} -Dis.testing=true ${suresafeArgs} - -Djava.net.preferIPv4Stack=true + -Djava.net.preferIPv4Stack=true ${jdk17JvmArgs} @@ -2215,7 +2215,7 @@ limitations under the License. -Dlog4j.configuration=${log4j-test.properties} -Dhost=localhost -Dport=8182 -Dslf4j.provider=ch.qos.logback.classic.spi.LogbackServiceProvider -Dbuild.dir=${project.build.directory} -Dis.testing=true ${suresafeArgs} - -Djava.net.preferIPv4Stack=true + -Djava.net.preferIPv4Stack=true ${jdk17JvmArgs} diff --git a/spark-gremlin/pom.xml b/spark-gremlin/pom.xml index ce0eff687e4..0b2917997d0 100644 --- a/spark-gremlin/pom.xml +++ b/spark-gremlin/pom.xml @@ -25,6 +25,42 @@ limitations under the License. spark-gremlin Apache TinkerPop :: Spark Gremlin + + + + + com.fasterxml.jackson + jackson-bom + 2.21.2 + pom + import + + + + com.thoughtworks.paranamer + paranamer + 2.8.3 + + + javax.servlet + javax.servlet-api + 4.0.1 + + + org.bouncycastle + bcprov-jdk18on + 1.82 + + + javax.xml.bind + jaxb-api + 2.2.12 + + + org.apache.tinkerpop @@ -105,7 +141,7 @@ limitations under the License. org.apache.spark - spark-core_2.12 + spark-core_2.13 ${spark.version} @@ -195,7 +231,7 @@ limitations under the License. io.dropwizard.metrics metrics-core - 4.2.19 + 4.2.37 org.apache.curator @@ -244,23 +280,20 @@ limitations under the License. org.scala-lang scala-library - 2.12.15 + 2.13.17 - + com.fasterxml.jackson.core jackson-annotations - 2.15.4 com.fasterxml.jackson.core jackson-core - 2.15.4 com.fasterxml.jackson.core jackson-databind - 2.15.4 @@ -366,7 +399,7 @@ limitations under the License. - org.apache.hadoop:hadoop-client:${hadoop.version};org.apache.hadoop:hadoop-yarn-server-web-proxy:${hadoop.version};org.apache.spark:spark-yarn_2.12:${spark.version} + org.apache.hadoop:hadoop-client:${hadoop.version};org.apache.hadoop:hadoop-yarn-server-web-proxy:${hadoop.version};org.apache.spark:spark-yarn_2.13:${spark.version} diff --git a/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/structure/Spark.java b/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/structure/Spark.java index 694b25e3828..afb3ff62a0f 100644 --- a/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/structure/Spark.java +++ b/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/structure/Spark.java @@ -23,7 +23,7 @@ import org.apache.spark.SparkConf; import org.apache.spark.SparkContext; import org.apache.spark.rdd.RDD; -import scala.collection.JavaConversions; +import scala.jdk.javaapi.CollectionConverters; import java.util.ArrayList; import java.util.HashSet; @@ -108,7 +108,7 @@ public static void refresh() { recreateStopped(); final Set keepNames = new HashSet<>(); - for (final RDD rdd : JavaConversions.asJavaIterable(CONTEXT.persistentRdds().values())) { + for (final RDD rdd : CollectionConverters.asJava(CONTEXT.persistentRdds().values())) { if (null != rdd.name()) { keepNames.add(rdd.name()); NAME_TO_RDD.put(rdd.name(), rdd); diff --git a/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/structure/io/gryo/GryoRegistrator.java b/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/structure/io/gryo/GryoRegistrator.java index 4cf871d0e96..705d883a73b 100644 --- a/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/structure/io/gryo/GryoRegistrator.java +++ b/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/structure/io/gryo/GryoRegistrator.java @@ -51,7 +51,7 @@ import org.javatuples.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import scala.collection.mutable.WrappedArray; +import scala.collection.immutable.ArraySeq; import java.util.Collections; import java.util.HashSet; @@ -206,7 +206,7 @@ private LinkedHashMap, Serializer> getExtraRegistrations() { } catch (final ClassNotFoundException e) { throw new IllegalStateException(e.getMessage(), e); } - m.put(WrappedArray.ofRef.class, null); + m.put(ArraySeq.ofRef.class, null); m.put(MessagePayload.class, null); m.put(ViewIncomingPayload.class, null); m.put(ViewOutgoingPayload.class, null); diff --git a/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/structure/io/gryo/GryoSerializer.java b/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/structure/io/gryo/GryoSerializer.java index 58fa16bcac6..35f631703dc 100644 --- a/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/structure/io/gryo/GryoSerializer.java +++ b/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/structure/io/gryo/GryoSerializer.java @@ -51,7 +51,7 @@ import org.apache.tinkerpop.shaded.kryo.serializers.JavaSerializer; import scala.Tuple2; import scala.Tuple3; -import scala.collection.mutable.WrappedArray; +import scala.collection.immutable.ArraySeq; import scala.runtime.BoxedUnit; import java.io.Serializable; @@ -147,7 +147,7 @@ private SparkIoRegistry() { super.register(GryoIo.class, Class.forName("scala.None$"), new JavaSerializer()); super.register(GryoIo.class, Class.forName("scala.Some$"), new JavaSerializer()); super.register(GryoIo.class, Class.forName("scala.Some"), new JavaSerializer()); - super.register(GryoIo.class, WrappedArray.ofRef.class, new WrappedArraySerializer()); + super.register(GryoIo.class, ArraySeq.ofRef.class, new WrappedArraySerializer()); super.register(GryoIo.class, MessagePayload.class, null); super.register(GryoIo.class, ViewIncomingPayload.class, null); super.register(GryoIo.class, ViewOutgoingPayload.class, null); diff --git a/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/structure/io/gryo/WrappedArraySerializer.java b/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/structure/io/gryo/WrappedArraySerializer.java index 1de17f1a90c..8d6be792f25 100644 --- a/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/structure/io/gryo/WrappedArraySerializer.java +++ b/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/structure/io/gryo/WrappedArraySerializer.java @@ -23,29 +23,32 @@ import org.apache.tinkerpop.shaded.kryo.Serializer; import org.apache.tinkerpop.shaded.kryo.io.Input; import org.apache.tinkerpop.shaded.kryo.io.Output; -import scala.collection.JavaConversions; -import scala.collection.mutable.WrappedArray; +import scala.collection.immutable.ArraySeq; /** + * Serializer for Scala's immutable {@link ArraySeq}. In Scala 2.13 (the Scala version Spark is built against) wrapping + * a reference array produces an {@code immutable.ArraySeq.ofRef}, which replaced the {@code WrappedArray} used under + * Scala 2.12. + * * @author Marko A. Rodriguez (http://markorodriguez.com) */ -public final class WrappedArraySerializer extends Serializer> { +public final class WrappedArraySerializer extends Serializer> { @Override - public void write(final Kryo kryo, final Output output, final WrappedArray iterable) { + public void write(final Kryo kryo, final Output output, final ArraySeq iterable) { output.writeVarInt(iterable.size(), true); - JavaConversions.asJavaCollection(iterable).forEach(t -> { - kryo.writeClassAndObject(output, t); - }); + for (int i = 0; i < iterable.size(); i++) { + kryo.writeClassAndObject(output, iterable.apply(i)); + } } @Override - public WrappedArray read(final Kryo kryo, final Input input, final Class> aClass) { + public ArraySeq read(final Kryo kryo, final Input input, final Class> aClass) { final int size = input.readVarInt(true); final Object[] array = new Object[size]; for (int i = 0; i < size; i++) { array[i] = kryo.readClassAndObject(input); } - return new WrappedArray.ofRef<>((T[]) array); + return new ArraySeq.ofRef<>((T[]) array); } } diff --git a/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/structure/SparkTest.java b/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/structure/SparkTest.java index d77f359ccc7..59e87ee4730 100644 --- a/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/structure/SparkTest.java +++ b/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/structure/SparkTest.java @@ -38,7 +38,7 @@ import org.apache.tinkerpop.gremlin.structure.Graph; import org.apache.tinkerpop.gremlin.structure.util.GraphFactory; import org.junit.Test; -import scala.collection.JavaConversions; +import scala.jdk.javaapi.CollectionConverters; import java.io.File; @@ -112,7 +112,7 @@ public void testSparkRDDPersistence() throws Exception { } private static boolean hasPersistedRDD(final String name) { - for (final RDD rdd : JavaConversions.asJavaIterable(Spark.getContext().persistentRdds().values())) { + for (final RDD rdd : CollectionConverters.asJava(Spark.getContext().persistentRdds().values())) { if (null != rdd.name() && rdd.name().equals(name)) return true; } @@ -121,7 +121,7 @@ private static boolean hasPersistedRDD(final String name) { private static int getPersistedRDDSize() { int counter = 0; - for (final RDD rdd : JavaConversions.asJavaIterable(Spark.getContext().persistentRdds().values())) { + for (final RDD rdd : CollectionConverters.asJava(Spark.getContext().persistentRdds().values())) { if (null != rdd.name()) counter++; }