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

pinal pushed a commit to branch ATLAS-5218
in repository https://gitbox.apache.org/repos/asf/atlas.git

commit 4c3d09a3cd4e28a35be16f0a122cc09d455df352
Author: Pinal Shah <[email protected]>
AuthorDate: Tue Feb 17 20:04:29 2026 +0530

    ATLAS-5218: Add command-line utility to run Gremlin queries
---
 distro/pom.xml                                     |   1 +
 distro/src/main/assemblies/atlas-gremlin-cli.xml   |  60 ++++++
 pom.xml                                            |   1 +
 tools/atlas-gremlin-cli/README                     |  37 ++++
 tools/atlas-gremlin-cli/pom.xml                    |  55 ++++++
 .../atlas-gremlin-cli/scripts/atlas-gremlin-cli.sh |  93 ++++++++++
 .../java/org/apache/atlas/tools/GremlinCli.java    | 202 +++++++++++++++++++++
 .../src/main/resources/atlas-logback.xml           |  47 +++++
 8 files changed, 496 insertions(+)

diff --git a/distro/pom.xml b/distro/pom.xml
index d7e958889..a953562ee 100644
--- a/distro/pom.xml
+++ b/distro/pom.xml
@@ -277,6 +277,7 @@ atlas.graph.storage.hbase.regions-per-server=1
                                         
<descriptor>src/main/assemblies/classification-updater.xml</descriptor>
                                         
<descriptor>src/main/assemblies/notification-analyzer.xml</descriptor>
                                         
<descriptor>src/main/assemblies/atlas-trino-extractor.xml</descriptor>
+                                        
<descriptor>src/main/assemblies/atlas-gremlin-cli.xml</descriptor>
                                     </descriptors>
                                     
<finalName>apache-atlas-${project.version}</finalName>
                                     <tarLongFileMode>gnu</tarLongFileMode>
diff --git a/distro/src/main/assemblies/atlas-gremlin-cli.xml 
b/distro/src/main/assemblies/atlas-gremlin-cli.xml
new file mode 100644
index 000000000..bcd88c913
--- /dev/null
+++ b/distro/src/main/assemblies/atlas-gremlin-cli.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<assembly xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+          
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2";
+          
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2
 http://maven.apache.org/xsd/assembly-1.1.2.xsd";>
+    <formats>
+        <format>tar.gz</format>
+    </formats>
+    <id>atlas-gremlin-cli</id>
+    <baseDirectory>atlas-gremlin-cli</baseDirectory>
+    <fileSets>
+        <fileSet>
+            <directory>../tools/atlas-gremlin-cli</directory>
+            <outputDirectory>.</outputDirectory>
+            <includes>
+                <include>README*</include>
+            </includes>
+        </fileSet>
+        <fileSet>
+            <directory>../tools/atlas-gremlin-cli/scripts</directory>
+            <outputDirectory>.</outputDirectory>
+            <includes>
+                <include>*.sh</include>
+            </includes>
+            <fileMode>0755</fileMode>
+            <directoryMode>0755</directoryMode>
+        </fileSet>
+        <fileSet>
+            
<directory>../tools/atlas-gremlin-cli/src/main/resources</directory>
+            <outputDirectory>.</outputDirectory>
+            <includes>
+                <include>atlas-logback.xml</include>
+            </includes>
+        </fileSet>
+        <fileSet>
+            <directory>../tools/atlas-gremlin-cli/target</directory>
+            <outputDirectory>/lib</outputDirectory>
+            <includes>
+                <include>atlas-gremlin-cli-tool-*.jar</include>
+            </includes>
+        </fileSet>
+    </fileSets>
+
+</assembly>
diff --git a/pom.xml b/pom.xml
index 079fc3125..f832a09a1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -71,6 +71,7 @@
         <module>repository</module>
         <module>server-api</module>
         <module>test-tools</module>
+        <module>tools/atlas-gremlin-cli</module>
         <module>tools/atlas-index-repair</module>
         <module>tools/classification-updater</module>
         <module>tools/notification-analyzer</module>
