You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by ke...@apache.org on 2026/08/19 21:13:04 UTC
(tinkerpop) branch bp-str-reg created (now 51d1c3b427)
This is an automated email from the ASF dual-hosted git repository.
kenhuuu pushed a change to branch bp-str-reg
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
at 51d1c3b427 Restrict GraphBinary traversal strategy deserialization
This branch includes the following new commits:
new 372ea51c21 Delete .github/workflows directory
new 07fe1acfd7 Backport TraversalStrategies.GlobalCache.GLOBAL_REGISTRY to 3.7
new 51d1c3b427 Restrict GraphBinary traversal strategy deserialization
The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(tinkerpop) 01/03: Delete .github/workflows directory
Posted by ke...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
kenhuuu pushed a commit to branch bp-str-reg
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 372ea51c2187271a9f73dbb62f3e15c5941cceb6
Author: Cole Greer <11...@users.noreply.github.com>
AuthorDate: Mon Aug 17 13:10:31 2026 -0700
Delete .github/workflows directory
---
.github/workflows/build-test.yml | 386 ---------------------------------------
.github/workflows/codeql.yml | 134 --------------
2 files changed, 520 deletions(-)
diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml
deleted file mode 100644
index d81e2f3dd1..0000000000
--- a/.github/workflows/build-test.yml
+++ /dev/null
@@ -1,386 +0,0 @@
-name: build-test
-on: [push, pull_request]
-concurrency:
- group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
- cancel-in-progress: ${{ github.event_name == 'pull_request' }}
-jobs:
- smoke:
- name: smoke
- timeout-minutes: 10
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v7
- - name: Set up JDK 11
- uses: actions/setup-java@v5
- with:
- java-version: '11'
- 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
- java-jdk17:
- name: mvn clean install - jdk17
- timeout-minutes: 45
- needs: smoke
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v7
- - name: Set up JDK 17
- uses: actions/setup-java@v5
- with:
- java-version: '17'
- distribution: 'temurin'
- - name: Build with Maven
- run: mvn clean install -pl -:gremlin-javascript,-:gremlin-dotnet-source,-:gremlin-dotnet-tests,-:gremlin-go,-:gremlin-python -Dci --batch-mode -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn
- java-jdk11:
- name: mvn clean install - jdk11
- timeout-minutes: 45
- needs: smoke
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v7
- - name: Set up JDK 11
- uses: actions/setup-java@v5
- with:
- java-version: '11'
- distribution: 'temurin'
- - name: Build with Maven
- run: mvn clean install -pl -:gremlin-javascript,-:gremlin-dotnet-source,-:gremlin-dotnet-tests,-:gremlin-go,-:gremlin-python -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-jdk8:
- name: mvn clean install - jdk8
- timeout-minutes: 45
- needs: smoke
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v7
- - name: Set up JDK 8
- uses: actions/setup-java@v5
- with:
- java-version: '8'
- distribution: 'temurin'
- - name: Build with Maven
- run: mvn clean install -pl -:gremlin-javascript -Dci --batch-mode -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn
- gremlin-server-default:
- name: gremlin-server default
- timeout-minutes: 45
- needs: smoke
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v7
- - name: Set up JDK 11
- uses: actions/setup-java@v5
- with:
- java-version: '11'
- distribution: 'temurin'
- - name: Build with Maven
- run: |
- mvn clean install -pl -:gremlin-javascript,-:gremlin-python,-gremlin-dotnet,-:gremlin-dotnet-source,-:gremlin-dotnet-tests,-:gremlint -q -DskipTests -Dci
- mvn verify -pl :gremlin-server -DskipTests -DskipIntegrationTests=false -DincludeNeo4j
- gremlin-server-unified:
- name: gremlin-server unified
- timeout-minutes: 45
- needs: smoke
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v7
- - name: Set up JDK 11
- uses: actions/setup-java@v5
- with:
- java-version: '11'
- distribution: 'temurin'
- - name: Build with Maven
- run: |
- mvn clean install -pl -:gremlin-javascript,-:gremlin-python,-gremlin-dotnet,-:gremlin-dotnet-source,-:gremlin-dotnet-tests,-:gremlint -q -DskipTests -Dci
- mvn verify -pl :gremlin-server -DskipTests -DskipIntegrationTests=false -DincludeNeo4j -DtestUnified=true
- cache-gremlin-server-docker-image:
- name: cache gremlin-server docker image
- timeout-minutes: 10
- needs: smoke
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v7
- - name: Set up JDK 11
- uses: actions/setup-java@v5
- with:
- java-version: '11'
- distribution: 'temurin'
- - name: Generate Gremlin Server Base
- working-directory: .
- run: |
- mvn clean install -pl gremlin-server -DskipTests -DskipIntegrationTests=true -Dci -am
- - name: Save Docker image
- working-directory: ./gremlin-server
- run: docker save --output gremlin-server.tar tinkerpop/gremlin-server
- - uses: actions/cache@v6
- id: gremlin-server-test-docker-image
- with:
- path: |
- ./gremlin-server/*
- ~/.m2/repository/org/apache/tinkerpop/*
- key: ${{ github.sha }}
- # Disabled until Linux containers are supported on Windows runners: https://github.com/actions/virtual-environments/issues/252
-# - name: Upload Docker image for Windows
-# uses: actions/upload-artifact@v3
-# with:
-# name: ${{ github.sha }}
-# path: ./gremlin-server/gremlin-server.tar
-# retention-days: 1
- hadoop-standard:
- name: hadoop standard
- timeout-minutes: 45
- needs: smoke
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v7
- - name: Set up JDK 11
- uses: actions/setup-java@v5
- with:
- java-version: '11'
- distribution: 'temurin'
- - name: Build with Maven
- run: |
- mvn clean install -pl -:gremlin-javascript,-:gremlin-python,-gremlin-dotnet,-:gremlin-dotnet-source,-:gremlin-dotnet-tests,-:gremlint -q -DskipTests -Dci
- mvn verify -pl :hadoop-gremlin -DskipTests -DskipIntegrationTests=false '-Dit.test=HadoopGraphProcessStandardIntegrateTest'
- hadoop-feature:
- name: hadoop feature
- timeout-minutes: 45
- needs: smoke
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v7
- - name: Set up JDK 11
- uses: actions/setup-java@v5
- with:
- java-version: '11'
- distribution: 'temurin'
- - name: Build with Maven
- run: |
- mvn clean install -pl -:gremlin-javascript,-:gremlin-python,-gremlin-dotnet,-:gremlin-dotnet-source,-:gremlin-dotnet-tests,-:gremlint -q -DskipTests -Dci
- mvn verify -pl :hadoop-gremlin -DskipTests -DskipIntegrationTests=false '-Dit.test=HadoopGraphFeatureIntegrateTest'
- spark-core:
- name: spark core
- timeout-minutes: 45
- needs: smoke
- runs-on: ${{ matrix.os }}
- strategy:
- matrix:
- os: [ubuntu-latest, windows-latest]
- steps:
- - uses: actions/checkout@v7
- - name: Set up JDK 11
- uses: actions/setup-java@v5
- with:
- java-version: '11'
- distribution: 'temurin'
- - name: Build with Maven Windows
- if: runner.os == 'Windows'
- run: |
- mvn clean install -pl -:gremlin-javascript,-:gremlin-python,-gremlin-dotnet,-:gremlin-dotnet-source,-:gremlin-dotnet-tests,-:gremlint -q -DskipTests -DskipImageBuild -Dci
- - name: Build with Maven Ubuntu
- if: runner.os == 'Linux'
- run: |
- mvn clean install -pl -:gremlin-javascript,-:gremlin-python,-gremlin-dotnet,-:gremlin-dotnet-source,-:gremlin-dotnet-tests,-:gremlint -q -DskipTests -Dci
- mvn verify -pl :spark-gremlin -DskipTests -DskipIntegrationTests=false '-Dit.test=*IntegrateTest,!SparkGryoSerializerGraphComputerProcessIntegrateTest'
- spark-gryo:
- name: spark gryo
- timeout-minutes: 45
- needs: smoke
- runs-on: ${{ matrix.os }}
- strategy:
- matrix:
- os: [ubuntu-latest, windows-latest]
- steps:
- - uses: actions/checkout@v7
- - name: Set up JDK 11
- uses: actions/setup-java@v5
- with:
- java-version: '11'
- distribution: 'temurin'
- - name: Build with Maven Windows
- if: runner.os == 'Windows'
- run: |
- mvn clean install -pl -:gremlin-javascript,-:gremlin-python,-gremlin-dotnet,-:gremlin-dotnet-source,-:gremlin-dotnet-tests,-:gremlint -q -DskipTests -DskipImageBuild -Dci
- - name: Build with Maven Ubuntu
- if: runner.os == 'Linux'
- run: |
- mvn clean install -pl -:gremlin-javascript,-:gremlin-python,-gremlin-dotnet,-:gremlin-dotnet-source,-:gremlin-dotnet-tests,-:gremlint -q -DskipTests -Dci
- mvn verify -pl :spark-gremlin -DskipTests -DskipIntegrationTests=false -Dit.test=SparkGryoSerializerGraphComputerProcessIntegrateTest
- gremlin-console:
- name: gremlin-console
- timeout-minutes: 20
- needs: smoke
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v7
- - name: Set up JDK 11
- uses: actions/setup-java@v5
- with:
- java-version: '11'
- distribution: 'temurin'
- - name: Build with Maven
- run: |
- touch gremlin-python/.glv
- mvn clean install -pl -:gremlin-javascript,-:gremlin-python,-gremlin-dotnet,-:gremlin-dotnet-source,-:gremlin-dotnet-tests,-:gremlint -q -DskipTests -Dci
- mvn verify -pl :gremlin-console -DskipTests -DskipIntegrationTests=false
- gremlin-driver:
- name: gremlin-driver
- timeout-minutes: 20
- needs: cache-gremlin-server-docker-image
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v7
- - name: Set up JDK 11
- uses: actions/setup-java@v5
- with:
- java-version: '11'
- distribution: 'temurin'
- - name: Build with Maven
- run: |
- mvn clean install -pl -:gremlin-javascript,-gremlin-dotnet,-:gremlin-dotnet-source,-:gremlin-dotnet-tests,-:gremlint -q -DskipTests -Dci
- mvn verify -pl gremlin-driver -DskipIntegrationTests=false
- javascript:
- name: javascript-glv-node${{ matrix.node-version }}
- timeout-minutes: 15
- needs: cache-gremlin-server-docker-image
- runs-on: ubuntu-latest
- strategy:
- fail-fast: false
- matrix:
- node-version: ['20', '22', '24', '26']
- steps:
- - uses: actions/checkout@v7
- - name: Set up JDK 11
- uses: actions/setup-java@v5
- with:
- java-version: '11'
- distribution: 'temurin'
- - name: Get Cached Server Base Image
- uses: actions/cache@v6
- id: gremlin-server-test-docker-image
- with:
- path: |
- ./gremlin-server/*
- ~/.m2/repository/org/apache/tinkerpop/*
- key: ${{ github.sha }}
- - name: Load Docker Image
- working-directory: ./gremlin-server
- run: docker load --input gremlin-server.tar
- - name: Build with Maven
- run: |
- mvn clean install -pl -:gremlin-python,-gremlin-dotnet,-:gremlin-dotnet-source,-:gremlin-dotnet-tests -q -DskipTests -Dci
- mvn verify -pl :gremlin-javascript,:gremlint -Dnode.test.version=${{ matrix.node-version }}
- python:
- name: python-${{ matrix.python-version }}
- timeout-minutes: 20
- needs: cache-gremlin-server-docker-image
- runs-on: ubuntu-latest
- strategy:
- fail-fast: false
- matrix:
- python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
- env:
- PYTHON_VERSION: ${{ matrix.python-version }}
- steps:
- - uses: actions/checkout@v7
- - name: Set up JDK 11
- uses: actions/setup-java@v5
- with:
- java-version: '11'
- distribution: 'temurin'
- - name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v7
- with:
- python-version: ${{ matrix.python-version }}
- - name: Build with Maven
- run: |
- touch gremlin-python/.glv
- mvn clean install -pl -:gremlin-javascript,-gremlin-dotnet,-:gremlin-dotnet-source,-:gremlin-dotnet-tests,-:gremlint -q -DskipTests -Dci
- mvn verify -pl gremlin-python
- dotnet:
- name: .NET
- timeout-minutes: 20
- needs: cache-gremlin-server-docker-image
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v7
- - name: Set up JDK11
- uses: actions/setup-java@v5
- with:
- java-version: '11'
- distribution: 'temurin'
- - name: Set up .NET 8.0.x
- uses: actions/setup-dotnet@v6
- with:
- dotnet-version: '8.0.x'
- - name: Get Cached Server Base Image
- uses: actions/cache@v6
- id: gremlin-server-test-docker-image
- with:
- path: |
- ./gremlin-server/*
- ~/.m2/repository/org/apache/tinkerpop/*
- key: ${{ github.sha }}
- - name: Load Docker Image
- working-directory: ./gremlin-server
- run: docker load --input gremlin-server.tar
- - name: Build with Maven
- run: |
- touch gremlin-dotnet/src/.glv
- touch gremlin-dotnet/test/.glv
- mvn clean install -pl -:gremlin-javascript,-:gremlin-python,-:gremlint -q -DskipTests -Dci
- mvn verify -pl :gremlin-dotnet,:gremlin-dotnet-tests -P gremlin-dotnet
- neo4j-gremlin:
- name: neo4j-gremlin
- timeout-minutes: 20
- needs: smoke
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v7
- - name: Set up JDK11
- uses: actions/setup-java@v5
- with:
- java-version: '11'
- distribution: 'temurin'
- - name: Build with Maven
- run: |
- mvn clean install -pl -:gremlin-javascript,-:gremlin-python,-gremlin-dotnet,-:gremlin-dotnet-source,-:gremlin-dotnet-tests -q -DskipTests -Dci
- mvn verify -pl :neo4j-gremlin -DincludeNeo4j
- go:
- name: go
- timeout-minutes: 20
- needs: cache-gremlin-server-docker-image
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v7
- - name: Setup Go
- uses: actions/setup-go@v7
- with:
- go-version: '1.25'
- - name: Get Cached Server Base Image
- uses: actions/cache@v6
- id: gremlin-server-test-docker-image
- with:
- path: |
- ./gremlin-server/*
- ~/.m2/repository/org/apache/tinkerpop/*
- key: ${{ github.sha }}
- - name: Load Docker Image
- working-directory: ./gremlin-server
- run: docker load --input gremlin-server.tar
- - name: Build with Maven
- working-directory: .
- run: |
- touch gremlin-go/.glv
- mvn clean install -pl -:gremlin-python,-:gremlin-javascript,-:gremlin-dotnet,-:gremlin-dotnet-source,-:gremlin-dotnet-tests -q -DskipTests -Dci
- mvn verify -pl :gremlin-go
- - name: Upload to Codecov
- uses: codecov/codecov-action@v7
- with:
- working-directory: ./gremlin-go
- - name: Go-Vet
- working-directory: ./gremlin-go
- run: go vet ./...
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
deleted file mode 100644
index 6ad33666ab..0000000000
--- a/.github/workflows/codeql.yml
+++ /dev/null
@@ -1,134 +0,0 @@
-name: "CodeQL"
-
-on:
- push:
- branches: [ "master", "3.5-dev", "3.6-dev", "3.7-dev" ]
- pull_request:
- # The branches below must be a subset of the branches above
- branches: [ "master", "3.5-dev", "3.6-dev", "3.7-dev" ]
- schedule:
- - cron: '22 8 * * 1'
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
- cancel-in-progress: ${{ github.event_name == 'pull_request' }}
-
-jobs:
- analyze:
- name: Analyze
- runs-on: ubuntu-latest
- permissions:
- actions: read
- contents: read
- security-events: write
-
- strategy:
- fail-fast: false
- matrix:
- language: [ 'go', 'javascript', 'python' ]
- # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
- # Use only 'java' to analyze code written in Java, Kotlin or both
- # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
- # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@v7
-
- # Initializes the CodeQL tools for scanning.
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v4
- with:
- languages: ${{ matrix.language }}
- # If you wish to specify custom queries, you can do so here or in a config file.
- # By default, queries listed here will override any specified in a config file.
- # Prefix the list here with "+" to use these queries and those in the config file.
-
- # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
- # queries: security-extended,security-and-quality
-
-
- # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
- # If this step fails, then you should remove it and run the build manually (see below)
- - name: Autobuild
- uses: github/codeql-action/autobuild@v4
-
- # âšī¸ Command-line programs to run using the OS shell.
- # đ See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
-
- # If the Autobuild fails above, remove it and uncomment the following three lines.
- # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
-
- # - run: |
- # echo "Run, Build Application using script"
- # ./location_of_script_within_repo/buildscript.sh
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v4
- with:
- category: "/language:${{matrix.language}}"
-
- # .Net is run as a separate job as the integration tests take far too long to build
- analyze_dotnet:
- name: Analyze .Net
- runs-on: ubuntu-latest
- permissions:
- actions: read
- contents: read
- security-events: write
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@v7
-
- # Initializes the CodeQL tools for scanning.
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v4
- with:
- languages: 'csharp'
-
- - run: |
- echo "Build .NET"
- dotnet build gremlin-dotnet/src/Gremlin.Net;
- dotnet build gremlin-dotnet/src/Gremlin.Net.Template
- dotnet build gremlin-dotnet/test/Gremlin.Net.UnitTest
- dotnet build gremlin-dotnet/test/Gremlin.Net.Benchmarks
-# Excluded due to extremely long (~45m) build time in github actions
-# dotnet build gremlin-dotnet/test/Gremlin.Net.IntegrationTest
-# dotnet build gremlin-dotnet/test/Gremlin.Net.Template.IntegrationTest
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v4
- with:
- category: "/language:csharp"
-
- # Java is run as a separate job as the autobuild for Java is prone to random timeout failures
- analyze_java:
- name: Analyze Java
- runs-on: ubuntu-latest
- permissions:
- actions: read
- contents: read
- security-events: write
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@v7
-
- # Initializes the CodeQL tools for scanning.
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v4
- with:
- languages: 'java'
-
- - run: |
- echo "Build Java"
- mvn clean package -B -V -e \
- -pl -:gremlin-javascript,-:gremlin-dotnet,-:gremlin-dotnet-source,-:gremlin-dotnet-tests,-:gremlin-go,-:gremlin-python,-:gremlint \
- -Dfindbugs.skip -Dcheckstyle.skip -Dpmd.skip=true -Dspotbugs.skip -Denforcer.skip -Dmaven.javadoc.skip \
- -DskipTests -Dmaven.test.skip.exec -Dlicense.skip=true -Drat.skip=true -Dspotless.check.skip=true
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v4
- with:
- category: "/language:java"
(tinkerpop) 03/03: Restrict GraphBinary traversal strategy deserialization
Posted by ke...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
kenhuuu pushed a commit to branch bp-str-reg
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 51d1c3b42710e98cf22856c52c872321768072e2
Author: Ken Hu <10...@users.noreply.github.com>
AuthorDate: Tue Aug 18 15:24:53 2026 -0700
Restrict GraphBinary traversal strategy deserialization
Assisted-by: Codex:gpt-5.6-sol
---
CHANGELOG.asciidoc | 2 +
docs/src/reference/gremlin-applications.asciidoc | 16 ++
docs/src/upgrade/release-3.7.x.asciidoc | 23 +++
.../process/traversal/TraversalStrategies.java | 20 +++
.../binary/types/TraversalStrategySerializer.java | 6 +-
.../gremlin/process/TraversalStrategiesTest.java | 57 +++++++
.../types/TraversalStrategySerializerTest.java | 176 +++++++++++++++++++++
7 files changed, 299 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 55be83bfc1..fff2b43cf7 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -45,6 +45,8 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
* Fixed a panic in `gremlin-go` `PartitionStrategy` when `ReadPartitions` was left unset.
* Fixed `gremlin-python` `ProductiveByStrategy` to pass through the `productiveKeys` argument, which was previously accepted but never serialized to the server.
* Deprecated `ProductiveByStrategy` which was introduced as a temporary way to mimic pre-3.5.0 null processing behavior.
+* Backported `TraversalStrategy` registration mechanism in `TraversalStrategies` from the 3.8.x line.
+* Restricted GraphBinary `TraversalStrategy` deserialization to strategies registered with `registerStrategy()` or `registerStrategies()`.
* Fixed `gremlin-python` GraphBinary serialization of `BigInteger`/`BigDecimal` negative boundary values (e.g. `-129`) that raised `OverflowError`.
* Fixed `gremlin-go` GraphBinary serialization of zero `BigInteger`/`BigDecimal` values, which were encoded with zero length and rejected by Java servers.
diff --git a/docs/src/reference/gremlin-applications.asciidoc b/docs/src/reference/gremlin-applications.asciidoc
index 1cc494ad30..76a2ebc40f 100644
--- a/docs/src/reference/gremlin-applications.asciidoc
+++ b/docs/src/reference/gremlin-applications.asciidoc
@@ -1274,6 +1274,22 @@ It has the MIME type of `application/vnd.graphbinary-v1.0` and the following con
|builder |Name of the `TypeSerializerRegistry.Builder` instance to be used to construct the `TypeSerializerRegistry`. |_none_
|=========================================================
+The GraphBinary reference implementation in `gremlin-core`, which is used by Gremlin Server, only deserializes a
+`TraversalStrategy` when its class is registered with `TraversalStrategies.GlobalCache`. TinkerPop's built-in
+strategies are registered by default. Providers must register every custom strategy before GraphBinary input is read,
+either as part of a graph or graph computer strategy set with `registerStrategies()`, or individually with
+`registerStrategy()`:
+
+[source,java]
+----
+TraversalStrategies.GlobalCache.registerStrategies(MyGraph.class, traversalStrategies);
+// or
+TraversalStrategies.GlobalCache.registerStrategy(MyStrategy.class);
+----
+
+The presence of a strategy on the application class path is not sufficient. Registration permits serialized data to
+construct the strategy from its configuration, so all strategies should be registered.
+
As described above, there are multiple ways in which to register serializers for GraphBinary-based serialization. Note
that the `ioRegistries` setting is applied first, followed by the `custom` setting.
diff --git a/docs/src/upgrade/release-3.7.x.asciidoc b/docs/src/upgrade/release-3.7.x.asciidoc
index 38e939150b..90ecfbbf11 100644
--- a/docs/src/upgrade/release-3.7.x.asciidoc
+++ b/docs/src/upgrade/release-3.7.x.asciidoc
@@ -187,6 +187,29 @@ Applications that store or transmit `InetAddress` values via GraphSON (as a vert
Gremlin parameter) must use literal IP address strings going forward. Existing serialized data containing hostname
strings will fail to deserialize after upgrading and will need to be migrated to literal IP addresses.
+=== Upgrading for Providers
+
+==== Graph System Providers
+
+===== GraphBinary Strategy Registry
+
+Before 3.7.7, GraphBinary deserialization loaded a custom `TraversalStrategy` from the application class path based on
+its serialized class name. Starting with 3.7.7, GraphBinary only deserializes strategies registered with
+`TraversalStrategies.GlobalCache`.
+
+Providers that send custom strategies over GraphBinary must register every strategy before requests are deserialized.
+Either the existing `registerStrategies()` method or the new `registerStrategy()` method (backported from 3.8.x line)
+can be used:
+
+[source,text]
+----
+// Existing mechanism
+TraversalStrategies.GlobalCache.registerStrategies(MyGraph.class, traversalStrategies);
+
+// New mechanism
+TraversalStrategies.GlobalCache.registerStrategy(MyStrategy.class);
+----
+
== TinkerPop 3.7.6
*Release Date: April 1, 2026*
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java
index bb07187b14..1eb27aab9d 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java
@@ -350,6 +350,26 @@ public interface TraversalStrategies extends Serializable, Cloneable, Iterable<T
return Optional.empty();
}
+ /**
+ * Looks up a strategy by the fully qualified class name that a serialized traversal carries, without loading
+ * the named class. Serializers use this when they resolve a strategy name that arrived as bytes, so that the
+ * only strategies they can construct are those registered in advance by trusted code, by way of
+ * {@link #registerStrategies(Class, TraversalStrategies)} or {@link #registerStrategy(Class)}.
+ * <p/>
+ * A name only resolves when it is the {@link Class#getName()} of the class registered under its simple name,
+ * so an unregistered class that shares a simple name with a registered one does not resolve.
+ */
+ public static Optional<? extends Class<? extends TraversalStrategy>> getRegisteredStrategyClassByFullName(
+ final String className) {
+ if (null == className) return Optional.empty();
+
+ // a nested class is registered under the simple name, which is the segment after the last '$'
+ final int start = Math.max(className.lastIndexOf('.'), className.lastIndexOf('$')) + 1;
+ final Class<? extends TraversalStrategy> clazz = GLOBAL_REGISTRY.get(className.substring(start));
+
+ return null != clazz && className.equals(clazz.getName()) ? Optional.of(clazz) : Optional.empty();
+ }
+
public static TraversalStrategies getStrategies(final Class graphOrGraphComputerClass) {
try {
// be sure to load the class so that its static{} traversal strategy registration component is loaded.
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/TraversalStrategySerializer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/TraversalStrategySerializer.java
index 7790b213ea..5618a76b37 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/TraversalStrategySerializer.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/TraversalStrategySerializer.java
@@ -24,6 +24,7 @@ import org.apache.tinkerpop.gremlin.structure.io.binary.DataType;
import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader;
import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
import org.apache.tinkerpop.gremlin.structure.io.Buffer;
@@ -43,7 +44,10 @@ public class TraversalStrategySerializer extends SimpleTypeSerializer<TraversalS
@Override
protected TraversalStrategy readValue(final Buffer buffer, final GraphBinaryReader context) throws IOException {
- final Class<TraversalStrategy> clazz = context.readValue(buffer, Class.class, false);
+ final String className = context.readValue(buffer, String.class, false);
+ final Class<? extends TraversalStrategy> clazz =
+ TraversalStrategies.GlobalCache.getRegisteredStrategyClassByFullName(className).
+ orElseThrow(() -> new IOException("TraversalStrategy not recognized - " + className));
final Map config = context.readValue(buffer, Map.class, false);
return new TraversalStrategyProxy(clazz, new MapConfiguration(config));
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/TraversalStrategiesTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/TraversalStrategiesTest.java
index e7a5b88b84..16189f0e08 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/TraversalStrategiesTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/TraversalStrategiesTest.java
@@ -56,6 +56,7 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies.GlobalCache.getRegisteredStrategyClass;
+import static org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies.GlobalCache.getRegisteredStrategyClassByFullName;
import static org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies.GlobalCache.registerStrategy;
import static org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies.GlobalCache.unregisterStrategy;
import static org.junit.Assert.assertEquals;
@@ -324,6 +325,62 @@ public class TraversalStrategiesTest {
}
}
+ @Test
+ public void shouldResolveRegisteredStrategyByFullName() {
+ assertEquals(ReadOnlyStrategy.class,
+ getRegisteredStrategyClassByFullName(ReadOnlyStrategy.class.getName()).get());
+ assertEquals(RemoteStrategy.class,
+ getRegisteredStrategyClassByFullName(RemoteStrategy.class.getName()).get());
+ assertEquals(RequirementsStrategy.class,
+ getRegisteredStrategyClassByFullName(RequirementsStrategy.class.getName()).get());
+ assertEquals(SackStrategy.class,
+ getRegisteredStrategyClassByFullName(SackStrategy.class.getName()).get());
+ assertEquals(SideEffectStrategy.class,
+ getRegisteredStrategyClassByFullName(SideEffectStrategy.class.getName()).get());
+ }
+
+ @Test
+ public void shouldResolveNestedRegisteredStrategyByFullName() {
+ // StrategyA is nested, so it is registered under the segment of its name that follows the '$'
+ assertEquals(StrategyA.class,
+ getRegisteredStrategyClassByFullName(StrategyA.class.getName()).get());
+ }
+
+ @Test
+ public void shouldNotResolveUnregisteredStrategyByFullName() {
+ unregisterStrategy(AbsentStrategy.class);
+ assertFalse(getRegisteredStrategyClassByFullName(AbsentStrategy.class.getName()).isPresent());
+ }
+
+ @Test
+ public void shouldNotResolveStrategySharingASimpleNameWithARegisteredOne() {
+ // borrowing the simple name of a registered strategy must not admit some other class of that name
+ assertFalse(getRegisteredStrategyClassByFullName("com.example.ReadOnlyStrategy").isPresent());
+ }
+
+ @Test
+ public void shouldNotResolveSimpleNameByFullName() {
+ assertFalse(getRegisteredStrategyClassByFullName(ReadOnlyStrategy.class.getSimpleName()).isPresent());
+ }
+
+ @Test
+ public void shouldNotResolveNullByFullName() {
+ assertFalse(getRegisteredStrategyClassByFullName(null).isPresent());
+ }
+
+ @Test
+ public void shouldResolveStrategyByFullNameAfterItIsRegistered() {
+ unregisterStrategy(AbsentStrategy.class);
+
+ try {
+ registerStrategy(AbsentStrategy.class);
+ assertEquals(AbsentStrategy.class,
+ getRegisteredStrategyClassByFullName(AbsentStrategy.class.getName()).get());
+ } finally {
+ unregisterStrategy(AbsentStrategy.class);
+ }
+ }
+
/**
* Tests that {@link org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies#sortStrategies(java.util.Set)}
* works as advertised. This class defines a bunch of dummy strategies which define an order. It is verified
diff --git a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/types/TraversalStrategySerializerTest.java b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/types/TraversalStrategySerializerTest.java
new file mode 100644
index 0000000000..cfe08fec8f
--- /dev/null
+++ b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/types/TraversalStrategySerializerTest.java
@@ -0,0 +1,176 @@
+/*
+ * 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.tinkerpop.gremlin.util.ser.binary.types;
+
+import io.netty.buffer.ByteBufAllocator;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy;
+import org.apache.tinkerpop.gremlin.structure.io.Buffer;
+import org.apache.tinkerpop.gremlin.structure.io.binary.DataType;
+import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader;
+import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter;
+import org.apache.tinkerpop.gremlin.structure.io.binary.TypeSerializerRegistry;
+import org.apache.tinkerpop.gremlin.structure.io.binary.types.SimpleTypeSerializer;
+import org.apache.tinkerpop.gremlin.util.ser.NettyBufferFactory;
+import org.junit.After;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.Collections;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.StringContains.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+public class TraversalStrategySerializerTest {
+
+ private static final NettyBufferFactory bufferFactory = new NettyBufferFactory();
+ private static boolean loadRecordingStrategyInitialized;
+ private final ByteBufAllocator allocator = ByteBufAllocator.DEFAULT;
+
+ @After
+ public void unregisterStrategy() {
+ TraversalStrategies.GlobalCache.unregisterStrategy(LoadRecordingStrategy.class);
+ }
+
+ @Test
+ public void shouldRejectStrategyThatIsNotRegistered() throws Exception {
+ final String fqcn = LoadRecordingStrategy.class.getName();
+ try {
+ readStrategy(reader(), fqcn);
+ fail("A strategy that is not registered must not deserialize");
+ } catch (IOException ex) {
+ assertThat(ex.getMessage(), containsString("TraversalStrategy not recognized - " + fqcn));
+ }
+ }
+
+ @Test
+ public void shouldRejectStrategyThatIsNotRegisteredWithoutInitializingIt() throws Exception {
+ // a class literal does not initialize the class, so naming it this way keeps the assertion below meaningful
+ final String fqcn = LoadRecordingStrategy.class.getName();
+ try {
+ readStrategy(reader(), fqcn);
+ fail("A strategy that is not registered must not deserialize");
+ } catch (IOException ignored) {
+ // asserted on by shouldRejectStrategyThatIsNotRegistered
+ }
+
+ assertFalse("The rejected strategy was initialized, so the check ran after the class was loaded",
+ loadRecordingStrategyInitialized);
+ }
+
+ @Test
+ public void shouldAdmitStrategyRegisteredAsABuiltIn() throws Exception {
+ assertEquals(SubgraphStrategy.class,
+ readStrategy(reader(), SubgraphStrategy.class.getName()).getStrategyClass());
+ }
+
+ @Test
+ public void shouldAdmitStrategyRegisteredByAProvider() throws Exception {
+ TraversalStrategies.GlobalCache.registerStrategy(LoadRecordingStrategy.class);
+
+ assertEquals(LoadRecordingStrategy.class,
+ readStrategy(reader(), LoadRecordingStrategy.class.getName()).getStrategyClass());
+ }
+
+ /**
+ * Reading the class as a name rather than as a {@code Class} value must not change the format, since the value of a
+ * {@code Class} is the class name written as a {@code String} value. Reading back what the writer produced the way
+ * a reader before this change did shows that the two agree.
+ */
+ @Test
+ public void shouldWriteTheStrategyClassInTheClassValueFormat() throws Exception {
+ final Buffer buffer = bufferFactory.create(allocator.buffer());
+ new GraphBinaryWriter().writeValue(ReadOnlyStrategy.instance(), buffer, false);
+
+ assertEquals(ReadOnlyStrategy.class, new GraphBinaryReader().readValue(buffer, Class.class, false));
+ }
+
+ /**
+ * The strategy class no longer reaches the {@code ClassSerializer}, which matters because that serializer resolves
+ * whatever name it is given. A registry whose {@code Class} serializer refuses to read anything still reads a
+ * strategy.
+ */
+ @Test
+ public void shouldNotReadTheStrategyClassThroughTheClassSerializer() throws Exception {
+ final GraphBinaryReader reader = new GraphBinaryReader(TypeSerializerRegistry.build().
+ add(Class.class, new RefusingClassSerializer()).create());
+
+ assertEquals(SubgraphStrategy.class, readStrategy(reader, SubgraphStrategy.class.getName()).getStrategyClass());
+ }
+
+ private GraphBinaryReader reader() {
+ return new GraphBinaryReader(TypeSerializerRegistry.build().create());
+ }
+
+ /**
+ * Writes the value of a {@code TraversalStrategy} as a name followed by an empty configuration, which is what a
+ * client sends for a strategy that takes no configuration.
+ */
+ private TraversalStrategyProxy readStrategy(final GraphBinaryReader reader, final String fqcn) throws IOException {
+ final GraphBinaryWriter writer = new GraphBinaryWriter();
+ final Buffer buffer = bufferFactory.create(allocator.buffer());
+ writer.writeValue(fqcn, buffer, false);
+ writer.writeValue(Collections.emptyMap(), buffer, false);
+
+ return (TraversalStrategyProxy) reader.readValue(buffer, TraversalStrategy.class, false);
+ }
+
+ private static final class LoadRecordingStrategy
+ extends AbstractTraversalStrategy<TraversalStrategy.DecorationStrategy>
+ implements TraversalStrategy.DecorationStrategy {
+
+ static {
+ loadRecordingStrategyInitialized = true;
+ }
+
+ @Override
+ public void apply(final Traversal.Admin<?, ?> traversal) {
+ // do nothing
+ }
+ }
+
+ /**
+ * Stands in for the {@code ClassSerializer} to show that nothing consults it while a strategy is read.
+ */
+ private static class RefusingClassSerializer extends SimpleTypeSerializer<Class> {
+
+ RefusingClassSerializer() {
+ super(DataType.CLASS);
+ }
+
+ @Override
+ protected Class readValue(final Buffer buffer, final GraphBinaryReader context) throws IOException {
+ throw new IOException("the Class serializer must not be consulted");
+ }
+
+ @Override
+ protected void writeValue(final Class value, final Buffer buffer,
+ final GraphBinaryWriter context) throws IOException {
+ throw new IOException("the Class serializer must not be consulted");
+ }
+ }
+}
(tinkerpop) 02/03: Backport TraversalStrategies.GlobalCache.GLOBAL_REGISTRY to 3.7
Posted by ke...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
kenhuuu pushed a commit to branch bp-str-reg
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 07fe1acfd7315b54eafd6df94a2924ba56555d4d
Author: Ken Hu <10...@users.noreply.github.com>
AuthorDate: Mon Aug 17 14:22:44 2026 -0700
Backport TraversalStrategies.GlobalCache.GLOBAL_REGISTRY to 3.7
Backports the 3.8 GLOBAL_REGISTRY so strategies can be looked up by
name. registerStrategies() publishes provider defaults, while
registerStrategy() and unregisterStrategy() support strategies that
should be named without becoming defaults.
RemoteStrategy, RequirementsStrategy, SackStrategy and
SideEffectStrategy are concrete gremlin-core strategies that were
omitted from the 3.8 seed list, so register them with the other
built-ins. SparqlStrategy belongs to the optional sparql-gremlin
module, so it registers itself when loaded instead of adding a core
dependency.
Adds registry contract coverage for built-ins, provider strategies,
lifecycle, duplicate simple names and invalid lookups.
Nothing in 3.7 consults the registry yet. It is the prerequisite
for resolving a strategy named by a serialized traversal against
what trusted code declared, rather than loading the named class.
Assisted-by: Claude Code:claude-opus-5
---
.../process/traversal/TraversalStrategies.java | 99 ++++++++++++++++++-
.../gremlin/process/TraversalStrategiesTest.java | 109 +++++++++++++++++++++
.../process/traversal/strategy/SparqlStrategy.java | 6 ++
.../dsl/sparql/SparqlTraversalSourceTest.java | 8 ++
4 files changed, 219 insertions(+), 3 deletions(-)
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java
index aea45618ab..bb07187b14 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java
@@ -19,11 +19,27 @@
package org.apache.tinkerpop.gremlin.process.traversal;
import org.apache.tinkerpop.gremlin.process.computer.GraphComputer;
+import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.decoration.VertexProgramStrategy;
import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.finalization.ComputerFinalizationStrategy;
import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.optimization.GraphFilterStrategy;
import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.optimization.MessagePassingReductionStrategy;
+import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.verification.VertexProgramRestrictionStrategy;
+import org.apache.tinkerpop.gremlin.process.remote.traversal.strategy.decoration.RemoteStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ConnectiveStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ElementIdStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.HaltedTraverserStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.OptionsStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.PartitionStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.RequirementsStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SackStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SeedStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SideEffectStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.MatchAlgorithmStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.ProfileStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.ReferenceElementStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.AdjacentToIncidentStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.CountStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.EarlyLimitStrategy;
@@ -37,8 +53,13 @@ import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.Matc
import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.OrderLimitStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathProcessorStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathRetractionStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.RepeatUnrollStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ComputerVerificationStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.EdgeLabelVerificationStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.LambdaRestrictionStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReservedKeysVerificationStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.StandardVerificationStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalStrategies;
import org.apache.tinkerpop.gremlin.structure.Graph;
@@ -214,6 +235,42 @@ public interface TraversalStrategies extends Serializable, Cloneable, Iterable<T
private static final Map<Class<? extends Graph>, TraversalStrategies> GRAPH_CACHE = new HashMap<>();
private static final Map<Class<? extends GraphComputer>, TraversalStrategies> GRAPH_COMPUTER_CACHE = new HashMap<>();
+ /**
+ * A register of the simple names for all strategies.
+ */
+ private static final Map<String, Class<? extends TraversalStrategy>> GLOBAL_REGISTRY = new HashMap<String, Class<? extends TraversalStrategy>>() {{
+ // decorations
+ put(ConnectiveStrategy.class.getSimpleName(), ConnectiveStrategy.class);
+ put(ElementIdStrategy.class.getSimpleName(), ElementIdStrategy.class);
+ put(EventStrategy.class.getSimpleName(), EventStrategy.class);
+ put(HaltedTraverserStrategy.class.getSimpleName(), HaltedTraverserStrategy.class);
+ put(OptionsStrategy.class.getSimpleName(), OptionsStrategy.class);
+ put(PartitionStrategy.class.getSimpleName(), PartitionStrategy.class);
+ put(RemoteStrategy.class.getSimpleName(), RemoteStrategy.class);
+ put(RequirementsStrategy.class.getSimpleName(), RequirementsStrategy.class);
+ put(SackStrategy.class.getSimpleName(), SackStrategy.class);
+ put(SeedStrategy.class.getSimpleName(), SeedStrategy.class);
+ put(SideEffectStrategy.class.getSimpleName(), SideEffectStrategy.class);
+ put(SubgraphStrategy.class.getSimpleName(), SubgraphStrategy.class);
+ put(VertexProgramStrategy.class.getSimpleName(), VertexProgramStrategy.class);
+
+ // finalization
+ put(MatchAlgorithmStrategy.class.getSimpleName(), MatchAlgorithmStrategy.class);
+ put(ReferenceElementStrategy.class.getSimpleName(), ReferenceElementStrategy.class);
+
+ // optimizations
+ put(ProductiveByStrategy.class.getSimpleName(), ProductiveByStrategy.class);
+ put(PathRetractionStrategy.class.getSimpleName(), PathRetractionStrategy.class);
+ put(RepeatUnrollStrategy.class.getSimpleName(), RepeatUnrollStrategy.class);
+
+ // verification
+ put(EdgeLabelVerificationStrategy.class.getSimpleName(), EdgeLabelVerificationStrategy.class);
+ put(LambdaRestrictionStrategy.class.getSimpleName(), LambdaRestrictionStrategy.class);
+ put(ReadOnlyStrategy.class.getSimpleName(), ReadOnlyStrategy.class);
+ put(ReservedKeysVerificationStrategy.class.getSimpleName(), ReservedKeysVerificationStrategy.class);
+ put(VertexProgramRestrictionStrategy.class.getSimpleName(), VertexProgramRestrictionStrategy.class);
+ }};
+
static {
final TraversalStrategies graphStrategies = new DefaultTraversalStrategies();
graphStrategies.addStrategies(
@@ -232,8 +289,8 @@ public interface TraversalStrategies extends Serializable, Cloneable, Iterable<T
LazyBarrierStrategy.instance(),
ProfileStrategy.instance(),
StandardVerificationStrategy.instance());
- GRAPH_CACHE.put(Graph.class, graphStrategies);
- GRAPH_CACHE.put(EmptyGraph.class, new DefaultTraversalStrategies());
+ registerStrategies(Graph.class, graphStrategies);
+ registerStrategies(EmptyGraph.class, new DefaultTraversalStrategies());
/////////////////////
@@ -245,9 +302,14 @@ public interface TraversalStrategies extends Serializable, Cloneable, Iterable<T
PathProcessorStrategy.instance(),
ComputerFinalizationStrategy.instance(),
ComputerVerificationStrategy.instance());
- GRAPH_COMPUTER_CACHE.put(GraphComputer.class, graphComputerStrategies);
+ registerStrategies(GraphComputer.class, graphComputerStrategies);
}
+ /**
+ * Register a set of strategies for a particular graph or graph computer class. This is typically done by the
+ * graph or graph computer class itself when it is loaded. Strategy names should be globally unique and are
+ * added to the {@link #GLOBAL_REGISTRY} such that duplicates will overwrite the previous registration.
+ */
public static void registerStrategies(final Class graphOrGraphComputerClass, final TraversalStrategies traversalStrategies) {
if (Graph.class.isAssignableFrom(graphOrGraphComputerClass))
GRAPH_CACHE.put(graphOrGraphComputerClass, traversalStrategies);
@@ -255,6 +317,37 @@ public interface TraversalStrategies extends Serializable, Cloneable, Iterable<T
GRAPH_COMPUTER_CACHE.put(graphOrGraphComputerClass, traversalStrategies);
else
throw new IllegalArgumentException("The TraversalStrategies.GlobalCache only supports Graph and GraphComputer strategy caching: " + graphOrGraphComputerClass.getCanonicalName());
+
+ // add the strategies in the traversalStrategy to the global registry
+ traversalStrategies.toList().forEach(strategy -> GLOBAL_REGISTRY.put(strategy.getClass().getSimpleName(), strategy.getClass()));
+ }
+
+ /**
+ * Registers a strategy by its simple name, but does not cache an instance of it. Choose this method if you
+ * don't want the strategy to be included as part of the default strategy set, but do want it available to
+ * be looked up by name.
+ */
+ public static void registerStrategy(final Class<? extends TraversalStrategy> clazz) {
+ GLOBAL_REGISTRY.put(clazz.getSimpleName(), clazz);
+ }
+
+ /**
+ * Unregisters a strategy by its simple name. If the strategy is not in the registry then it cannot be
+ * referenced by name, which means that it cannot be removed from execution using
+ * {{@link GraphTraversalSource#withoutStrategies(Class[])}}.
+ */
+ public static void unregisterStrategy(final Class<? extends TraversalStrategy> clazz) {
+ GLOBAL_REGISTRY.remove(clazz.getSimpleName());
+ }
+
+ /**
+ * Looks up a strategy by its simple name.
+ */
+ public static Optional<? extends Class<? extends TraversalStrategy>> getRegisteredStrategyClass(final String strategyName) {
+ if (GLOBAL_REGISTRY.containsKey(strategyName))
+ return Optional.of(GLOBAL_REGISTRY.get(strategyName));
+
+ return Optional.empty();
}
public static TraversalStrategies getStrategies(final Class graphOrGraphComputerClass) {
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/TraversalStrategiesTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/TraversalStrategiesTest.java
index 4d0fd22cbf..e7a5b88b84 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/TraversalStrategiesTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/TraversalStrategiesTest.java
@@ -24,10 +24,16 @@ import org.apache.tinkerpop.gremlin.process.computer.ComputerResult;
import org.apache.tinkerpop.gremlin.process.computer.GraphComputer;
import org.apache.tinkerpop.gremlin.process.computer.MapReduce;
import org.apache.tinkerpop.gremlin.process.computer.VertexProgram;
+import org.apache.tinkerpop.gremlin.process.remote.traversal.strategy.decoration.RemoteStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.RequirementsStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SackStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SideEffectStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.CountStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.apache.tinkerpop.gremlin.structure.Property;
@@ -49,6 +55,9 @@ import java.util.concurrent.Future;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import static org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies.GlobalCache.getRegisteredStrategyClass;
+import static org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies.GlobalCache.registerStrategy;
+import static org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies.GlobalCache.unregisterStrategy;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -136,6 +145,89 @@ public class TraversalStrategiesTest {
assertTrue(strategies.getStrategy(StrategyE.class).isPresent());
}
+ @Test
+ public void shouldRegisterBuiltInStrategiesByName() {
+ assertEquals(ReadOnlyStrategy.class, getRegisteredStrategyClass(ReadOnlyStrategy.class.getSimpleName()).get());
+ assertEquals(CountStrategy.class, getRegisteredStrategyClass(CountStrategy.class.getSimpleName()).get());
+ assertEquals(RemoteStrategy.class, getRegisteredStrategyClass(RemoteStrategy.class.getSimpleName()).get());
+ assertEquals(RequirementsStrategy.class,
+ getRegisteredStrategyClass(RequirementsStrategy.class.getSimpleName()).get());
+ assertEquals(SackStrategy.class, getRegisteredStrategyClass(SackStrategy.class.getSimpleName()).get());
+ assertEquals(SideEffectStrategy.class,
+ getRegisteredStrategyClass(SideEffectStrategy.class.getSimpleName()).get());
+ }
+
+ @Test
+ public void shouldRegisterGraphAndGraphComputerStrategiesByName() {
+ assertEquals(StrategyA.class, getRegisteredStrategyClass(StrategyA.class.getSimpleName()).get());
+ assertEquals(StrategyB.class, getRegisteredStrategyClass(StrategyB.class.getSimpleName()).get());
+ assertEquals(StrategyC.class, getRegisteredStrategyClass(StrategyC.class.getSimpleName()).get());
+ }
+
+ @Test
+ public void shouldRegisterAndUnregisterStrategyByNameWithoutChangingDefaults() {
+ unregisterStrategy(StrategyD.class);
+ assertFalse(getRegisteredStrategyClass(StrategyD.class.getSimpleName()).isPresent());
+
+ try {
+ registerStrategy(StrategyD.class);
+ assertEquals(StrategyD.class, getRegisteredStrategyClass(StrategyD.class.getSimpleName()).get());
+
+ // registerStrategy() only adds the class to GLOBAL_REGISTRY and must not alter graph defaults
+ assertFalse(TraversalStrategies.GlobalCache.getStrategies(Graph.class).
+ getStrategy(StrategyD.class).isPresent());
+ } finally {
+ unregisterStrategy(StrategyD.class);
+ }
+
+ assertFalse(getRegisteredStrategyClass(StrategyD.class.getSimpleName()).isPresent());
+ }
+
+ @Test
+ public void shouldOverwriteStrategyRegisteredWithSameSimpleName() {
+ final String strategyName = FirstStrategyNamespace.DuplicateStrategy.class.getSimpleName();
+ unregisterStrategy(FirstStrategyNamespace.DuplicateStrategy.class);
+
+ try {
+ registerStrategy(FirstStrategyNamespace.DuplicateStrategy.class);
+ assertEquals(FirstStrategyNamespace.DuplicateStrategy.class,
+ getRegisteredStrategyClass(strategyName).get());
+
+ registerStrategy(SecondStrategyNamespace.DuplicateStrategy.class);
+ assertEquals(SecondStrategyNamespace.DuplicateStrategy.class,
+ getRegisteredStrategyClass(strategyName).get());
+ } finally {
+ unregisterStrategy(SecondStrategyNamespace.DuplicateStrategy.class);
+ }
+
+ assertFalse(getRegisteredStrategyClass(strategyName).isPresent());
+ }
+
+ @Test
+ public void shouldNotResolveInvalidStrategyNames() {
+ assertFalse(getRegisteredStrategyClass("UnknownStrategy").isPresent());
+ assertFalse(getRegisteredStrategyClass(ReadOnlyStrategy.class.getName()).isPresent());
+ assertFalse(getRegisteredStrategyClass("readonlystrategy").isPresent());
+ assertFalse(getRegisteredStrategyClass("").isPresent());
+ assertFalse(getRegisteredStrategyClass(null).isPresent());
+ }
+
+ @Test
+ public void shouldIgnoreUnregisterOfAbsentStrategy() {
+ unregisterStrategy(StrategyD.class);
+ unregisterStrategy(AbsentStrategy.class);
+
+ try {
+ registerStrategy(StrategyD.class);
+ unregisterStrategy(AbsentStrategy.class);
+
+ assertEquals(StrategyD.class, getRegisteredStrategyClass(StrategyD.class.getSimpleName()).get());
+ assertFalse(getRegisteredStrategyClass(AbsentStrategy.class.getSimpleName()).isPresent());
+ } finally {
+ unregisterStrategy(StrategyD.class);
+ }
+ }
+
public static class TestGraphComputer implements GraphComputer {
@Override
@@ -420,6 +512,23 @@ public class TraversalStrategiesTest {
}
+ private static class FirstStrategyNamespace {
+
+ private static class DuplicateStrategy extends DummyStrategy {
+
+ }
+ }
+
+ private static class SecondStrategyNamespace {
+
+ private static class DuplicateStrategy extends DummyStrategy {
+
+ }
+ }
+
+ private static class AbsentStrategy extends DummyStrategy {
+
+ }
private static class DummyStrategy<S extends TraversalStrategy> extends AbstractTraversalStrategy<S> {
diff --git a/sparql-gremlin/src/main/java/org/apache/tinkerpop/gremlin/sparql/process/traversal/strategy/SparqlStrategy.java b/sparql-gremlin/src/main/java/org/apache/tinkerpop/gremlin/sparql/process/traversal/strategy/SparqlStrategy.java
index 7b6dd7cdc4..32d22cf4ab 100644
--- a/sparql-gremlin/src/main/java/org/apache/tinkerpop/gremlin/sparql/process/traversal/strategy/SparqlStrategy.java
+++ b/sparql-gremlin/src/main/java/org/apache/tinkerpop/gremlin/sparql/process/traversal/strategy/SparqlStrategy.java
@@ -20,6 +20,7 @@ package org.apache.tinkerpop.gremlin.sparql.process.traversal.strategy;
import org.apache.tinkerpop.gremlin.process.remote.traversal.strategy.decoration.RemoteStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.InjectStep;
@@ -52,6 +53,11 @@ public class SparqlStrategy extends AbstractTraversalStrategy<TraversalStrategy.
private static final Set<Class<? extends DecorationStrategy>> PRIORS = Collections.singleton(RemoteStrategy.class);
+ static {
+ // makes the strategy resolvable by name, which serializers require of a strategy that arrives in a request
+ TraversalStrategies.GlobalCache.registerStrategy(SparqlStrategy.class);
+ }
+
private SparqlStrategy() {}
public static SparqlStrategy instance() {
diff --git a/sparql-gremlin/src/test/java/org/apache/tinkerpop/gremlin/sparql/process/traversal/dsl/sparql/SparqlTraversalSourceTest.java b/sparql-gremlin/src/test/java/org/apache/tinkerpop/gremlin/sparql/process/traversal/dsl/sparql/SparqlTraversalSourceTest.java
index 552ac73e49..b9139ad39d 100644
--- a/sparql-gremlin/src/test/java/org/apache/tinkerpop/gremlin/sparql/process/traversal/dsl/sparql/SparqlTraversalSourceTest.java
+++ b/sparql-gremlin/src/test/java/org/apache/tinkerpop/gremlin/sparql/process/traversal/dsl/sparql/SparqlTraversalSourceTest.java
@@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.sparql.process.traversal.dsl.sparql;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.sparql.process.traversal.strategy.SparqlStrategy;
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerFactory;
@@ -31,6 +32,7 @@ import java.util.List;
import java.util.Map;
import static org.apache.tinkerpop.gremlin.process.traversal.Operator.mult;
+import static org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies.GlobalCache.getRegisteredStrategyClass;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.outE;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
@@ -46,6 +48,12 @@ public class SparqlTraversalSourceTest {
private static final SparqlTraversalSource g = graph.traversal(SparqlTraversalSource.class);
private static final GraphTraversalSource _g = graph.traversal();
+ @Test
+ public void shouldRegisterSparqlStrategy() {
+ SparqlStrategy.instance();
+ assertEquals(SparqlStrategy.class, getRegisteredStrategyClass(SparqlStrategy.class.getSimpleName()).get());
+ }
+
@Test
public void shouldStartWithSparqlReturningMapAndEndWithGremlin() {
final List<?> x = g.sparql("SELECT ?name ?age WHERE { ?person v:name ?name . ?person v:age ?age }").select("name").toList();