You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by or...@apache.org on 2024/09/26 11:59:45 UTC

(camel) branch camel-4.8.x updated (e0325d2f9d0 -> d6d40c00cc1)

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

orpiske pushed a change to branch camel-4.8.x
in repository https://gitbox.apache.org/repos/asf/camel.git


    from e0325d2f9d0 CAMEL-21271: camel-jbang - Export using @PropertyInject with ENV function may fail due to type converter issue.
     new 13a68400d1d CAMEL-21264: avoid leaking implementation details
     new d6d40c00cc1 CAMEL-21263: fix eagerly closing the connection on the camel-smb consumer

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../smb/converter/SmbConverterLoader.java          | 65 +++++++++++++++
 .../services/org/apache/camel/TypeConverterLoader  |  2 +
 .../camel-smb/src/main/docs/smb-component.adoc     | 26 ++++++
 .../apache/camel/component/smb/SmbConstants.java   |  6 ++
 .../apache/camel/component/smb/SmbConsumer.java    | 52 +++++++++---
 .../SmbConverter.java}                             | 28 +++++--
 .../component/smb/SmbComponentConnectionIT.java    | 79 ++++++++++++++++++
 .../smb/SmbComponentTypeConversionIT.java          | 93 ++++++++++++++++++++++
 8 files changed, 334 insertions(+), 17 deletions(-)
 create mode 100644 components/camel-smb/src/generated/java/org/apache/camel/component/smb/converter/SmbConverterLoader.java
 create mode 100644 components/camel-smb/src/generated/resources/META-INF/services/org/apache/camel/TypeConverterLoader
 copy components/camel-smb/src/main/java/org/apache/camel/component/smb/{SmbConstants.java => converter/SmbConverter.java} (56%)
 create mode 100644 components/camel-smb/src/test/java/org/apache/camel/component/smb/SmbComponentConnectionIT.java
 create mode 100644 components/camel-smb/src/test/java/org/apache/camel/component/smb/SmbComponentTypeConversionIT.java


(camel) 01/02: CAMEL-21264: avoid leaking implementation details

Posted by or...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

orpiske pushed a commit to branch camel-4.8.x
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 13a68400d1de4c838e349a6438d1dd3ff88d0618
Author: Otavio Rodolfo Piske <an...@gmail.com>
AuthorDate: Wed Sep 25 10:49:58 2024 -0300

    CAMEL-21264: avoid leaking implementation details
    
    - also added an integration test
    - updated the documentation
---
 .../smb/converter/SmbConverterLoader.java          | 65 +++++++++++++++
 .../services/org/apache/camel/TypeConverterLoader  |  2 +
 .../camel-smb/src/main/docs/smb-component.adoc     | 26 ++++++
 .../apache/camel/component/smb/SmbConstants.java   |  6 ++
 .../apache/camel/component/smb/SmbConsumer.java    |  2 +
 .../SmbConverter.java}                             | 28 +++++--
 .../smb/SmbComponentTypeConversionIT.java          | 93 ++++++++++++++++++++++
 7 files changed, 216 insertions(+), 6 deletions(-)

