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

davsclaus pushed a commit to branch smb2
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 392100145037299c1899217ae197cbf2ba62242f
Author: Claus Ibsen <claus.ib...@gmail.com>
AuthorDate: Wed Jan 1 15:09:34 2025 +0100

    CAMEL-21352: camel-smb - Base on camel-file. Work in progress.
---
 .../apache/camel/component/smb2/Smb2Component.java |  67 +++++++
 .../camel/component/smb2/Smb2Configuration.java    | 128 +++++++++++++
 .../apache/camel/component/smb2/Smb2Constants.java |  26 +++
 .../apache/camel/component/smb2/Smb2Endpoint.java  |  91 +++++++++
 .../org/apache/camel/component/smb2/Smb2File.java  |  85 +++++++++
 .../camel/component/smb2/Smb2FileOperations.java   |  58 ++++++
 .../camel/component/smb2/Smb2Operations.java       | 204 +++++++++++++++++++++
 7 files changed, 659 insertions(+)

diff --git 
a/components/camel-smb/src/main/java/org/apache/camel/component/smb2/Smb2Component.java
 
b/components/camel-smb/src/main/java/org/apache/camel/component/smb2/Smb2Component.java
new file mode 100644
index 00000000000..a54ce096949
--- /dev/null
+++ 
b/components/camel-smb/src/main/java/org/apache/camel/component/smb2/Smb2Component.java
@@ -0,0 +1,67 @@
+/*
+ * 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.smb2;
+
+import java.net.URI;
+import java.util.Map;
+
+import com.hierynomus.smbj.share.File;
+import org.apache.camel.CamelContext;
+import org.apache.camel.component.file.GenericFileComponent;
+import org.apache.camel.component.file.GenericFileEndpoint;
+import org.apache.camel.spi.annotations.Component;
+import org.apache.camel.util.StringHelper;
+
+@Component("smb2")
+public class Smb2Component extends GenericFileComponent<File> {
+
+    public Smb2Component() {
+    }
+
+    public Smb2Component(CamelContext context) {
+        super(context);
+    }
+
+    @Override
+    protected GenericFileEndpoint<File> buildFileEndpoint(String uri, String 
remaining, Map<String, Object> parameters)
+            throws Exception {
+        String baseUri = getBaseUri(uri);
+
+        // lets make sure we create a new configuration as each endpoint can
+        // customize its own version
+        // must pass on baseUri to the configuration (see above)
+        Smb2Configuration config = new Smb2Configuration(new URI(baseUri));
+
+        Smb2Endpoint answer = new Smb2Endpoint(uri, this, config);
+        return answer;
+    }
+
+    @Override
+    protected void afterPropertiesSet(GenericFileEndpoint<File> endpoint) 
throws Exception {
+        // noop
+    }
+
+    /**
+     * Get the base uri part before the options as they can be non URI valid 
such as the expression using $ chars and
+     * the URI constructor will regard $ as an illegal character, and we don't 
want to enforce end users to escape the $
+     * for the expression (file language)
+     */
+    protected String getBaseUri(String uri) {
+        return StringHelper.before(uri, "?", uri);
+    }
+
+}
diff --git 
a/components/camel-smb/src/main/java/org/apache/camel/component/smb2/Smb2Configuration.java
 
