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

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


The following commit(s) were added to refs/heads/3.7-dev by this push:
     new 0c9b8bf759 TINKERPOP-3163 fixed CallStep serialization (#3140)
0c9b8bf759 is described below

commit 0c9b8bf75987c12a7ecb7a7a2dba4068a8d19303
Author: Valentyn Kahamlyk <[email protected]>
AuthorDate: Tue Jul 8 14:54:25 2025 -0700

    TINKERPOP-3163 fixed CallStep serialization (#3140)
    
    Fixed serialization of CallStep.
---
 CHANGELOG.asciidoc                                 |  1 +
 .../process/traversal/step/map/CallStep.java       | 22 +++----
 .../process/traversal/step/map/CallStepTest.java   | 68 ++++++++++++++++++++++
 3 files changed, 81 insertions(+), 10 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 9735cb1de8..6287d06220 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -47,6 +47,7 @@ 
image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 * Changed `PythonTranslator` to generate snake case step naming instead of 
camel case.
 * Changed `gremlin-go` Client `ReadBufferSize` and `WriteBufferSize` defaults 
to 1048576 (1MB) to align with DriverRemoteConnection.
 * Fixed bug in `IndexStep` which prevented Java serialization due to 
non-serializable lambda usage by creating serializable function classes.
+* Fixed bug in `CallStep` which prevented Java serialization due to 
non-serializable `ServiceCallContext` and `Service` usage.
 * Fixed bug in `Operator` which was caused only a single method parameter to 
be Collection type checked instead of all parameters.
 * Support hot reloading of SSL certificates.
 * Added the `PopContaining` interface designed to get label and `Pop` 
combinations held in a `PopInstruction` object.
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/CallStep.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/CallStep.java
index 2435be373c..feb7f90b8d 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/CallStep.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/CallStep.java
@@ -59,10 +59,10 @@ public final class CallStep<S, E> extends AbstractStep<S, 
E> implements Traversa
     private final boolean isStart;
     private boolean first = true;
 
-    private ServiceCallContext ctx;
-    private String serviceName;
-    private Service<S, E> service;
-    private Map staticParams;
+    private transient ServiceCallContext ctx;
+    private final String serviceName;
+    private transient Service<S, E> service;
+    private final Map staticParams;
     private Traversal.Admin<S,Map> mapTraversal;
     private Parameters parameters;
 
@@ -90,7 +90,6 @@ public final class CallStep<S, E> extends AbstractStep<S, E> 
implements Traversa
         this.staticParams = staticParams == null ? new LinkedHashMap() : 
staticParams;
         this.mapTraversal = mapTraversal == null ? null : 
integrateChild(mapTraversal);
         this.parameters = new Parameters();
-        this.ctx = new ServiceCallContext(traversal, this);
     }
 
     protected Service<S, E> service() {
@@ -98,6 +97,10 @@ public final class CallStep<S, E> extends AbstractStep<S, E> 
implements Traversa
         return service != null ? service : (service = 
getServiceRegistry().get(serviceName, isStart, staticParams));
     }
 
+    private ServiceCallContext ctx() {
+        return ctx != null ? ctx : (ctx = new ServiceCallContext(traversal, 
this));
+    }
+
     public String getServiceName() {
         return serviceName;
     }
@@ -244,17 +247,17 @@ public final class CallStep<S, E> extends AbstractStep<S, 
E> implements Traversa
 
     protected CloseableIterator start() {
         final Map params = getMergedParams();
-        return service().execute(this.ctx, params);
+        return service().execute(this.ctx(), params);
     }
 
     protected CloseableIterator flatMap(final Traverser.Admin<S> traverser) {
         final Map params = getMergedParams(traverser);
-        return service().execute(this.ctx, traverser, params);
+        return service().execute(this.ctx(), traverser, params);
     }
 
     protected CloseableIterator flatMap(final TraverserSet<S> traverserSet) {
         final Map params = getMergedParams(traverserSet);
-        return service().execute(this.ctx, traverserSet, params);
+        return service().execute(this.ctx(), traverserSet, params);
     }
 
     protected ServiceRegistry getServiceRegistry() {
@@ -288,7 +291,7 @@ public final class CallStep<S, E> extends AbstractStep<S, 
E> implements Traversa
         if (mapTraversal != null)
             this.integrateChild(mapTraversal);
         parameters.getTraversals().forEach(this::integrateChild);
-        ctx = new ServiceCallContext(parentTraversal, this);
+        ctx = null;
     }
 
     @Override
@@ -328,7 +331,6 @@ public final class CallStep<S, E> extends AbstractStep<S, 
E> implements Traversa
         final CallStep<S, E> clone = (CallStep<S, E>) super.clone();
         clone.mapTraversal = mapTraversal != null ? mapTraversal.clone() : 
null;
         clone.parameters = parameters.clone();
-        clone.ctx = new ServiceCallContext(traversal, clone);
         clone.iterator = EmptyCloseableIterator.instance();
         clone.head = null;
         return clone;
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/CallStepTest.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/CallStepTest.java
new file mode 100644
index 0000000000..8309d7abcb
--- /dev/null
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/CallStepTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.process.traversal.step.map;
+
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversal;
+import org.apache.tinkerpop.gremlin.structure.service.Service;
+import org.junit.Test;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class CallStepTest {
+
+    @Test
+    public void testSerializationRoundTrip() {
+        final byte[] serialized = 
SerializationUtils.serialize(__.call("--list"));
+        final DefaultTraversal<Object, Object> deserialized = 
SerializationUtils.deserialize(serialized);
+
+        assertEquals(1, deserialized.getSteps().size());
+        assertTrue(deserialized.getSteps().get(0) instanceof CallStep);
+        assertEquals("--list", ((CallStep) 
deserialized.getSteps().get(0)).getServiceName());
+    }
+
+    @Test
+    public void traversalReplacementTest() {
+        final Traversal.Admin a = __.inject("a").asAdmin();
+        final Traversal.Admin b = __.inject("b").asAdmin();
+
+        final CallStep step = new CallStep(a, false, "service");
+        assertEquals("a", getContextTraversalValue(step));
+        step.setTraversal(b);
+        assertEquals("b", getContextTraversalValue(step));
+    }
+
+    private Object getContextTraversalValue(final CallStep step) {
+        try {
+            final Method privateMethod = 
CallStep.class.getDeclaredMethod("ctx");
+            privateMethod.setAccessible(true);
+            final Service.ServiceCallContext context = 
(Service.ServiceCallContext) privateMethod.invoke(step);
+            return context.getTraversal().next();
+        } catch (final NoSuchMethodException | InvocationTargetException | 
IllegalAccessException e) {
+            // This should never happen.
+            throw new RuntimeException("ctx() method in class CallStep is 
renamed or removed. Please fix test.");
+        }
+    }
+}

Reply via email to