diff --git a/components/camel-smb/src/generated/java/org/apache/camel/component/smb/converter/SmbConverterLoader.java b/components/camel-smb/src/generated/java/org/apache/camel/component/smb/converter/SmbConverterLoader.java
new file mode 100644
index 00000000000..da9c125ba19
--- /dev/null
+++ b/components/camel-smb/src/generated/java/org/apache/camel/component/smb/converter/SmbConverterLoader.java
@@ -0,0 +1,65 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.smb.converter;
+
+import javax.annotation.processing.Generated;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.DeferredContextBinding;
+import org.apache.camel.Exchange;
+import org.apache.camel.TypeConversionException;
+import org.apache.camel.TypeConverterLoaderException;
+import org.apache.camel.spi.TypeConverterLoader;
+import org.apache.camel.spi.TypeConverterRegistry;
+import org.apache.camel.support.SimpleTypeConverter;
+import org.apache.camel.support.TypeConverterSupport;
+import org.apache.camel.util.DoubleMap;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.TypeConverterLoaderGeneratorMojo")
+@SuppressWarnings("unchecked")
+@DeferredContextBinding
+public final class SmbConverterLoader implements TypeConverterLoader, CamelContextAware {
+
+    private CamelContext camelContext;
+
+    public SmbConverterLoader() {
+    }
+
+    @Override
+    public void setCamelContext(CamelContext camelContext) {
+        this.camelContext = camelContext;
+    }
+
+    @Override
+    public CamelContext getCamelContext() {
+        return camelContext;
+    }
+
+    @Override
+    public void load(TypeConverterRegistry registry) throws TypeConverterLoaderException {
+        registerConverters(registry);
+    }
+
+    private void registerConverters(TypeConverterRegistry registry) {
+        addTypeConverter(registry, byte[].class, com.hierynomus.smbj.share.File.class, false,
+            (type, exchange, value) -> getSmbConverter().toByteArray((com.hierynomus.smbj.share.File) value));
+        addTypeConverter(registry, java.io.InputStream.class, com.hierynomus.smbj.share.File.class, false,
+            (type, exchange, value) -> getSmbConverter().toInputStream((com.hierynomus.smbj.share.File) value));
+    }
+
+    private static void addTypeConverter(TypeConverterRegistry registry, Class<?> toType, Class<?> fromType, boolean allowNull, SimpleTypeConverter.ConversionMethod method) {
+        registry.addTypeConverter(toType, fromType, new SimpleTypeConverter(allowNull, method));
+    }
+
+    private volatile org.apache.camel.component.smb.converter.SmbConverter smbConverter;
+    private org.apache.camel.component.smb.converter.SmbConverter getSmbConverter() {
+        if (smbConverter == null) {
+            smbConverter = new org.apache.camel.component.smb.converter.SmbConverter();
+            CamelContextAware.trySetCamelContext(smbConverter, camelContext);
+        }
+        return smbConverter;
+    }
+}
diff --git a/components/camel-smb/src/generated/resources/META-INF/services/org/apache/camel/TypeConverterLoader b/components/camel-smb/src/generated/resources/META-INF/services/org/apache/camel/TypeConverterLoader
new file mode 100644
index 00000000000..dbd9de44e90
--- /dev/null
+++ b/components/camel-smb/src/generated/resources/META-INF/services/org/apache/camel/TypeConverterLoader
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+org.apache.camel.component.smb.converter.SmbConverterLoader
diff --git a/components/camel-smb/src/main/docs/smb-component.adoc b/components/camel-smb/src/main/docs/smb-component.adoc
index 9559e5e3078..5f89dc63b0c 100644
--- a/components/camel-smb/src/main/docs/smb-component.adoc
+++ b/components/camel-smb/src/main/docs/smb-component.adoc
@@ -53,8 +53,32 @@ include::partial$component-endpoint-headers.adoc[]
 
 == Examples
 
+=== Polling files
+
 For instance, polling all the files from an SMB file share and reading their contents would look like this:
 
