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

acosentino pushed a commit to branch ci-issue-CAMEL-23295
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 3f2a1da139c81c8cd484b68c545356c91ac05ee6
Author: Andrea Cosentino <[email protected]>
AuthorDate: Wed Apr 8 09:38:05 2026 +0200

    CAMEL-23295: Fix resource leak and improve error handling in 
camel-splunk-hec producer
    
    - Add warning log when skipTlsVerify is enabled to alert operators
    - Replace RuntimeException with RuntimeCamelException for proper Camel 
error handling
    - Log error response body at DEBUG level for easier debugging
    - Consume response entity on success path to prevent potential connection 
leaks
    - Add unit tests for error handling behavior
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
    Signed-off-by: Andrea Cosentino <[email protected]>
---
 .../component/splunkhec/SplunkHECProducer.java     |  15 ++-
 .../component/splunkhec/SplunkHECProducerTest.java | 137 +++++++++++++++++++++
 2 files changed, 150 insertions(+), 2 deletions(-)

diff --git 
a/components/camel-splunk-hec/src/main/java/org/apache/camel/component/splunkhec/SplunkHECProducer.java
 
b/components/camel-splunk-hec/src/main/java/org/apache/camel/component/splunkhec/SplunkHECProducer.java
index 09c6665ac9e0..0936d801f727 100644
--- 
a/components/camel-splunk-hec/src/main/java/org/apache/camel/component/splunkhec/SplunkHECProducer.java
+++ 
b/components/camel-splunk-hec/src/main/java/org/apache/camel/component/splunkhec/SplunkHECProducer.java
@@ -24,6 +24,7 @@ import java.util.Map;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import org.apache.camel.Exchange;
 import org.apache.camel.Message;
+import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.support.DefaultProducer;
 import org.apache.hc.client5.http.classic.methods.HttpPost;
 import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