b/components/camel-smb/src/main/java/org/apache/camel/component/smb2/Smb2Configuration.java
new file mode 100644
index 00000000000..dbea729944f
--- /dev/null
+++ 
b/components/camel-smb/src/main/java/org/apache/camel/component/smb2/Smb2Configuration.java
@@ -0,0 +1,128 @@
+/*
+ * 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.smb2;
+
+import java.net.URI;
+
+import org.apache.camel.component.file.GenericFileConfiguration;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriParams;
+import org.apache.camel.spi.UriPath;
+
+@UriParams
+public class Smb2Configuration extends GenericFileConfiguration {
+
+    // component name is implied as the protocol
+    private String protocol;
+
+    @UriPath
+    @Metadata(required = true)
+    private String hostname;
+    @UriPath(defaultValue = "445")
+    private int port;
+    @UriPath
+    @Metadata(required = true)
+    private String shareName;
+    @UriParam(label = "security", description = "The username required to 
access the share", secret = true)
+    private String username;
+    @UriParam(label = "security", description = "The password to access the 
share", secret = true)
+    private String password;
+    @UriParam(label = "security", description = "The user domain")
+    private String domain;
+
+    public Smb2Configuration() {
+        setProtocol("smb");
+    }
+
+    public Smb2Configuration(URI uri) {
+        super.configure(uri);
+        setProtocol(uri.getScheme());
+        setHostname(uri.getHost());
+        if (uri.getPort() > 0) {
+            setPort(uri.getPort());
+        }
+    }
+
+    public String getProtocol() {
+        return protocol;
+    }
+
+    /**
+     * The smb protocol to use
+     */
+    public void setProtocol(String protocol) {
+        this.protocol = protocol;
+    }
+
+    public String getHostname() {
+        return hostname;
+    }
+
+    /**
+     * The share hostname or IP address
+     */
+    public void setHostname(String hostname) {
+        this.hostname = hostname;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    /**
+     * The share port number
+     */
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public String getShareName() {
+        return shareName;
+    }
+
+    /**
+     * The name of the share to connect to.
+     */
+    public void setShareName(String shareName) {
+        this.shareName = shareName;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getDomain() {
+        return domain;
+    }
+
+    public void setDomain(String domain) {
+        this.domain = domain;
+    }
+}
diff --git 
a/components/camel-smb/src/main/java/org/apache/camel/component/smb2/Smb2Constants.java
 
b/components/camel-smb/src/main/java/org/apache/camel/component/smb2/Smb2Constants.java
new file mode 100644
index 00000000000..bb184a8c0e4
--- /dev/null
+++ 
b/components/camel-smb/src/main/java/org/apache/camel/component/smb2/Smb2Constants.java
@@ -0,0 +1,26 @@
+/*
+ * 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.smb2;
+
+import org.apache.camel.spi.Metadata;
+
+public class Smb2Constants {
+
+    @Metadata(description = "The remote hostname.", javaType = "String")
+    public static final String FILE_HOST = "CamelFileHost";
+
+}
diff --git 
a/components/camel-smb/src/main/java/org/apache/camel/component/smb2/Smb2Endpoint.java
 
b/components/camel-smb/src/main/java/org/apache/camel/component/smb2/Smb2Endpoint.java
new file mode 100644
index 00000000000..38c52a84170
--- /dev/null
+++ 
b/components/camel-smb/src/main/java/org/apache/camel/component/smb2/Smb2Endpoint.java
@@ -0,0 +1,91 @@
+/*
+ * 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.smb2;
+
+import java.util.Map;
+
+import com.hierynomus.smbj.share.File;
+import org.apache.camel.Exchange;
+import org.apache.camel.component.file.GenericFile;
+import org.apache.camel.component.file.GenericFileEndpoint;
+import org.apache.camel.spi.EndpointServiceLocation;
+import org.apache.camel.spi.UriParam;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Smb2Endpoint extends GenericFileEndpoint<File> implements 
EndpointServiceLocation {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(Smb2Endpoint.class);
+
+    @UriParam
+    protected Smb2Configuration configuration;
+
+    protected Smb2Endpoint(String uri, Smb2Component component, 
Smb2Configuration configuration) {
+        super(uri, component);
+        this.configuration = configuration;
+    }
+
+    @Override
+    public boolean isSingletonProducer() {
+        // this producer is stateful because the smb file operations is not
+        // thread safe
+        return false;
+    }
+
+    @Override
+    public String getScheme() {
+        return "smb2";
+    }
+
+    @Override
+    public char getFileSeparator() {
+        return '/';
+    }
+
+    @Override
+    public boolean isAbsolute(String name) {
+        return name.startsWith("/");
+    }
+
+    @Override
+    public String getServiceUrl() {
+        return configuration.getProtocol() + ":" + configuration.getHostname() 
+ ":" + configuration.getPort();
+    }
+
+    @Override
+    public String getServiceProtocol() {
+        return configuration.getProtocol();
+    }
+
+    @Override
+    public Map<String, String> getServiceMetadata() {
+        if (configuration.getUsername() != null) {
+            return Map.of("username", configuration.getUsername());
+        }
+        return null;
+    }
+
+    @Override
+    public Exchange createExchange(GenericFile<File> file) {
+        Exchange answer = super.createExchange();
+        if (file != null) {
+            file.bindToExchange(answer);
+        }
+        return answer;
+    }
+
+}
diff --git 
a/components/camel-smb/src/main/java/org/apache/camel/component/smb2/Smb2File.java
 
b/components/camel-smb/src/main/java/org/apache/camel/component/smb2/Smb2File.java
new file mode 100644
index 00000000000..d0868e1a340
--- /dev/null
+++ 
b/components/camel-smb/src/main/java/org/apache/camel/component/smb2/Smb2File.java
@@ -0,0 +1,85 @@
+/*
+ * 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.smb2;
+
+import com.hierynomus.smbj.share.File;
+import org.apache.camel.component.file.GenericFile;
+import org.apache.camel.component.file.GenericFileMessage;
+
+public class Smb2File extends GenericFile<File> implements Cloneable {
+
+    private String hostname;
+
+    /**
+     * Populates the {@link GenericFileMessage} relevant headers
+     *
+     * @param message the message to populate with headers
+     */
+    public void populateHeaders(GenericFileMessage message) {
+        if (message != null) {
+            // because there is not probeContentType option
+            // in other file based components, false may be passed
+            // as the second argument.
+            super.populateHeaders(message, false);
+            message.setHeader(Smb2Constants.FILE_HOST, getHostname());
+        }
+    }
+
+    @Override
+    public void populateHeaders(GenericFileMessage message, boolean 
isProbeContentTypeFromEndpoint) {
+        populateHeaders(message);
+    }
+
+    public String getHostname() {
+        return hostname;
+    }
+
+    public void setHostname(String hostname) {
+        this.hostname = hostname;
+    }
+
+    @Override
+    public char getFileSeparator() {
+        // always use / as separator for FTP
+        return '/';
+    }
+
+    @Override
+    protected boolean isAbsolute(String name) {
+        if (!name.isEmpty()) {
+            return name.charAt(0) == '/' || name.charAt(0) == '\\';
+        }
+        return false;
+    }
+
+    @Override
+    protected String normalizePath(String name) {
+        return name;
+    }
+
+    @Override
+    public void copyFromPopulateAdditional(GenericFile source, GenericFile 
result) {
+        Smb2File remoteSource = (Smb2File) source;
+        Smb2File remoteResult = (Smb2File) result;
+        remoteResult.setHostname(remoteSource.getHostname());
+    }
+
+    @Override
+    public String toString() {
+        return "Smb2File[" + (isAbsolute() ? getAbsoluteFilePath() : 
getRelativeFilePath()) + "]";
+    }
+}
diff --git 
a/components/camel-smb/src/main/java/org/apache/camel/component/smb2/Smb2FileOperations.java
 
