This is an automated email from the ASF dual-hosted git repository.

kenhuuu pushed a commit to branch 3.7-dev
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 5dda51e73ea528d2ee3277acfa68ba5f0c404d50
Author: Ken Hu <[email protected]>
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.
    
    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     |  91 +++++++++++++++++-
 .../gremlin/process/TraversalStrategiesTest.java   | 104 +++++++++++++++++++++
 2 files changed, 192 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..d9a9248f46 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,23 @@
 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.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.SeedStrategy;
+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 +49,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 +231,38 @@ 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(SeedStrategy.class.getSimpleName(), SeedStrategy.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 +281,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 +294,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 +309,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..4c72ff7422 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
@@ -28,6 +28,10 @@ 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.OptionsStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.MatchAlgorithmStrategy;
+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 +53,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 +143,86 @@ public class TraversalStrategiesTest {
         assertTrue(strategies.getStrategy(StrategyE.class).isPresent());
     }
 
+    @Test
+    public void shouldRegisterBuiltInStrategiesByName() {
+        assertEquals(OptionsStrategy.class, 
getRegisteredStrategyClass(OptionsStrategy.class.getSimpleName()).get());
+        assertEquals(MatchAlgorithmStrategy.class,
+                
getRegisteredStrategyClass(MatchAlgorithmStrategy.class.getSimpleName()).get());
+        assertEquals(ReadOnlyStrategy.class, 
getRegisteredStrategyClass(ReadOnlyStrategy.class.getSimpleName()).get());
+        assertEquals(CountStrategy.class, 
getRegisteredStrategyClass(CountStrategy.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 +507,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> {
 

Reply via email to