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

acosentino pushed a commit to branch docling-backports-4.18.x
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 2c7680395394353279b49ff087e63139a0aabb94
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   |  19 ++++
 .../docling/DoclingTempFileCleanupTest.java        | 105 +++++++++++++++++++++
 2 files changed, 124 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 48e68ffa2eb4..f85a25373f37 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
@@ -66,6 +66,9 @@ import org.apache.camel.Exchange;
 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;
 
@@ -1357,6 +1360,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();
             }
@@ -1366,6 +1370,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());
@@ -1375,6 +1380,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");
+            }
+        };
+    }
+}

Reply via email to