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

Croway 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 6fd66062e6e6 CAMEL-24529: camel-djl - close the loaded ZooModel when 
the producer stops
6fd66062e6e6 is described below

commit 6fd66062e6e697746c95231d19299d868a81453b
Author: Andrea Cosentino <[email protected]>
AuthorDate: Fri Aug 28 06:42:29 2026 +0200

    CAMEL-24529: camel-djl - close the loaded ZooModel when the producer stops
    
    Each Zoo*Predictor loads a ZooModel via ModelZoo.loadModel in its 
constructor and
    keeps it for the producer's lifetime, but nothing ever closed it: 
DJLProducer had
    no doStop and AbstractPredictor had no close hook. On route stop or 
redeploy the
    native memory and file handles held by the model were leaked.
    
    Add a close() lifecycle method to AbstractPredictor (a no-op by default, so 
the
    custom predictors that hold no long-lived model are unaffected) and 
override it in
    the zoo predictors and the two zoo base classes (AbstractCvZooPredictor,
    AbstractNlpZooPredictor) to close the held model. DJLProducer.doStop now 
calls
    predictor.close().
    
    Co-Authored-By: Claude Opus 4.8 <[email protected]>
    Signed-off-by: Andrea Cosentino <[email protected]>
---
 .../apache/camel/component/djl/DJLProducer.java    |  6 +++
 .../component/djl/model/AbstractPredictor.java     |  9 +++++
 .../djl/model/audio/ZooAudioPredictor.java         |  7 ++++
 .../djl/model/cv/AbstractCvZooPredictor.java       |  7 ++++
 .../djl/model/cv/ZooImageGenerationPredictor.java  |  7 ++++
 .../djl/model/nlp/AbstractNlpZooPredictor.java     |  7 ++++
 .../djl/model/nlp/ZooQuestionAnswerPredictor.java  |  7 ++++
 .../model/timeseries/ZooForecastingPredictor.java  |  7 ++++
 .../camel/component/djl/DJLProducerTest.java       | 43 ++++++++++++++++++++++
 9 files changed, 100 insertions(+)

diff --git 
a/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/DJLProducer.java
 
b/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/DJLProducer.java
index d82c57fac13f..e0552a868850 100644
--- 
a/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/DJLProducer.java
+++ 
b/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/DJLProducer.java
@@ -36,4 +36,10 @@ public class DJLProducer extends DefaultProducer {
     public void process(Exchange exchange) throws Exception {
         this.predictor.process(exchange);
     }
+
+    @Override
+    protected void doStop() throws Exception {
+        super.doStop();
+        this.predictor.close();
+    }
 }
diff --git 
a/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/AbstractPredictor.java
 
b/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/AbstractPredictor.java
index 3d0d45a3faef..ea7b93cf2b73 100644
--- 
a/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/AbstractPredictor.java
+++ 
b/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/AbstractPredictor.java
@@ -29,6 +29,15 @@ public abstract class AbstractPredictor {
 
     public abstract void process(Exchange exchange) throws Exception;
 
+    /**
+     * Releases any resources held by this predictor, such as a model loaded 
from the DJL model zoo. Called when the
+     * owning producer is stopped. The default implementation does nothing; 
predictors that keep a long-lived model
+     * override this method to close it.
+     */
+    public void close() {
+        // no-op by default
+    }
+
     protected DJLEndpoint getEndpoint() {
         return endpoint;
     }
diff --git 
a/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/audio/ZooAudioPredictor.java
 
b/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/audio/ZooAudioPredictor.java
index 9d369c922087..e35048a22a99 100644
--- 
a/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/audio/ZooAudioPredictor.java
+++ 
b/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/audio/ZooAudioPredictor.java
@@ -74,4 +74,11 @@ public class ZooAudioPredictor extends AbstractPredictor {
             throw new RuntimeCamelException("Could not process input or 
output", e);
         }
     }
+
+    @Override
+    public void close() {
+        if (model != null) {
+            model.close();
+        }
+    }
 }
diff --git 
a/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/cv/AbstractCvZooPredictor.java
 
b/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/cv/AbstractCvZooPredictor.java
index 914ef40c76b7..f2bf3e617c9c 100644
--- 
a/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/cv/AbstractCvZooPredictor.java
+++ 
b/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/cv/AbstractCvZooPredictor.java
@@ -55,4 +55,11 @@ public abstract class AbstractCvZooPredictor<T> extends 
AbstractPredictor {
             throw new RuntimeCamelException("Could not process input or 
output", e);
         }
     }
