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"
+}