Skip to content

Commit 4aec190

Browse files
committed
docs and tests
1 parent c3f156f commit 4aec190

11 files changed

Lines changed: 809 additions & 35 deletions

File tree

CHANGELOG.asciidoc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
2727
2828
* Added Gremlator, a single page web application, that translates Gremlin into various programming languages like Javascript and Python.
2929
* Removed `uuid` dependency from `gremlin-javascript` in favor of the built-in `globalThis.crypto.randomUUID()`.
30+
* Added declarative `traversalSources` configuration to Gremlin Server YAML for creating `TraversalSource` instances with optional strategy configuration queries.
31+
* Added Java-based `lifecycleHooks` configuration to Gremlin Server YAML, replacing Groovy init script `LifeCycleHook` creation.
32+
* Added `TinkerFactoryDataLoader` `LifeCycleHook` implementation for loading sample datasets without Groovy.
33+
* Added auto-creation of `TraversalSource` bindings from `graphs` configuration (`graph` maps to `g`, others to `g_<name>`).
34+
* Added `GraphManager` to `LifeCycleHook.Context` for Java-based hooks to access configured graphs.
35+
* Deprecated Groovy-based `LifeCycleHook` and `TraversalSource` creation via init scripts in favor of YAML configuration.
36+
* Updated all default Gremlin Server configs to remove Groovy dependency from initialization.
3037
3138
[[release-4-0-0-beta-2]]
3239
=== TinkerPop 4.0.0-beta.2 (April 1, 2026)

docs/src/reference/gremlin-applications.asciidoc

Lines changed: 112 additions & 32 deletions
Large diffs are not rendered by default.