+
+    @Override
+    public void close() {
+        if (model != null) {
+            model.close();
+        }
+    }
 }
diff --git 
a/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/cv/ZooImageGenerationPredictor.java
 
b/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/cv/ZooImageGenerationPredictor.java
index 9101e7fd22b4..bafc76db3a5e 100644
--- 
a/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/cv/ZooImageGenerationPredictor.java
+++ 
b/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/cv/ZooImageGenerationPredictor.java
@@ -73,4 +73,11 @@ public class ZooImageGenerationPredictor extends 
AbstractPredictor {
             throw new RuntimeCamelException("Could not process input or 
output", e);
         }
     }
+
+    @Override
+    public void close() {
+        if (model != null) {
+            model.close();
+        }
+    }
 }
diff --git 
a/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/nlp/AbstractNlpZooPredictor.java
 
b/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/nlp/AbstractNlpZooPredictor.java
index 64d9cfdf3509..557750e889db 100644
--- 
a/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/nlp/AbstractNlpZooPredictor.java
+++ 
b/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/nlp/AbstractNlpZooPredictor.java
@@ -52,4 +52,11 @@ public abstract class AbstractNlpZooPredictor<T> extends 
AbstractPredictor {
             throw new RuntimeCamelException("Could not process input or 
output", e);
         }
     }
+
+    @Override
+    public void close() {
+        if (model != null) {
+            model.close();
+        }
+    }
 }
diff --git 
a/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/nlp/ZooQuestionAnswerPredictor.java
 
b/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/nlp/ZooQuestionAnswerPredictor.java
index 259d2916496e..f4ad767291bb 100644
--- 
a/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/nlp/ZooQuestionAnswerPredictor.java
+++ 
b/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/nlp/ZooQuestionAnswerPredictor.java
@@ -82,4 +82,11 @@ public class ZooQuestionAnswerPredictor extends 
AbstractPredictor {
             throw new RuntimeCamelException("Could not process input or 
output", e);
         }
     }
+
+    @Override
+    public void close() {
+        if (model != null) {
+            model.close();
+        }
+    }
 }
diff --git 
a/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/timeseries/ZooForecastingPredictor.java
 
b/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/timeseries/ZooForecastingPredictor.java
index c79fa3f0a9d7..6c5d04ce09ed 100644
--- 
a/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/timeseries/ZooForecastingPredictor.java
+++ 
b/components/camel-ai/camel-djl/src/main/java/org/apache/camel/component/djl/model/timeseries/ZooForecastingPredictor.java
@@ -74,4 +74,11 @@ public class ZooForecastingPredictor extends 
AbstractPredictor {
             throw new RuntimeCamelException("Could not process input or 
output", e);
         }
     }
+
+    @Override
+    public void close() {
+        if (model != null) {
+            model.close();
+        }
+    }
 }
diff --git 
a/components/camel-ai/camel-djl/src/test/java/org/apache/camel/component/djl/DJLProducerTest.java
 
b/components/camel-ai/camel-djl/src/test/java/org/apache/camel/component/djl/DJLProducerTest.java
new file mode 100644
index 000000000000..a3e5bf58861e
--- /dev/null
+++ 
b/components/camel-ai/camel-djl/src/test/java/org/apache/camel/component/djl/DJLProducerTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.djl;
+
+import org.apache.camel.impl.DefaultCamelContext;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+class DJLProducerTest {
+
+    // Stopping the producer must release the predictor so a zoo model does 
not leak native memory across
+    // restart/redeploy (DJLProducer.doStop -> AbstractPredictor.close). For 
the custom (model-less) predictor
+    // path close() is the inherited no-op, so stopping must complete without 
error. The zoo predictors close
+    // their loaded model in their own close() overrides.
+    @Test
+    void stoppingProducerReleasesPredictor() {
+        DJLEndpoint endpoint = new 
DJLEndpoint("djl:tabular/linear_regression", null, "tabular/linear_regression");
+        endpoint.setCamelContext(new DefaultCamelContext());
+        endpoint.setModel("MyModel");
+        endpoint.setTranslator("MyTranslator");
+
+        assertDoesNotThrow(() -> {
+            DJLProducer producer = new DJLProducer(endpoint);
+            producer.start();
+            producer.stop();
+        });
+    }
+}

Reply via email to