diff --git a/tools/atlas-gremlin-cli/README b/tools/atlas-gremlin-cli/README
new file mode 100644
index 000000000..974df2d0c
--- /dev/null
+++ b/tools/atlas-gremlin-cli/README
@@ -0,0 +1,37 @@
+#
+# 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.
+#
+
+Introduction
+  atlas-gremlin-cli is a small command-line utility to run Gremlin queries 
against Atlas' embedded JanusGraph backend.
+
+  It uses Atlas' normal configuration loading, so it requires:
+    -Datlas.conf=/path/to/atlas/conf
+
+Build
+  mvn -pl tools/atlas-gremlin-cli -am package
+
+Run
+  export ATLAS_CONF=/etc/atlas/conf
+  export ATLAS_CLASSPATH="/path/to/dependency/jars/*"
+  ./atlas-gremlin-cli.sh -q "g.V().limit(5).valueMap(true).toList()"
+
+Notes
+  - Default behavior is to rollback the transaction (safe for read-only). Use 
--commit to persist mutations.
+  - For best output, end queries with .toList() or .next() or similar 
materialization.
+
+
diff --git a/tools/atlas-gremlin-cli/pom.xml b/tools/atlas-gremlin-cli/pom.xml
new file mode 100644
index 000000000..2f92f9b1e
--- /dev/null
+++ b/tools/atlas-gremlin-cli/pom.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
https://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.atlas</groupId>
+        <artifactId>apache-atlas</artifactId>
+        <version>3.0.0-SNAPSHOT</version>
+        <relativePath>../../</relativePath>
+    </parent>
+
+    <artifactId>atlas-gremlin-cli-tool</artifactId>
+    <packaging>jar</packaging>
+    <name>Apache Atlas Gremlin CLI Tool</name>
+    <description>Apache Atlas Gremlin CLI Tool (embedded 
JanusGraph)</description>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>commons-cli</groupId>
+            <artifactId>commons-cli</artifactId>
+            <version>${commons-cli.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.atlas</groupId>
+            <artifactId>atlas-graphdb-janus</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/tools/atlas-gremlin-cli/scripts/atlas-gremlin-cli.sh 
b/tools/atlas-gremlin-cli/scripts/atlas-gremlin-cli.sh
new file mode 100755
index 000000000..e13d69227
--- /dev/null
+++ b/tools/atlas-gremlin-cli/scripts/atlas-gremlin-cli.sh
@@ -0,0 +1,93 @@
+#!/bin/bash
+#
+# 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.
+#
+
+set -euo pipefail
+
+show_usage() {
+  cat <<'USAGE'
+Usage: atlas-gremlin-cli.sh [options] [-- gremlin-cli-args]
+
+Environment variables expected (set by user or system):
+  ATLAS_CONF            Path to Atlas config directory (required unless -h)
+  ATLAS_CLASSPATH       Optional classpath from an Atlas server (appended 
before tool jars)
+
+Examples:
+  ATLAS_CONF=/etc/atlas/conf ./atlas-gremlin-cli.sh -q "g.V().limit(5)"
+  ./atlas-gremlin-cli.sh --help
+USAGE
+}
+
+# quick -h/--help handling
+for arg in "$@"; do
+  if [[ "$arg" == "-h" || "$arg" == "--help" ]]; then
+    show_usage
+    exit 0
+  fi
+done
+
+# Find java binary
+if [ -n "${JAVA_HOME:-}" ]; then
+  JAVA_BIN="${JAVA_HOME}/bin/java"
+else
+  JAVA_BIN="$(command -v java || true)"
+fi
+
+if [ -z "${JAVA_BIN}" ] || [ ! -x "${JAVA_BIN}" ]; then
+  echo "java not found. Please set JAVA_HOME or ensure java is on PATH." >&2
+  exit 1
+fi
+
+# Require ATLAS_CONF by default (keeps behaviour same as original unless user 
requests help)
+if [ -z "${ATLAS_CONF:-}" ]; then
+  echo "ATLAS_CONF is not set. Example: export ATLAS_CONF=/etc/atlas/conf" >&2
+  echo "This script will set: -Datlas.conf=\$ATLAS_CONF" >&2
+  exit 2
+fi
+
+LOGFILE_DIR="${LOGFILE_DIR:-/tmp/}"
+LOGFILE_NAME="${LOGFILE_NAME:-atlas-gremlin-cli.log}"
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+# In the distro tarball, the CLI jar is under ./lib next to this script.
+if [[ ! -d "${SCRIPT_DIR}/lib" ]]; then
+  echo "Could not locate ./lib next to this script. Are you running from the 
atlas-gremlin-cli tarball?" >&2
+  exit 3
+fi
+TOOL_CP="${SCRIPT_DIR}/lib/*"
+
+if [[ -n "${ATLAS_CLASSPATH:-}" ]]; then
+  CLASSPATH="${ATLAS_CLASSPATH}:${TOOL_CP}"
+else
+  CLASSPATH="${TOOL_CP}"
+fi
+
+LOGBACK_CFG="${SCRIPT_DIR}/atlas-logback.xml"
+if [[ ! -f "${LOGBACK_CFG}" ]]; then
+  echo "Could not locate ${LOGBACK_CFG}. Are you running from the 
atlas-gremlin-cli tarball?" >&2
+  exit 4
+fi
+exec "${JAVA_BIN}" \
+  -cp "${CLASSPATH}" \
+  -Dlogback.configurationFile="${LOGBACK_CFG}" \
+  -Datlas.log.dir="${LOGFILE_DIR}" \
+  -Datlas.log.file="${LOGFILE_NAME}" \
+  -Datlas.conf="${ATLAS_CONF}" \
+  org.apache.atlas.tools.GremlinCli "$@"
+
diff --git 
a/tools/atlas-gremlin-cli/src/main/java/org/apache/atlas/tools/GremlinCli.java 
b/tools/atlas-gremlin-cli/src/main/java/org/apache/atlas/tools/GremlinCli.java
new file mode 100644
index 000000000..bd22e7670
--- /dev/null
+++ 
b/tools/atlas-gremlin-cli/src/main/java/org/apache/atlas/tools/GremlinCli.java
@@ -0,0 +1,202 @@
+/**
+ * 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.atlas.tools;
+
+import org.apache.atlas.repository.graphdb.janus.AtlasJanusGraphDatabase;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine;
+import org.apache.tinkerpop.gremlin.process.traversal.P;
+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.janusgraph.core.JanusGraph;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.script.Bindings;
+import javax.script.ScriptException;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+/**
+ * Small embedded Gremlin CLI for Atlas' JanusGraph backend.
+ *
+ * Requires Atlas configuration directory to be provided via system property:
+ *   -Datlas.conf=/path/to/atlas/conf
+ *
+ * Examples:
+ *   -q "g.V().limit(5).valueMap(true).toList()"
+ *   -f /tmp/query.groovy
+ */
+public class GremlinCli {
+    private static final Logger LOG = 
LoggerFactory.getLogger(GremlinCli.class);
+
+    public static void main(String[] args) throws Exception {
+        CommandLine cmd = parseArgs(args);
+
+        if (cmd.hasOption("h")) {
+            printHelp();
+            return;
+        }
+
+        final String query = getQuery(cmd);
+        final boolean commit = cmd.hasOption("commit");
+
+        // Ensure AtlasJanusGraphDatabase constructor runs (it registers 
GraphSON/Janus registries)
+        // even though we primarily use getGraphInstance().
+        new AtlasJanusGraphDatabase();
+
+        JanusGraph graph = AtlasJanusGraphDatabase.getGraphInstance();
+
+        try {
+            GraphTraversalSource g = graph.traversal();
+            // DefaultImportCustomizer isn't available in some Gremlin 
distributions used by this project.
+            // Use the no-arg constructor and bind helpful symbols directly.
+            GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine();
+
+            Bindings bindings = engine.createBindings();
+            bindings.put("graph", graph);
+            bindings.put("g", g);
+            bindings.put("__", __.class);
+            bindings.put("P", P.class);
+
+            Object result = eval(engine, bindings, query);
+
+            // If user returns a Traversal (e.g. g.V()), materialize it for 
convenience.
+            if (result instanceof Traversal) {
+                result = ((Traversal<?, ?>) result).toList();
+            }
+
+            System.out.println(String.valueOf(result));
+
+            finishTx(graph, commit);
+        } catch (ScriptException se) {
+            safeRollback(graph);
+            throw se;
+        } catch (Throwable t) {
+            safeRollback(graph);
+            throw t;
+        } finally {
+            try {
+                graph.close();
+            } catch (Exception e) {
+                LOG.warn("Failed to close graph", e);
+            }
+        }
+    }
+
+    private static Object eval(GremlinGroovyScriptEngine engine, Bindings 
bindings, String query) throws ScriptException {
+        LOG.info("Executing Gremlin ({} chars)", query != null ? 
query.length() : 0);
+        return engine.eval(query, bindings);
+    }
+
+    private static void finishTx(JanusGraph graph, boolean commit) {
+        // Default behavior is rollback to protect against accidental writes.
+        if (!graph.tx().isOpen()) {
+            return;
+        }
+
+        if (commit) {
+            graph.tx().commit();
+        } else {
+            graph.tx().rollback();
+        }
+    }
+
+    private static void safeRollback(JanusGraph graph) {
+        try {
+            if (graph != null && graph.tx().isOpen()) {
+                graph.tx().rollback();
+            }
+        } catch (Exception ignored) {
+        }
+    }
+
+    // Build Options in a single place so parsing and help use the same 
definitions.
+    private static Options buildOptions() {
+        Options options = new Options();
+
+        options.addOption(Option.builder("q")
+                .longOpt("query")
+                .hasArg()
+                .argName("gremlin")
+                .desc("Gremlin-Groovy to evaluate. Example: 
g.V().limit(5).valueMap(true).toList()")
+                .build());
+
+        options.addOption(Option.builder("f")
+                .longOpt("file")
+                .hasArg()
+                .argName("path")
+                .desc("Read Gremlin-Groovy script from file")
+                .build());
+
+        options.addOption(Option.builder()
+                .longOpt("commit")
+                .desc("Commit the transaction after evaluation (default: 
rollback)")
+                .build());
+
+        options.addOption(Option.builder("h")
+                .longOpt("help")
+                .desc("Print help")
+                .build());
+
+        return options;
+    }
+
+    private static CommandLine parseArgs(String[] args) throws ParseException {
+        return new DefaultParser().parse(buildOptions(), args);
+    }
+
+    private static void printHelp() {
+        HelpFormatter formatter = new HelpFormatter();
+
+        String header = "\nEmbedded Gremlin CLI for Apache Atlas 
(JanusGraph).\n\n" +
+                "Requires: -Datlas.conf=/path/to/atlas/conf\n";
+        String footer = "\nExamples:\n" +
+                "  -q \"g.V().limit(5).valueMap(true).toList()\"\n" +
+                "  -f /tmp/query.groovy\n";
+
+        formatter.printHelp("GremlinCli", header, buildOptions(), footer, 
true);
+    }
+
+    private static String getQuery(CommandLine cmd) throws IOException {
+        String q = cmd.getOptionValue("q");
+        String f = cmd.getOptionValue("f");
+
+        if ((q == null || q.trim().isEmpty()) && (f == null || 
f.trim().isEmpty())) {
+            throw new IllegalArgumentException("Missing query. Provide 
-q/--query or -f/--file. Use -h for help.");
+        }
+
+        if (q != null && f != null) {
+            throw new IllegalArgumentException("Provide only one of -q/--query 
or -f/--file.");
+        }
+
+        if (q != null) {
+            return q;
+        }
+
+        return new String(Files.readAllBytes(Paths.get(f)), 
StandardCharsets.UTF_8);
+    }
+}
diff --git a/tools/atlas-gremlin-cli/src/main/resources/atlas-logback.xml 
b/tools/atlas-gremlin-cli/src/main/resources/atlas-logback.xml
new file mode 100644
index 000000000..f8637fb27
--- /dev/null
+++ b/tools/atlas-gremlin-cli/src/main/resources/atlas-logback.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+  ~ 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.
+  -->
+
+<configuration>
+    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+        <param name="Target" value="System.out"/>
+        <encoder>
+            <pattern>%date [%thread] %level{5} [%file:%line] %msg%n</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>INFO</level>
+        </filter>
+    </appender>
+
+    <appender name="FILE" 
class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${atlas.log.dir}/${atlas.log.file}</file>
+        <append>true</append>
+        <encoder>
+            <pattern>%date [%thread] %level{5} [%file:%line] %msg%n</pattern>
+        </encoder>
+        <rollingPolicy 
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            
<fileNamePattern>${atlas.log.dir}/${atlas.log.file}-%d</fileNamePattern>
+            <maxHistory>20</maxHistory>
+            <cleanHistoryOnStart>true</cleanHistoryOnStart>
+        </rollingPolicy>
+    </appender>
+
+    <root level="info">
+        <appender-ref ref="FILE"/>
+    </root>
+</configuration>

Reply via email to