This is an automated email from the ASF dual-hosted git repository.
acosentino pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 5711d4556bf1 CAMEL-23106 - camel-docling - Remove temp file in
DoclingProducer.getInputPath() (#21674)
5711d4556bf1 is described below
commit 5711d4556bf1dcd4a54a7a34019eccfd172dd744
Author: Andrea Cosentino <[email protected]>
AuthorDate: Mon Mar 2 11:44:53 2026 +0100
CAMEL-23106 - camel-docling - Remove temp file in
DoclingProducer.getInputPath() (#21674)
Signed-off-by: Andrea Cosentino <[email protected]>
---
.../camel/component/docling/DoclingProducer.java | 17 ++++
.../docling/DoclingTempFileCleanupTest.java | 105 +++++++++++++++++++++
2 files changed, 122 insertions(+)
diff --git
a/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/DoclingProducer.java
b/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/DoclingProducer.java
index e066933887bb..a1a04f6f70e9 100644
---
a/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/DoclingProducer.java
+++
b/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/DoclingProducer.java
@@ -72,6 +72,7 @@ import org.apache.camel.InvalidPayloadException;
import org.apache.camel.WrappedFile;
import org.apache.camel.support.DefaultProducer;
import org.apache.camel.support.OAuthHelper;
+import org.apache.camel.support.SynchronizationAdapter;
import org.apache.camel.util.ObjectHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -1506,6 +1507,7 @@ public class DoclingProducer extends DefaultProducer {
// Treat as content to be written to a temp file
Path tempFile = Files.createTempFile("docling-", ".tmp");
Files.write(tempFile, content.getBytes());
+ registerTempFileCleanup(exchange, tempFile);
validateFileSize(tempFile.toString());
return tempFile.toString();
}
@@ -1515,6 +1517,7 @@ public class DoclingProducer extends DefaultProducer {
}
Path tempFile = Files.createTempFile("docling-", ".tmp");
Files.write(tempFile, content);
+ registerTempFileCleanup(exchange, tempFile);
return tempFile.toString();
} else if (body instanceof File file) {
validateFileSize(file.getAbsolutePath());
@@ -1524,6 +1527,20 @@ public class DoclingProducer extends DefaultProducer {
throw new InvalidPayloadException(exchange, String.class);
}
+ private void registerTempFileCleanup(Exchange exchange, Path tempFile) {
+ exchange.getExchangeExtension().addOnCompletion(new
SynchronizationAdapter() {
+ @Override
+ public void onDone(Exchange exchange) {
+ try {
+ Files.deleteIfExists(tempFile);
+ LOG.debug("Cleaned up temp file: {}", tempFile);
+ } catch (IOException e) {
+ LOG.warn("Failed to clean up temp file: {}", tempFile, e);
+ }
+ }
+ });
+ }
+
private void validateFileSize(String filePath) throws IOException {
Path path = Paths.get(filePath);
if (Files.exists(path)) {
diff --git
a/components/camel-ai/camel-docling/src/test/java/org/apache/camel/component/docling/DoclingTempFileCleanupTest.java
b/components/camel-ai/camel-docling/src/test/java/org/apache/camel/component/docling/DoclingTempFileCleanupTest.java
new file mode 100644
index 000000000000..69c0178e19ff
--- /dev/null
+++
b/components/camel-ai/camel-docling/src/test/java/org/apache/camel/component/docling/DoclingTempFileCleanupTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.camel.component.docling;
+
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Verifies that temporary files created in {@code
DoclingProducer.getInputPath()} are cleaned up after exchange
+ * processing completes.
+ *
+ * <p>
+ * Before the fix, temp files created for String content and byte[] bodies
accumulated on disk indefinitely. After the
+ * fix, an {@code addOnCompletion} callback deletes them when the exchange
finishes.
+ */
+class DoclingTempFileCleanupTest extends CamelTestSupport {
+
+ @Test
+ void tempFileFromStringContentIsCleanedUp() throws Exception {
+ // Snapshot temp files before
+ List<Path> before = listDoclingTempFiles();
+
+ // Send string content (not a path, not a URL) — this triggers temp
file creation.
+ // The docling CLI will fail (not installed), but the temp file cleanup
+ // runs on exchange completion regardless of success or failure.
+ try {
+ template.requestBody("direct:convert", "This is raw text content
to convert");
+ } catch (CamelExecutionException e) {
+ // Expected — docling binary not available in test env
+ }
+
+ // After exchange completes, temp files should have been cleaned up
+ List<Path> after = listDoclingTempFiles();
+ List<Path> leaked = new ArrayList<>(after);
+ leaked.removeAll(before);
+
+ assertTrue(leaked.isEmpty(),
+ "Temp files leaked after exchange completion: " + leaked);
+ }
+
+ @Test
+ void tempFileFromByteArrayIsCleanedUp() throws Exception {
+ List<Path> before = listDoclingTempFiles();
+
+ try {
+ template.requestBody("direct:convert", "Binary content for
conversion".getBytes());
+ } catch (CamelExecutionException e) {
+ // Expected — docling binary not available in test env
+ }
+
+ List<Path> after = listDoclingTempFiles();
+ List<Path> leaked = new ArrayList<>(after);
+ leaked.removeAll(before);
+
+ assertTrue(leaked.isEmpty(),
+ "Temp files leaked after exchange completion: " + leaked);
+ }
+
+ private List<Path> listDoclingTempFiles() throws IOException {
+ List<Path> result = new ArrayList<>();
+ Path tmpDir = Path.of(System.getProperty("java.io.tmpdir"));
+ try (DirectoryStream<Path> stream = Files.newDirectoryStream(tmpDir,
"docling-*.tmp")) {
+ for (Path entry : stream) {
+ result.add(entry);
+ }
+ }
+ return result;
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:convert")
+ .to("docling:convert?operation=CONVERT_TO_MARKDOWN");
+ }
+ };
+ }
+}