docs/src/upgrade/release-4.x.x.asciidoc

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,93 @@ deserialization in GraphBinary is unchanged. Applications that depend on the `uu
5151
`gremlin-javascript` brought it in as a transitive dependency should add it directly to their own `package.json`
5252
if they still need it.
5353
54+
==== Gremlin Server Initialization Without Groovy
55+
56+
Gremlin Server no longer requires Groovy for server initialization. Three new YAML configuration mechanisms replace
57+
the Groovy init scripts that were previously used to create `TraversalSource` bindings, load data, and run lifecycle
58+
hooks.
59+
60+
===== Auto-Created TraversalSources
61+
62+
Any graph defined in the `graphs` section that does not have an explicit `traversalSources` entry will automatically
63+
get a `TraversalSource` binding. A graph named `graph` is bound to `g`; all others are bound to `g_<name>`.
64+
65+
A minimal server configuration is now:
66+
67+
[source,yaml]
68+
----
69+
graphs: {
70+
graph: conf/tinkergraph-empty.properties}
71+
----
72+
73+
This automatically creates a `g` binding — no init script needed.
74+
75+
===== Declarative `traversalSources`
76+
77+
A new `traversalSources` YAML section allows explicit `TraversalSource` creation with optional strategy configuration:
78+
79+
[source,yaml]
80+
----
81+
traversalSources: {
82+
g: {graph: graph},
83+
gReadOnly: {graph: graph, query: "g.withStrategies(ReadOnlyStrategy)"}}
84+
----
85+
86+
Each entry specifies:
87+
88+
- `graph` (required) — references a key in the `graphs` section
89+
- `query` (optional) — a Gremlin expression evaluated with a base traversal source bound as `g`
90+
- `language` (optional) — which script engine to use for the query (defaults to `gremlin-lang`, or the single
91+
configured non-`gremlin-lang` engine if only one exists)
92+
93+
===== Java-Based `lifecycleHooks`
94+
95+
A new `lifecycleHooks` YAML section replaces Groovy-based `LifeCycleHook` creation:
96+
97+
[source,yaml]
98+
----
99+
lifecycleHooks:
100+
- className: org.apache.tinkerpop.gremlin.server.util.TinkerFactoryDataLoader
101+
config: {graph: graph, dataset: modern}
102+
----
103+
104+
Each entry specifies a `className` implementing `LifeCycleHook` and an optional `config` map passed to the hook's
105+
`init()` method. The built-in `TinkerFactoryDataLoader` supports datasets: `modern`, `classic`, `crew`, `grateful`,
106+
and `sink`.
107+
108+
===== Deprecation of Groovy Init Scripts
109+
110+
Creating `TraversalSource` and `LifeCycleHook` instances via Groovy init scripts is now deprecated. Existing scripts
111+
continue to work, but a deprecation warning is logged at startup. Migrate to the YAML-based configuration described
112+
above.
113+
114+
===== Migration Examples
115+
116+
*Before (Groovy init script):*
117+
118+
[source,groovy]
119+
----
120+
def globals = [:]
121+
globals << [hook : [
122+
onStartUp: { ctx ->
123+
org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerFactory.generateModern(graph)
124+
}
125+
] as LifeCycleHook]
126+
globals << [g : traversal().withEmbedded(graph)]
127+
----
128+
129+
*After (YAML only):*
130+
131+
[source,yaml]
132+
----
133+
graphs: {
134+
graph: conf/tinkergraph-empty.properties}
135+
lifecycleHooks:
136+
- { className: org.apache.tinkerpop.gremlin.server.util.TinkerFactoryDataLoader, config: {graph: graph, dataset: modern}}
137+
----
138+
139+
The `g` binding is auto-created from the `graph` entry. No `scriptEngines` section is needed.
140+
54141
== TinkerPop 4.0.0-beta.2
55142
56143
*Release Date: April 1, 2026*

gremlin-server/conf/gremlin-server-modern-readonly.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ graphs: {
2323
traversalSources: {
2424
g: {graph: graph, query: "g.withStrategies(ReadOnlyStrategy)"}}
2525
lifecycleHooks:
26-
- { className: org.apache.tinkerpop.gremlin.server.util.TinkerFactoryDataLoader, config: {graph: graph, dataset: classic}}
26+
- { className: org.apache.tinkerpop.gremlin.server.util.TinkerFactoryDataLoader, config: {graph: graph, dataset: modern}}
2727
serializers:
2828
- { className: org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV3, config: { ioRegistries: [org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV3] }} # application/json
2929
- { className: org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1 } # application/vnd.graphbinary-v1.0
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.tinkerpop.gremlin.server;
20+
21+
import org.apache.tinkerpop.gremlin.driver.Client;
22+
import org.apache.tinkerpop.gremlin.driver.Cluster;
23+
import org.apache.tinkerpop.gremlin.driver.Result;
24+
import org.apache.tinkerpop.gremlin.tinkergraph.structure.AbstractTinkerGraph;
25+
import org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV4;
26+
import org.junit.Test;
27+
import org.junit.runner.RunWith;
28+
import org.junit.runners.Parameterized;
29+
import org.slf4j.Logger;
30+
import org.slf4j.LoggerFactory;
31+
32+
import java.io.File;
33+
import java.io.FileInputStream;
34+
import java.util.ArrayList;
35+
import java.util.Arrays;
36+
import java.util.Collection;
37+
import java.util.List;
38+
39+
import static org.hamcrest.MatcherAssert.assertThat;
40+
import static org.hamcrest.Matchers.is;
41+
42+
/**
43+
* Validates that each shipped server config can start successfully and serve a basic query.
44+
* Configs requiring SSL or authentication infrastructure are excluded.
45+
*/
46+
@RunWith(Parameterized.class)
47+
public class GremlinServerConfigIntegrateTest {
48+
49+
private static final Logger logger = LoggerFactory.getLogger(GremlinServerConfigIntegrateTest.class);
50+
private static final int TEST_PORT = 45950;
51+
52+
@Parameterized.Parameter
53+
public String configName;
54+
55+
@Parameterized.Parameters(name = "{0}")
56+
public static Collection<String> configs() {
57+
return Arrays.asList(
58+
"gremlin-server.yaml",
59+
"gremlin-server-min.yaml",
60+
"gremlin-server-modern.yaml",
61+
"gremlin-server-classic.yaml",
62+
"gremlin-server-modern-readonly.yaml",
63+
"gremlin-server-rest-modern.yaml",
64+
"gremlin-server-transaction.yaml"
65+
);
66+
}
67+
68+
@Test
69+
public void shouldStartAndServeQuery() throws Exception {
70+
final File confDir = new File(System.getProperty("build.dir"), "../conf");
71+
final Settings settings = Settings.read(new FileInputStream(new File(confDir, configName)));
72+
73+
settings.port = TEST_PORT;
74+
ServerTestHelper.rewritePathsInGremlinServerSettings(settings);
75+
76+
// ensure a V4 serializer is available so the V4 client can connect
77+
if (!hasV4Serializer(settings)) {
78+
final Settings.SerializerSettings v4 = new Settings.SerializerSettings();
79+
v4.className = GraphBinaryMessageSerializerV4.class.getName();
80+
final List<Settings.SerializerSettings> serializers = new ArrayList<>(settings.serializers);
81+
serializers.add(v4);
82+
settings.serializers = serializers;
83+
}
84+
85+
final GremlinServer server = new GremlinServer(settings);
86+
try {
87+
server.start().join();
88+
logger.info("Started server with config: {}", configName);
89+
90+
final Cluster cluster = Cluster.build("localhost").port(TEST_PORT).create();
91+
final Client client = cluster.connect();
92+
try {
93+
final List<Result> results = client.submit("g.inject(1)").all().get();
94+
assertThat(results.size(), is(1));
95+
assertThat(results.get(0).getInt(), is(1));
96+
} finally {
97+
client.close();
98+
cluster.close();
99+
}
100+
} finally {
101+
server.getServerGremlinExecutor().getGraphManager().getAsBindings().values().stream()
102+
.filter(g -> g instanceof AbstractTinkerGraph)
103+
.forEach(g -> ((AbstractTinkerGraph) g).clear());
104+
server.stop().join();
105+
}
106+
}
107+
108+
private static boolean hasV4Serializer(final Settings settings) {
109+
return settings.serializers.stream()
110+
.anyMatch(s -> s.className.contains("V4"));
111+
}
112+
}

gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/SettingsTest.java

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@
2424

2525
import java.io.InputStream;
2626

27+
import static org.hamcrest.MatcherAssert.assertThat;
28+
import static org.hamcrest.Matchers.hasKey;
29+
import static org.hamcrest.Matchers.is;
30+
import static org.hamcrest.Matchers.notNullValue;
31+
import static org.hamcrest.Matchers.nullValue;
2732
import static org.junit.Assert.assertEquals;
2833

2934
public class SettingsTest {
@@ -56,4 +61,77 @@ public void defaultCustomValuesAreHandledCorrectly() throws Exception {
5661

5762
assertEquals("localhost", settings.customValue);
5863
}
64+
65+
@Test
66+
public void scriptEnginesDefaultsToGremlinLangWhenAbsentFromYaml() throws Exception {
67+
final InputStream stream = SettingsTest.class.getResourceAsStream("gremlin-server-minimal.yaml");
68+
final Settings settings = Settings.read(stream);
69+
70+
assertThat(settings.scriptEngines, is(notNullValue()));
71+
assertThat(settings.scriptEngines.size(), is(1));
72+
assertThat(settings.scriptEngines, hasKey("gremlin-lang"));
73+
}
74+
75+
@Test
76+
public void scriptEnginesPopulatedWhenPresentInYaml() throws Exception {
77+
final InputStream stream = SettingsTest.class.getResourceAsStream("gremlin-server-integration.yaml");
78+
final Settings settings = Settings.read(stream);
79+
80+
assertThat(settings.scriptEngines, hasKey("gremlin-groovy"));
81+
assertThat(settings.scriptEngines, hasKey("gremlin-lang"));
82+
}
83+
84+
@Test
85+
public void traversalSourcesParsedFromYaml() throws Exception {
86+
final InputStream stream = SettingsTest.class.getResourceAsStream("gremlin-server-with-traversal-sources.yaml");
87+
final Settings settings = Settings.read(stream);
88+
89+
assertThat(settings.traversalSources.size(), is(2));
90+
assertThat(settings.traversalSources, hasKey("g"));
91+
assertThat(settings.traversalSources, hasKey("gReadOnly"));
92+
93+
final Settings.TraversalSourceSettings gSettings = settings.traversalSources.get("g");
94+
assertThat(gSettings.graph, is("graph"));
95+
assertThat(gSettings.query, is(nullValue()));
96+
assertThat(gSettings.language, is(nullValue()));
97+
98+
final Settings.TraversalSourceSettings roSettings = settings.traversalSources.get("gReadOnly");
99+
assertThat(roSettings.graph, is("graph"));
100+
assertThat(roSettings.query, is("g.withStrategies(ReadOnlyStrategy)"));
101+
assertThat(roSettings.language, is("gremlin-groovy"));
102+
}
103+
104+
@Test
105+
public void traversalSourcesDefaultsToEmptyMapWhenAbsentFromYaml() throws Exception {
106+
final InputStream stream = SettingsTest.class.getResourceAsStream("gremlin-server-minimal.yaml");
107+
final Settings settings = Settings.read(stream);
108+
109+
assertThat(settings.traversalSources, is(notNullValue()));
110+
assertThat(settings.traversalSources.isEmpty(), is(true));
111+
}
112+
113+
@Test
114+
public void lifecycleHooksParsedFromYaml() throws Exception {
115+
final InputStream stream = SettingsTest.class.getResourceAsStream("gremlin-server-with-traversal-sources.yaml");
116+
final Settings settings = Settings.read(stream);
117+
118+
assertThat(settings.lifecycleHooks.size(), is(2));
119+
120+
final Settings.LifeCycleHookSettings first = settings.lifecycleHooks.get(0);
121+
assertThat(first.className, is("org.apache.tinkerpop.gremlin.server.util.TinkerFactoryDataLoader"));
122+
assertThat(first.config, hasKey("graph"));
123+
assertThat(first.config.get("dataset"), is("modern"));
124+
125+
final Settings.LifeCycleHookSettings second = settings.lifecycleHooks.get(1);
126+
assertThat(second.config.get("dataset"), is("classic"));
127+
}
128+
129+
@Test
130+
public void lifecycleHooksDefaultsToEmptyListWhenAbsentFromYaml() throws Exception {
131+
final InputStream stream = SettingsTest.class.getResourceAsStream("gremlin-server-minimal.yaml");
132+
final Settings settings = Settings.read(stream);
133+
134+
assertThat(settings.lifecycleHooks, is(notNullValue()));
135+
assertThat(settings.lifecycleHooks.isEmpty(), is(true));
136+
}
59137
}

0 commit comments

Comments
 (0)