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

tballison pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tika.git


The following commit(s) were added to refs/heads/main by this push:
     new 7e81c2decb TIKA-4834 allow json config comments (#3065)
7e81c2decb is described below

commit 7e81c2decb7c984ef535c5843f3d4c9ea66f461d
Author: Tim Allison <[email protected]>
AuthorDate: Mon Aug 24 17:18:32 2026 -0400

    TIKA-4834 allow json config comments (#3065)
---
 CHANGES.txt                                        |  7 +++++
 .../org/apache/tika/async/cli/TikaAsyncCLI.java    |  4 ++-
 .../apache/tika/async/cli/AsyncCliParserTest.java  | 17 +++++++++++
 .../tika/pipes/core/config/ConfigMerger.java       |  6 ++--
 .../tika/pipes/core/config/ConfigMergerTest.java   | 34 ++++++++++++++++++++++
 .../server/core/TikaServerIntegrationTest.java     | 12 ++++++++
 .../configs/tika-config-server-comments.json       | 20 +++++++++++++
 7 files changed, 96 insertions(+), 4 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index eea5ad329b..7764f16272 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,12 @@
 Release 4.1.0 - unreleased
 
+   * tika-server and tika-async-cli now start from a config that contains
+     // or /* */ comments, as the configuration docs have always said they
+     may. The main loader accepted them; the steps that re-read the user's
+     file to merge in server/CLI overrides (ConfigMerger, ensurePluginRoots)
+     used their own bare parser and refused the whole file; they now use the
+     shared TikaObjectMapperFactory mapper (TIKA-4834).
+
    * The Kafka pipes iterator no longer stops at the first empty poll. A newly
      subscribed consumer spends its first poll(s) joining the group and returns
      empty even when the topic has a backlog, so the iterator could enqueue 
zero
diff --git 
a/tika-pipes/tika-async-cli/src/main/java/org/apache/tika/async/cli/TikaAsyncCLI.java
 
b/tika-pipes/tika-async-cli/src/main/java/org/apache/tika/async/cli/TikaAsyncCLI.java
index e6b7b9db03..0e9f00c98e 100644
--- 
a/tika-pipes/tika-async-cli/src/main/java/org/apache/tika/async/cli/TikaAsyncCLI.java
+++ 
b/tika-pipes/tika-async-cli/src/main/java/org/apache/tika/async/cli/TikaAsyncCLI.java
@@ -38,6 +38,7 @@ import org.slf4j.LoggerFactory;
 
 import org.apache.tika.config.EmbeddedLimits;
 import org.apache.tika.config.loader.TikaJsonConfig;
+import org.apache.tika.config.loader.TikaObjectMapperFactory;
 import org.apache.tika.exception.TikaConfigException;
 import org.apache.tika.parser.ParseContext;
 import org.apache.tika.pipes.api.FetchEmitTuple;
@@ -444,7 +445,8 @@ public class TikaAsyncCLI {
      * @return the config path to use (original if plugin-roots exists, or a 
new merged config)
      */
     static Path ensurePluginRoots(Path originalConfigPath, String pluginsDir) 
throws IOException {
-        ObjectMapper mapper = new ObjectMapper();
+        // The shared config mapper: same comment and strictness rules as the 
main loader.
+        ObjectMapper mapper = TikaObjectMapperFactory.getMapper();
         JsonNode rootNode = mapper.readTree(originalConfigPath.toFile());
 
         if (rootNode.has("plugin-roots")) {
diff --git 
a/tika-pipes/tika-async-cli/src/test/java/org/apache/tika/async/cli/AsyncCliParserTest.java
 
b/tika-pipes/tika-async-cli/src/test/java/org/apache/tika/async/cli/AsyncCliParserTest.java
index 7c151e25e2..44bd73c415 100644
--- 
a/tika-pipes/tika-async-cli/src/test/java/org/apache/tika/async/cli/AsyncCliParserTest.java
+++ 
b/tika-pipes/tika-async-cli/src/test/java/org/apache/tika/async/cli/AsyncCliParserTest.java
@@ -159,6 +159,23 @@ public class AsyncCliParserTest {
     @TempDir
     Path tempDir;
 
+    /** TIKA-4834: the config docs permit comments. */
+    @Test
+    public void testEnsurePluginRootsAcceptsComments() throws Exception {
+        Path configPath = tempDir.resolve("config-comments.json");
+        Files.writeString(configPath, """
+            // leading comment
+            {
+              /* block */
+              "pipes": { "numClients": 3 } // trailing
+            }
+            """);
+        Path result = TikaAsyncCLI.ensurePluginRoots(configPath, null);
+        JsonNode root = new ObjectMapper().readTree(result.toFile());
+        assertTrue(root.has("plugin-roots"));
+        assertEquals(3, root.get("pipes").get("numClients").asInt());
+    }
+
     @Test
     public void testEnsurePluginRootsAddsDefault() throws Exception {
         // Create a config without plugin-roots
diff --git 
a/tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/config/ConfigMerger.java
 
b/tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/config/ConfigMerger.java
index 506e14577d..57d92625b5 100644
--- 
a/tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/config/ConfigMerger.java
+++ 
b/tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/config/ConfigMerger.java
@@ -27,13 +27,13 @@ import java.util.UUID;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.SerializationFeature;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import org.apache.tika.config.TimeoutLimits;
+import org.apache.tika.config.loader.TikaObjectMapperFactory;
 import org.apache.tika.pipes.api.ComponentIds;
 
 /**
@@ -90,8 +90,8 @@ public class ConfigMerger {
      */
     public static MergeResult mergeOrCreate(Path existingConfig, 
ConfigOverrides overrides)
             throws IOException {
-        ObjectMapper mapper = new ObjectMapper();
-        mapper.enable(SerializationFeature.INDENT_OUTPUT);
+        // The shared config mapper: same comment and strictness rules as the 
main loader.
+        ObjectMapper mapper = TikaObjectMapperFactory.getMapper();
 
         ObjectNode root;
         if (existingConfig != null && Files.exists(existingConfig)) {
diff --git 
a/tika-pipes/tika-pipes-core/src/test/java/org/apache/tika/pipes/core/config/ConfigMergerTest.java
 
b/tika-pipes/tika-pipes-core/src/test/java/org/apache/tika/pipes/core/config/ConfigMergerTest.java
index c1471ce3fe..69101bfa19 100644
--- 
a/tika-pipes/tika-pipes-core/src/test/java/org/apache/tika/pipes/core/config/ConfigMergerTest.java
+++ 
b/tika-pipes/tika-pipes-core/src/test/java/org/apache/tika/pipes/core/config/ConfigMergerTest.java
@@ -82,6 +82,40 @@ public class ConfigMergerTest {
         Files.deleteIfExists(result.configPath());
     }
 
+    /** TIKA-4834: the config docs permit comments; the merge step must not 
reject them. */
+    @Test
+    public void testMergeWithCommentedConfig() throws IOException {
+        String existingConfig = """
+                // leading line comment
+                {
+                    /* block comment */
+                    "fetchers": {
+                        "existing-fetcher": { // trailing comment
+                            "file-system-fetcher": {
+                                "basePath": "/existing/path"
+                            }
+                        }
+                    },
+                    "plugin-roots": "existing-plugins"
+                }
+                """;
+        Path existingPath = tempDir.resolve("commented-config.json");
+        Files.writeString(existingPath, existingConfig);
+
+        ConfigOverrides overrides = ConfigOverrides.builder()
+                .addFetcher("new-fetcher", "file-system-fetcher", 
Map.of("basePath", "/new/path"))
+                .build();
+        ConfigMerger.MergeResult result = 
ConfigMerger.mergeOrCreate(existingPath, overrides);
+
+        // The merged file is plain JSON: readable by a strict mapper, content 
intact.
+        JsonNode root = new 
ObjectMapper().readTree(result.configPath().toFile());
+        assertEquals("/existing/path", 
root.get("fetchers").get("existing-fetcher")
+                .get("file-system-fetcher").get("basePath").asText());
+        assertTrue(root.get("fetchers").has("new-fetcher"));
+        assertEquals("existing-plugins", root.get("plugin-roots").asText());
+        Files.deleteIfExists(result.configPath());
+    }
+
     @Test
     public void testMergeWithExistingConfig() throws IOException {
         // Create existing config
diff --git 
a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerIntegrationTest.java
 
b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerIntegrationTest.java
index 9182f18f3c..d934988fb8 100644
--- 
a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerIntegrationTest.java
+++ 
b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerIntegrationTest.java
@@ -100,6 +100,18 @@ public class TikaServerIntegrationTest extends 
IntegrationTestBase {
         testBaseline();
     }
 
+    /** TIKA-4834: the docs permit comments in the config; the server must 
start from one. */
+    @Test
+    public void testCommentedConfig() throws Exception {
+        startProcess(new String[]{"-config", 
getConfig("tika-config-server-comments.json")});
+        awaitServerStartup();
+        Response response = WebClient
+                .create(endPoint + RMETA_PATH)
+                .accept("application/json")
+                .put(ClassLoader.getSystemResourceAsStream(TEST_HELLO_WORLD));
+        assertEquals(200, response.getStatus());
+    }
+
     @Test
     public void testBasicWithPipes() throws Exception {
         // Test that pipes-based parsing works for normal documents
diff --git 
a/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-comments.json
 
b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-comments.json
new file mode 100644
index 0000000000..2664a465d8
--- /dev/null
+++ 
b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-comments.json
@@ -0,0 +1,20 @@
+// TIKA-4834: comments everywhere the docs say they are allowed
+{
+  /* block comment before a key */
+  "server": {
+    "port": 9999, // trailing comment
+    "allowPipes": true,
+    "endpoints": [
+      "rmeta",
+      // comment inside an array
+      "tika"
+    ]
+  },
+  "pipes": {
+    "numClients": 1,
+    "forkedJvmArgs": [
+      "-Xmx256m"
+    ]
+  },
+  "plugin-roots": "target/plugins"
+}

Reply via email to