@@ -36,13 +37,17 @@ import 
org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
 import org.apache.hc.core5.http.ContentType;
 import org.apache.hc.core5.http.config.RegistryBuilder;
 import org.apache.hc.core5.http.io.entity.EntityTemplate;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
 import org.apache.hc.core5.http.message.StatusLine;
 import org.apache.hc.core5.ssl.SSLContextBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * The Splunk HEC producer.
  */
 public class SplunkHECProducer extends DefaultProducer {
+    private static final Logger LOG = 
LoggerFactory.getLogger(SplunkHECProducer.class);
     private static final ObjectMapper MAPPER = new ObjectMapper();
     private final SplunkHECEndpoint endpoint;
     private CloseableHttpClient httpClient;
@@ -59,6 +64,9 @@ public class SplunkHECProducer extends DefaultProducer {
                 .setUserAgent("Camel Splunk HEC/" + 
getEndpoint().getCamelContext().getVersion());
         PoolingHttpClientConnectionManager connManager;
         if (endpoint.getConfiguration().isSkipTlsVerify()) {
+            LOG.warn("Splunk HEC endpoint is configured with 
skipTlsVerify=true."
+                     + " TLS certificate and hostname verification are 
disabled."
+                     + " This should not be used in production environments.");
             SSLContextBuilder sslbuilder = new SSLContextBuilder();
             sslbuilder.loadTrustMaterial(null, (chain, authType) -> true);
             SSLConnectionSocketFactory sslsf
@@ -100,9 +108,12 @@ public class SplunkHECProducer extends DefaultProducer {
                     if (response.getCode() != 200) {
                         ByteArrayOutputStream output = new 
ByteArrayOutputStream();
                         response.getEntity().writeTo(output);
-
-                        throw new RuntimeException(new StatusLine(response) + 
"\n" + output.toString(StandardCharsets.UTF_8));
+                        String responseBody = 
output.toString(StandardCharsets.UTF_8);
+                        LOG.debug("Splunk HEC error response (HTTP {}): {}", 
response.getCode(), responseBody);
+                        throw new RuntimeCamelException(
+                                "Splunk HEC request failed: " + new 
StatusLine(response) + "\n" + responseBody);
                     }
+                    EntityUtils.consume(response.getEntity());
                     return null;
                 });
     }
diff --git 
a/components/camel-splunk-hec/src/test/java/org/apache/camel/component/splunkhec/SplunkHECProducerTest.java
 
b/components/camel-splunk-hec/src/test/java/org/apache/camel/component/splunkhec/SplunkHECProducerTest.java
new file mode 100644
index 000000000000..366c84be0c61
--- /dev/null
+++ 
b/components/camel-splunk-hec/src/test/java/org/apache/camel/component/splunkhec/SplunkHECProducerTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.splunkhec;
+
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.support.DefaultExchange;
+import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.io.HttpClientResponseHandler;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class SplunkHECProducerTest {
+
+    private DefaultCamelContext camelContext;
+    private SplunkHECEndpoint endpoint;
+    private SplunkHECProducer producer;
+
+    @BeforeEach
+    void setUp() throws Exception {
+        camelContext = new DefaultCamelContext();
+        camelContext.start();
+
+        SplunkHECConfiguration config = new SplunkHECConfiguration();
+        config.setToken("11111111-1111-1111-1111-111111111111");
+        config.setHttps(false);
+        config.setSkipTlsVerify(false);
+
+        SplunkHECComponent component = new SplunkHECComponent();
+        component.setCamelContext(camelContext);
+
+        endpoint = new SplunkHECEndpoint("splunk-hec:localhost:8088", 
component, config);
+        endpoint.setSplunkURL("localhost:8088");
+
+        producer = new SplunkHECProducer(endpoint);
+    }
+
+    @Test
+    public void testProcessThrowsRuntimeCamelExceptionOnNon200Response() 
throws Exception {
+        CloseableHttpClient mockClient = createMockClient(400, "Bad Request",
+                "{\"text\":\"Invalid data format\",\"code\":6}");
+        injectHttpClient(producer, mockClient);
+
+        Exchange exchange = new DefaultExchange(camelContext);
+        exchange.getIn().setBody("test event");
+
+        RuntimeCamelException thrown = 
assertThrows(RuntimeCamelException.class, () -> producer.process(exchange));
+        assertTrue(thrown.getMessage().contains("Splunk HEC request failed"));
+        assertTrue(thrown.getMessage().contains("Invalid data format"));
+    }
+
+    @Test
+    public void testProcessThrowsRuntimeCamelExceptionOnServerError() throws 
Exception {
+        CloseableHttpClient mockClient = createMockClient(503, "Service 
Unavailable",
+                "{\"text\":\"Server is busy\",\"code\":9}");
+        injectHttpClient(producer, mockClient);
+
+        Exchange exchange = new DefaultExchange(camelContext);
+        exchange.getIn().setBody("test event");
+
+        RuntimeCamelException thrown = 
assertThrows(RuntimeCamelException.class, () -> producer.process(exchange));
+        assertTrue(thrown.getMessage().contains("Splunk HEC request failed"));
+        assertTrue(thrown.getMessage().contains("Server is busy"));
+    }
+
+    @Test
+    public void testExceptionTypeIsRuntimeCamelExceptionNotRuntimeException() 
throws Exception {
+        CloseableHttpClient mockClient = createMockClient(401, "Unauthorized",
+                "{\"text\":\"Invalid token\",\"code\":4}");
+        injectHttpClient(producer, mockClient);
+
+        Exchange exchange = new DefaultExchange(camelContext);
+        exchange.getIn().setBody("test event");
+
+        Exception thrown = assertThrows(Exception.class, () -> 
producer.process(exchange));
+        assertEquals(RuntimeCamelException.class, thrown.getClass());
+    }
+
+    @SuppressWarnings("unchecked")
+    private CloseableHttpClient createMockClient(int statusCode, String 
reasonPhrase, String responseBody)
+            throws Exception {
+        CloseableHttpClient mockClient = mock(CloseableHttpClient.class);
+        when(mockClient.execute(any(HttpPost.class), 
any(HttpClientResponseHandler.class)))
+                .thenAnswer(invocation -> {
+                    HttpClientResponseHandler<Object> handler = 
invocation.getArgument(1);
+                    ClassicHttpResponse mockResponse = 
mock(ClassicHttpResponse.class);
+                    when(mockResponse.getCode()).thenReturn(statusCode);
+                    
when(mockResponse.getReasonPhrase()).thenReturn(reasonPhrase);
+                    
when(mockResponse.getVersion()).thenReturn(org.apache.hc.core5.http.HttpVersion.HTTP_1_1);
+                    HttpEntity entity = mock(HttpEntity.class);
+                    doAnswer(inv -> {
+                        OutputStream os = inv.getArgument(0);
+                        
os.write(responseBody.getBytes(StandardCharsets.UTF_8));
+                        return null;
+                    }).when(entity).writeTo(any(OutputStream.class));
+                    when(mockResponse.getEntity()).thenReturn(entity);
+                    return handler.handleResponse(mockResponse);
+                });
+        return mockClient;
+    }
+
+    private void injectHttpClient(SplunkHECProducer producer, 
CloseableHttpClient client) throws Exception {
+        Field httpClientField = 
SplunkHECProducer.class.getDeclaredField("httpClient");
+        httpClientField.setAccessible(true);
+        httpClientField.set(producer, client);
+    }
+}

Reply via email to