b/components/camel-smb/src/main/java/org/apache/camel/component/smb2/Smb2FileOperations.java
new file mode 100644
index 00000000000..316aec137b0
--- /dev/null
+++ 
b/components/camel-smb/src/main/java/org/apache/camel/component/smb2/Smb2FileOperations.java
@@ -0,0 +1,58 @@
+/*
+ * 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.smb2;
+
+import com.hierynomus.smbj.share.File;
+import org.apache.camel.Exchange;
+import org.apache.camel.component.file.GenericFileOperationFailedException;
+import org.apache.camel.component.file.GenericFileOperations;
+
+public interface Smb2FileOperations extends GenericFileOperations<File> {
+
+    /**
+     * Connects to the remote server
+     *
+     * @param  configuration                       configuration
+     * @param  exchange                            the exchange that trigger 
the connect (if any)
+     * @return                                     <tt>true</tt> if connected
+     * @throws GenericFileOperationFailedException can be thrown
+     */
+    boolean connect(Smb2Configuration configuration, Exchange exchange) throws 
GenericFileOperationFailedException;
+
+    /**
+     * Returns whether we are connected to the remote server or not
+     *
+     * @return                                     <tt>true</tt> if connected, 
<tt>false</tt> if not
+     * @throws GenericFileOperationFailedException can be thrown
+     */
+    boolean isConnected() throws GenericFileOperationFailedException;
+
+    /**
+     * Disconnects from the remote server
+     *
+     * @throws GenericFileOperationFailedException can be thrown
+     */
+    void disconnect() throws GenericFileOperationFailedException;
+
+    /**
+     * Forces a hard disconnect from the remote server and cause the client to 
be re-created on next poll.
+     *
+     * @throws GenericFileOperationFailedException can be thrown
+     */
+    void forceDisconnect() throws GenericFileOperationFailedException;
+
+}
diff --git 
a/components/camel-smb/src/main/java/org/apache/camel/component/smb2/Smb2Operations.java
 