+[source,java]
+----
+private void process(Exchange exchange) throws IOException {
+    final byte[] data = exchange.getMessage().getBody(byte[].class);
+    LOG.debug("Read exchange as bytes with contents: {}", new String(data));
+}
+
+public void configure() {
+    fromF("smb:%s/%s?username=%s&password=%s&path=/", service.address(), service.shareName(), service.userName(), service.password())
+        .process(this::process)
+        .to("mock:result");
+}
+----
+
+NOTE: you may also get the file contents as an InputStream using `exchange.getMessage().getBody(InputStream.class)`.
+
+=== Polling files (advanced)
+
+
+You can also get access to the file using the underlying `File` implementation provided by smbj. In that case, polling all the
+files from an SMB file share and reading their contents would look like this:
+
 [source,java]
 ----
 private void process(Exchange exchange) throws IOException {
@@ -74,6 +98,8 @@ public void configure() {
 [NOTE]
 ====
 Beware that the File object provided is not a `java.io.File` instance, but, instead a `com.hierynomus.smbj.share.File` instance.
+Relying on the underlying implementation may make your code more susceptible to problems between version upgrades of the library
+used to implement this component.
 ====
 
 
diff --git a/components/camel-smb/src/main/java/org/apache/camel/component/smb/SmbConstants.java b/components/camel-smb/src/main/java/org/apache/camel/component/smb/SmbConstants.java
index 0d3fd613e7a..fe7d7592d65 100644
--- a/components/camel-smb/src/main/java/org/apache/camel/component/smb/SmbConstants.java
+++ b/components/camel-smb/src/main/java/org/apache/camel/component/smb/SmbConstants.java
@@ -22,4 +22,10 @@ public interface SmbConstants {
     @Metadata(label = "producer", description = "The expected behavior if the file already exists.",
               javaType = "org.apache.camel.component.file.GenericFileExist")
     String SMB_FILE_EXISTS = "CamelSmbFileExists";
+
+    @Metadata(label = "consumer", description = "The path of the file being processed within the share")
+    String SMB_FILE_PATH = "CamelSmbFilePath";
+
+    @Metadata(label = "consumer", description = "The UNC path of the file being processed within the share")
+    String SMB_UNC_PATH = "CamelSmbUncPath";
 }
diff --git a/components/camel-smb/src/main/java/org/apache/camel/component/smb/SmbConsumer.java b/components/camel-smb/src/main/java/org/apache/camel/component/smb/SmbConsumer.java
index 8a79bb22cff..c52effead01 100644
--- a/components/camel-smb/src/main/java/org/apache/camel/component/smb/SmbConsumer.java
+++ b/components/camel-smb/src/main/java/org/apache/camel/component/smb/SmbConsumer.java
@@ -82,6 +82,8 @@ public class SmbConsumer extends ScheduledPollConsumer {
                         smbIOBean.createOptions());
 
                 repository.add(fullFilePath);
+                exchange.getMessage().setHeader(SmbConstants.SMB_FILE_PATH, file.getPath());
+                exchange.getMessage().setHeader(SmbConstants.SMB_UNC_PATH, file.getUncPath());
                 exchange.getMessage().setBody(file);
                 try {
                     getProcessor().process(exchange);
diff --git a/components/camel-smb/src/main/java/org/apache/camel/component/smb/SmbConstants.java b/components/camel-smb/src/main/java/org/apache/camel/component/smb/converter/SmbConverter.java
similarity index 56%
copy from components/camel-smb/src/main/java/org/apache/camel/component/smb/SmbConstants.java
copy to components/camel-smb/src/main/java/org/apache/camel/component/smb/converter/SmbConverter.java
index 0d3fd613e7a..32e95615935 100644
--- a/components/camel-smb/src/main/java/org/apache/camel/component/smb/SmbConstants.java
+++ b/components/camel-smb/src/main/java/org/apache/camel/component/smb/converter/SmbConverter.java
@@ -14,12 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.component.smb;
 
-import org.apache.camel.spi.Metadata;
+package org.apache.camel.component.smb.converter;
 
-public interface SmbConstants {
-    @Metadata(label = "producer", description = "The expected behavior if the file already exists.",
-              javaType = "org.apache.camel.component.file.GenericFileExist")
-    String SMB_FILE_EXISTS = "CamelSmbFileExists";
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.camel.Converter;
+
+@Converter(generateLoader = true)
+public class SmbConverter {
+
+    @Converter
+    public InputStream toInputStream(com.hierynomus.smbj.share.File smbFile) {
+        return smbFile.getInputStream();
+    }
+
+    @Converter
+    public byte[] toByteArray(com.hierynomus.smbj.share.File smbFile) {
+        try (InputStream is = toInputStream(smbFile)) {
+            return is.readAllBytes();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
 }
diff --git a/components/camel-smb/src/test/java/org/apache/camel/component/smb/SmbComponentTypeConversionIT.java b/components/camel-smb/src/test/java/org/apache/camel/component/smb/SmbComponentTypeConversionIT.java
new file mode 100644
index 00000000000..c2cb857764b
--- /dev/null
+++ b/components/camel-smb/src/test/java/org/apache/camel/component/smb/SmbComponentTypeConversionIT.java
@@ -0,0 +1,93 @@
+/*
+ * 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.smb;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.TimeUnit;
+
+import com.hierynomus.smbj.SmbConfig;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.infra.smb.services.SmbService;
+import org.apache.camel.test.infra.smb.services.SmbServiceFactory;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SmbComponentTypeConversionIT extends CamelTestSupport {
+    private static final Logger LOG = LoggerFactory.getLogger(SmbComponentTypeConversionIT.class);
+
+    @RegisterExtension
+    public static SmbService service = SmbServiceFactory.createService();
+
+    @EndpointInject("mock:result")
+    protected MockEndpoint mockResultEndpoint;
+
+    private boolean allValidConversions = true;
+
+    @Test
+    public void testSmbRead() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedMessageCount(100);
+
+        mock.assertIsSatisfied();
+        Assertions.assertTrue(allValidConversions, "There were invalid conversions");
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            private void process(Exchange exchange) {
+                final byte[] data = exchange.getMessage().getBody(byte[].class);
+
+                if (data == null || data.length == 0) {
+                    allValidConversions = false;
+                }
+
+                LOG.debug("Read exchange as bytes with contents: {}", new String(data));
+
+                final InputStream is = exchange.getMessage().getBody(InputStream.class);
+                try {
+                    LOG.debug("Read exchange via InputStream with contents: {}", new String(is.readAllBytes()));
+                } catch (IOException e) {
+                    allValidConversions = false;
+                    throw new RuntimeException(e);
+                }
+            }
+
+            public void configure() {
+                SmbConfig config = SmbConfig.builder()
+                        .withTimeout(120, TimeUnit.SECONDS) // Timeout sets Read, Write, and Transact timeouts (default is 60 seconds)
+                        .withSoTimeout(180, TimeUnit.SECONDS) // Socket Timeout (default is 0 seconds, blocks forever)
+                        .build();
+                context.getRegistry().bind("smbConfig", config);
+
+                fromF("smb:%s/%s?username=%s&password=%s&path=/&smbConfig=#smbConfig", service.address(), service.shareName(),
+                        service.userName(), service.password())
+                        .process(this::process)
+                        .to("mock:result");
+
+            }
+        };
+    }
+}


(camel) 02/02: CAMEL-21263: fix eagerly closing the connection on the camel-smb consumer

Posted by or...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

orpiske pushed a commit to branch camel-4.8.x
in repository https://gitbox.apache.org/repos/asf/camel.git

commit d6d40c00cc124eba8bd3be48c6f5c5db4048c335
Author: Otavio Rodolfo Piske <an...@gmail.com>
AuthorDate: Wed Sep 25 09:46:25 2024 -0300

    CAMEL-21263: fix eagerly closing the connection on the camel-smb consumer
    
    - also added a reproducer for the issue
---
 .../apache/camel/component/smb/SmbConsumer.java    | 50 +++++++++++---
 .../component/smb/SmbComponentConnectionIT.java    | 79 ++++++++++++++++++++++
 2 files changed, 118 insertions(+), 11 deletions(-)

diff --git a/components/camel-smb/src/main/java/org/apache/camel/component/smb/SmbConsumer.java b/components/camel-smb/src/main/java/org/apache/camel/component/smb/SmbConsumer.java
index c52effead01..6eecc1ba21e 100644
--- a/components/camel-smb/src/main/java/org/apache/camel/component/smb/SmbConsumer.java
+++ b/components/camel-smb/src/main/java/org/apache/camel/component/smb/SmbConsumer.java
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.component.smb;
 
+import java.io.IOException;
+
 import com.hierynomus.msfscc.fileinformation.FileIdBothDirectoryInformation;
 import com.hierynomus.smbj.SMBClient;
 import com.hierynomus.smbj.auth.AuthenticationContext;
@@ -27,12 +29,16 @@ import org.apache.camel.Exchange;
 import org.apache.camel.Processor;
 import org.apache.camel.spi.IdempotentRepository;
 import org.apache.camel.support.ScheduledPollConsumer;
+import org.apache.camel.util.IOHelper;
 
 public class SmbConsumer extends ScheduledPollConsumer {
     private final SmbEndpoint endpoint;
     private final SmbConfiguration configuration;
 
     private final SMBClient smbClient;
+    private Connection connection;
+    private Session session;
+    private DiskShare share;
 
     public SmbConsumer(SmbEndpoint endpoint, Processor processor) {
         super(endpoint, processor);
@@ -104,19 +110,41 @@ public class SmbConsumer extends ScheduledPollConsumer {
     protected int poll() throws Exception {
         int polledCount = 0;
 
+        polledCount = pollDirectory(share, configuration, polledCount, configuration.getPath());
+
+        return polledCount;
+    }
+
+    private void refreshConnection() throws IOException {
+        if (connection != null && connection.isConnected()) {
+            return;
+        }
+
+        connection = smbClient.connect(endpoint.getHostname(), endpoint.getPort());
+
         // start a single threaded pool to monitor events
-        try (Connection connection = smbClient.connect(endpoint.getHostname(), endpoint.getPort())) {
-            AuthenticationContext ac = new AuthenticationContext(
-                    configuration.getUsername(), configuration.getPassword().toCharArray(),
-                    configuration.getDomain());
-            Session session = connection.authenticate(ac);
-
-            // Connect to Share
-            try (DiskShare share = (DiskShare) session.connectShare(endpoint.getShareName())) {
-                polledCount = pollDirectory(share, configuration, polledCount, configuration.getPath());
-            }
+        AuthenticationContext ac = new AuthenticationContext(
+                configuration.getUsername(), configuration.getPassword().toCharArray(),
+                configuration.getDomain());
+        session = connection.authenticate(ac);
+
+        // Connect to the share
+        share = (DiskShare) session.connectShare(endpoint.getShareName());
+    }
+
+    @Override
+    protected void doStart() throws Exception {
+        super.doStart();
+
+        refreshConnection();
+    }
+
+    @Override
+    protected void doStop() throws Exception {
+        if (connection != null) {
+            IOHelper.close(connection);
         }
 
-        return polledCount;
+        super.doStop();
     }
 }
diff --git a/components/camel-smb/src/test/java/org/apache/camel/component/smb/SmbComponentConnectionIT.java b/components/camel-smb/src/test/java/org/apache/camel/component/smb/SmbComponentConnectionIT.java
new file mode 100644
index 00000000000..aeb9f8d1b7a
--- /dev/null
+++ b/components/camel-smb/src/test/java/org/apache/camel/component/smb/SmbComponentConnectionIT.java
@@ -0,0 +1,79 @@
+/*
+ * 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.smb;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import com.hierynomus.smbj.SmbConfig;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.infra.smb.services.SmbService;
+import org.apache.camel.test.infra.smb.services.SmbServiceFactory;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SmbComponentConnectionIT extends CamelTestSupport {
+    private static final Logger LOG = LoggerFactory.getLogger(SmbComponentIT.class);
+
+    @RegisterExtension
+    public static SmbService service = SmbServiceFactory.createService();
+
+    @EndpointInject("mock:result")
+    protected MockEndpoint mockResultEndpoint;
+
+    @Test
+    public void testSmbRead() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedMessageCount(100);
+
+        mock.assertIsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            private void process(Exchange exchange) throws IOException {
+                final byte[] data = exchange.getMessage().getBody(byte[].class);
+                final String filePath = exchange.getMessage().getHeader(SmbConstants.SMB_FILE_PATH, String.class);
+                final String uncPath = exchange.getMessage().getHeader(SmbConstants.SMB_UNC_PATH, String.class);
+                LOG.debug("Read exchange {} ({}) with contents: {}", filePath, uncPath, new String(data));
+            }
+
+            public void configure() {
+                SmbConfig config = SmbConfig.builder()
+                        .withTimeout(120, TimeUnit.SECONDS) // Timeout sets Read, Write, and Transact timeouts (default is 60 seconds)
+                        .withSoTimeout(180, TimeUnit.SECONDS) // Socket Timeout (default is 0 seconds, blocks forever)
+                        .build();
+                context.getRegistry().bind("smbConfig", config);
+
+                fromF("smb:%s/%s?username=%s&password=%s&path=/&smbConfig=#smbConfig", service.address(), service.shareName(),
+                        service.userName(), service.password())
+                        .to("seda:intermediate");
+
+                from("seda:intermediate?concurrentConsumers=4")
+                        .process(this::process)
+                        .to("mock:result");
+            }
+        };
+    }
+}