b/components/camel-smb/src/main/java/org/apache/camel/component/smb2/Smb2Operations.java
new file mode 100644
index 00000000000..4df1e9f1e64
--- /dev/null
+++ 
b/components/camel-smb/src/main/java/org/apache/camel/component/smb2/Smb2Operations.java
@@ -0,0 +1,204 @@
+/*
+ * 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.smb2;
+
+import java.io.IOException;
+
+import com.hierynomus.smbj.SMBClient;
+import com.hierynomus.smbj.auth.AuthenticationContext;
+import com.hierynomus.smbj.connection.Connection;
+import com.hierynomus.smbj.session.Session;
+import com.hierynomus.smbj.share.DiskShare;
+import com.hierynomus.smbj.share.File;
+import com.hierynomus.smbj.utils.SmbFiles;
+import org.apache.camel.Exchange;
+import org.apache.camel.component.file.GenericFile;
+import org.apache.camel.component.file.GenericFileEndpoint;
+import org.apache.camel.component.file.GenericFileOperationFailedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Smb2Operations implements Smb2FileOperations {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(Smb2Operations.class);
+
+    private final Smb2Configuration configuration;
+    private Smb2Endpoint endpoint;
+    private boolean loggedIn;
+    private Session session;
+    private DiskShare share;
+    private SMBClient smbClient;
+
+    public Smb2Operations(Smb2Configuration configuration) {
+        this.configuration = configuration;
+    }
+
+    @Override
+    public boolean connect(Smb2Configuration configuration, Exchange exchange) 
throws GenericFileOperationFailedException {
+        if (smbClient == null) {
+            smbClient = new SMBClient();
+        }
+        try {
+            connectIfNecessary();
+        } catch (IOException e) {
+            throw new GenericFileOperationFailedException(
+                    "Cannot connect to: " + configuration.getHostname() + ":" 
+ configuration.getPort() + " due to: "
+                                                          + e.getMessage(),
+                    e);
+        }
+        return false;
+    }
+
+    protected void connectIfNecessary() throws IOException {
+        Connection connection = smbClient.connect(configuration.getHostname(), 
configuration.getPort());
+
+        if (!loggedIn || !isConnected()) {
+            LOG.debug("Not already connected/logged in. Connecting to: {}:{}", 
configuration.getHostname(),
+                    configuration.getPort());
+
+            AuthenticationContext ac = new AuthenticationContext(
+                    configuration.getUsername(),
+                    configuration.getPassword().toCharArray(),
+                    configuration.getDomain());
+            session = connection.authenticate(ac);
+
+            share = (DiskShare) 
session.connectShare(configuration.getShareName());
+
+            LOG.debug("Connected and logged in to: {}:{}", 
configuration.getHostname(), configuration.getPort());
+            loggedIn = true;
+        }
+    }
+
+    @Override
+    public boolean isConnected() throws GenericFileOperationFailedException {
+        if (share != null) {
+            return share.isConnected();
+        }
+        return false;
+    }
+
+    @Override
+    public void disconnect() throws GenericFileOperationFailedException {
+        loggedIn = false;
+        if (share != null) {
+            try {
+                share.close();
+            } catch (IOException e) {
+                // ignore
+            }
+            share = null;
+        }
+        if (session != null) {
+            try {
+                session.close();
+            } catch (Exception e) {
+                // ignore
+            }
+            session = null;
+        }
+    }
+
+    @Override
+    public void forceDisconnect() throws GenericFileOperationFailedException {
+        disconnect();
+        if (smbClient != null) {
+            smbClient.close();
+        }
+        smbClient = null;
+    }
+
+    @Override
+    public GenericFile<File> newGenericFile() {
+        return new Smb2File();
+    }
+
+    @Override
+    public void setEndpoint(GenericFileEndpoint<File> endpoint) {
+        this.endpoint = (Smb2Endpoint) endpoint;
+    }
+
+    @Override
+    public boolean deleteFile(String name) throws 
GenericFileOperationFailedException {
+        share.rm(name);
+        return true;
+    }
+
+    @Override
+    public boolean existsFile(String name) throws 
GenericFileOperationFailedException {
+        return share.fileExists(name);
+    }
+
+    @Override
+    public boolean renameFile(String from, String to) throws 
GenericFileOperationFailedException {
+        return false;
+    }
+
+    @Override
+    public boolean buildDirectory(String directory, boolean absolute) throws 
GenericFileOperationFailedException {
+        SmbFiles files = new SmbFiles();
+        files.mkdirs(share, normalize(directory));
+        return true;
+    }
+
+    @Override
+    public boolean retrieveFile(String name, Exchange exchange, long size) 
throws GenericFileOperationFailedException {
+        return true;
+    }
+
+    @Override
+    public void releaseRetrievedFileResources(Exchange exchange) throws 
GenericFileOperationFailedException {
+        // noop
+    }
+
+    @Override
+    public boolean storeFile(String name, Exchange exchange, long size) throws 
GenericFileOperationFailedException {
+        return false;
+    }
+
+    @Override
+    public String getCurrentDirectory() throws 
GenericFileOperationFailedException {
+        return null; // noop
+    }
+
+    @Override
+    public void changeCurrentDirectory(String path) throws 
GenericFileOperationFailedException {
+        // noop
+    }
+
+    @Override
+    public void changeToParentDirectory() throws 
GenericFileOperationFailedException {
+        // noop
+    }
+
+    @Override
+    public File[] listFiles() throws GenericFileOperationFailedException {
+        return null; // noop
+    }
+
+    @Override
+    public File[] listFiles(String path) throws 
GenericFileOperationFailedException {
+        return null; //noop
+    }
+
+    /*
+     * Normalize changes separators for smb
+     */
+    private String normalize(String file) {
+        return file.replace('\\', endpoint.getFileSeparator());
+    }
+
+}

Reply via email to