GH-206: jCIFS to the latest version for SMB2/3

Fixes https://github.com/spring-projects/spring-integration-extensions/issues/206

Updated test to better reflect reality, getCanonicalPath() -> getPath()

Updated readme doc with more information, guidance

Updated to address deprecated constructor in jCIFS library

* Polishing code, versions, checkstyle, Copyrights year
* Add `@author` to affected classes
This commit is contained in:
Gregory Bragg
2019-04-08 09:15:56 -04:00
committed by Artem Bilan
parent 2b0f44ebbb
commit 376a34c031
8 changed files with 140 additions and 76 deletions

View File

@@ -1,4 +1,4 @@
Spring Integration Smb Support
Spring Integration SMB Support
==============================
## Introduction
@@ -7,3 +7,54 @@ This module add Spring Integration support for [Server Message Block][] (SMB).
[Server Message Block]: https://en.wikipedia.org/wiki/Server_Message_Block
## Version
[Versions in Maven Repository](http://central.maven.org/maven2/org/springframework/integration/spring-integration-smb/)
## Using Maven
Put the following block into pom.xml if using Maven:
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-smb</artifactId>
<version>1.0.0.RELEASE</version>
</dependency>
## Changes
* Updated to use the latest version of the [JCIFS](https://github.com/codelibs/jcifs) library
* SMB2 (2.02 protocol level) support, some SMB3 support
## Overview
The Java CIFS Client Library has been chosen as a Java implementation for the CIFS/SMB networking protocol.
Its `SmbFile` abstraction is simply wrapped to the Spring Integration "Remote File" foundations like `SmbSession`, `SmbRemoteFileTemplate`, etc.
The SMB Channel Adapters and support classes implementations are fully similar to existing components for (S)FTP or AWS S3 protocols.
So, if you familiar with those components, it is pretty straightforward to use this extension. But any way here are several words about existing components:
### SMB Inbound Channel Adapter
To download SMB files locally the `SmbInboundFileSynchronizingMessageSource` is provided.
It is simple extension of the `AbstractInboundFileSynchronizingMessageSource` which requires `SmbInboundFileSynchronizer` injection.
For filtering remote files you still can use any existing `FileListFilter` implementations, but particular `SmbRegexPatternFileListFilter` and `SmbSimplePatternFileListFilter` are provided.
For XML configuration the `<int-smb:inbound-channel-adapter>` component is provided.
### SMB Outbound Channel Adapter
There is no (yet) some SMB specific requirements for files transferring to SMB, so for XML `<int-smb:outbound-channel-adapter>` component we simply reuse an existing `FileTransferringMessageHandler`.
In case of Java configuration that `FileTransferringMessageHandler` should be supplied with the `SmbSessionFactory` (or `SmbRemoteFileTemplate`).
@ServiceActivator(inputChannel = "storeToSmb")
@Bean
public MessageHandler smbMessageHandler(SmbSessionFactory smbSessionFactory) {
FileTransferringMessageHandler<SmbFile> handler =
new FileTransferringMessageHandler<>(smbSessionFactory);
handler.setRemoteDirectoryExpression(
new LiteralExpression("remote-target-dir"));
handler.setFileNameGenerator(m ->
m.getHeaders().get(FileHeaders.FILENAME, String.class) + ".test");
handler.setAutoCreateDirectory(true);
return handler;
}

View File

@@ -15,7 +15,7 @@ plugins {
id 'idea'
id 'jacoco'
id 'checkstyle'
id 'org.sonarqube' version '2.6.2'
id 'org.sonarqube' version '2.7'
}
description = 'Spring Integration SMB Support'
@@ -56,9 +56,9 @@ compileTestJava {
ext {
idPrefix = 'smb'
jcifsVersion = '1.3.18.3'
log4jVersion = '2.11.0'
springIntegrationVersion = '5.0.13.BUILD-SNAPSHOT'
jcifsVersion = '2.1.7'
log4jVersion = '2.11.2'
springIntegrationVersion = '5.0.13.RELEASE'
linkHomepage = 'https://github.com/SpringSource/spring-integration-extensions'
@@ -81,12 +81,12 @@ sourceSets {
}
jacoco {
toolVersion = "0.7.8"
toolVersion = "0.8.2"
}
checkstyle {
configFile = file("$rootDir/src/checkstyle/checkstyle.xml")
toolVersion = "8.9"
toolVersion = "8.19"
}
dependencies {

View File

@@ -1 +1 @@
version=1.0.1.BUILD-SNAPSHOT
version=1.1.0.BUILD-SNAPSHOT

View File

@@ -1,13 +1,12 @@
<?xml version="1.0"?>
<!DOCTYPE suppressions PUBLIC
"-//Puppy Crawl//DTD Suppressions 1.1//EN"
"https://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
"-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN"
"https://checkstyle.org/dtds/suppressions_1_2.dtd">
<suppressions>
<suppress files="package-info\.java" checks=".*" />
<suppress files="[\\/]test[\\/]" checks="RequireThis" />
<suppress files="[\\/]test[\\/]" checks="FinalClass" />
<suppress files="[\\/]test[\\/]" checks="AvoidStaticImport" />
<suppress files="[\\/]test[\\/]" checks="InnerTypeLast" />
<suppress files="CachingSessionFactory" checks="FinalClass" /> <!-- Tests spy -->
<suppress files="[\\/]test[\\/]" checks="Javadoc*" />
</suppressions>

View File

@@ -1,5 +1,8 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.2//EN" "https://www.puppycrawl.com/dtds/configuration_1_2.dtd">
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<module name="Checker">
<module name="SuppressionFilter">

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2018 the original author or authors.
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -53,6 +53,7 @@ import jcifs.smb.SmbFileOutputStream;
* @author Oleg Zhurakousky
* @author Artem Bilan
* @author Prafull Kumar Soni
* @author Gregory Bragg
*
*/
public class SmbSession implements Session<SmbFile> {
@@ -63,10 +64,6 @@ public class SmbSession implements Session<SmbFile> {
private static final String SMB_FILE_SEPARATOR = "/";
static {
configureJcifs();
}
private final SmbShare smbShare;
/**
@@ -458,7 +455,7 @@ public class SmbSession implements Session<SmbFile> {
return this.smbShare;
}
SmbFile smbFile = new SmbFile(this.smbShare, cleanedPath, SmbFile.FILE_SHARE_READ);
SmbFile smbFile = new SmbFile(this.smbShare, cleanedPath);
boolean appendFileSeparator = !cleanedPath.endsWith(SMB_FILE_SEPARATOR);
if (appendFileSeparator) {
@@ -498,32 +495,6 @@ public class SmbSession implements Session<SmbFile> {
return createSmbFileObject(_path, true);
}
/**
* Static configuration of the JCIFS library.
* The log level of this class is mapped to a suitable <code>jcifs.util.loglevel</code>
*/
static void configureJcifs() {
// TODO jcifs.Config.setProperty("jcifs.smb.client.useExtendedSecurity", "false");
// TODO jcifs.Config.setProperty("jcifs.smb.client.disablePlainTextPasswords", "false");
// set JCIFS SMB client library' log level unless already configured by system property
final String sysPropLogLevel = "jcifs.util.loglevel";
if (jcifs.Config.getProperty(sysPropLogLevel) == null) {
// set log level according to this class' logger's log level.
Log log = LogFactory.getLog(SmbSession.class);
if (log.isTraceEnabled()) {
jcifs.Config.setProperty(sysPropLogLevel, "N");
}
else if (log.isDebugEnabled()) {
jcifs.Config.setProperty(sysPropLogLevel, "3");
}
else {
jcifs.Config.setProperty(sysPropLogLevel, "1");
}
}
}
@Override
public String[] listNames(String path) {
throw new UnsupportedOperationException("Not implemented yet");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,12 +26,14 @@ import org.springframework.core.NestedIOException;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import jcifs.context.SingletonContext;
import jcifs.smb.NtlmPasswordAuthenticator;
import jcifs.smb.SmbException;
import jcifs.smb.SmbFile;
/**
* @author Markus Spann
* @since 1.0
* @author Gregory Bragg
*/
public class SmbShare extends SmbFile {
@@ -43,12 +45,20 @@ public class SmbShare extends SmbFile {
private final AtomicBoolean useTempFile = new AtomicBoolean(false);
/**
* @deprecated as of release 1.1.0, use {@link #SmbShare(SmbConfig)} instead.
* @param url do not use
* @throws IOException do not use
*/
@Deprecated
public SmbShare(String url) throws IOException {
super(StringUtils.cleanPath(url));
}
public SmbShare(SmbConfig _smbConfig) throws IOException {
this(_smbConfig.validate().getUrl());
super(StringUtils.cleanPath(_smbConfig.validate().getUrl()),
SingletonContext.getInstance().withCredentials(new NtlmPasswordAuthenticator(
_smbConfig.getDomain(), _smbConfig.getUsername(), _smbConfig.getPassword())));
}
public void init() throws NestedIOException {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,81 +27,111 @@ import jcifs.smb.SmbFile;
/**
*
* @author Gunnar Hillert
* @author Gregory Bragg
*
*/
public class SmbSessionTests {
@Test
public void testCreateSmbFileObjectWithBackSlash1() throws IOException {
System.setProperty("file.separator", "\\");
SmbShare smbShare = new SmbShare("smb://myshare/shared/");
SmbConfig config = new SmbConfig();
config.setHost("myshare");
config.setPort(445);
config.setShareAndDir("shared/");
SmbShare smbShare = new SmbShare(config);
SmbSession smbSession = new SmbSession(smbShare);
SmbFile smbFile = smbSession.createSmbFileObject("smb://myshare\\blubba\\");
assertEquals("smb://myshare/blubba/", smbFile.getCanonicalPath());
assertEquals("smb://myshare/blubba/", smbFile.getPath());
smbSession.close();
}
@Test
public void testCreateSmbFileObjectWithBackSlash2() throws IOException {
System.setProperty("file.separator", "\\");
SmbShare smbShare = new SmbShare("smb://myshare\\shared\\");
SmbConfig config = new SmbConfig();
config.setHost("myshare");
config.setPort(445);
config.setShareAndDir("shared\\");
SmbShare smbShare = new SmbShare(config);
SmbSession smbSession = new SmbSession(smbShare);
SmbFile smbFile = smbSession.createSmbFileObject("smb://myshare\\blubba\\");
assertEquals("smb://myshare/blubba/", smbFile.getCanonicalPath());
assertEquals("smb://myshare/blubba/", smbFile.getPath());
smbSession.close();
}
@Test
public void testCreateSmbFileObjectWithBackSlash3() throws IOException {
System.setProperty("file.separator", "\\");
SmbShare smbShare = new SmbShare("smb://myshare\\shared\\");
SmbConfig config = new SmbConfig();
config.setHost("myshare");
config.setPort(445);
config.setShareAndDir("shared\\");
SmbShare smbShare = new SmbShare(config);
SmbSession smbSession = new SmbSession(smbShare);
SmbFile smbFile = smbSession.createSmbFileObject("..\\another");
assertEquals("smb://myshare/another/", smbFile.getCanonicalPath());
assertEquals("smb://myshare:445/another", smbFile.getPath());
smbSession.close();
}
@Test
public void testCreateSmbFileObjectWithBackSlash4() throws IOException {
System.setProperty("file.separator", "/");
SmbShare smbShare = new SmbShare("smb://myshare/shared/");
SmbConfig config = new SmbConfig();
config.setHost("myshare");
config.setPort(445);
config.setShareAndDir("shared/");
SmbShare smbShare = new SmbShare(config);
SmbSession smbSession = new SmbSession(smbShare);
SmbFile smbFile = smbSession.createSmbFileObject("smb://myshare\\blubba\\");
assertEquals("smb://myshare/blubba/", smbFile.getCanonicalPath());
assertEquals("smb://myshare/blubba/", smbFile.getPath());
smbSession.close();
}
@Test
public void testCreateSmbFileObjectwithMissingTrailingSlash1() throws IOException {
SmbShare smbShare = new SmbShare("smb://myshare/shared");
public void testCreateSmbFileObjectWithMissingTrailingSlash1() throws IOException {
SmbConfig config = new SmbConfig();
config.setHost("myshare");
config.setPort(445);
config.setShareAndDir("shared");
SmbShare smbShare = new SmbShare(config);
SmbSession smbSession = new SmbSession(smbShare);
SmbFile smbFile = smbSession.createSmbFileObject("smb://myshare\\blubba");
assertEquals("smb://myshare/blubba/", smbFile.getCanonicalPath());
assertEquals("smb://myshare/blubba", smbFile.getPath());
smbSession.close();
}
@Test
public void testCreateSmbFileObjectwithMissingTrailingSlash2() throws IOException {
SmbShare smbShare = new SmbShare("smb://myshare/shared/");
public void testCreateSmbFileObjectWithMissingTrailingSlash2() throws IOException {
SmbConfig config = new SmbConfig();
config.setHost("myshare");
config.setPort(445);
config.setShareAndDir("shared/");
SmbShare smbShare = new SmbShare(config);
SmbSession smbSession = new SmbSession(smbShare);
SmbFile smbFile = smbSession.createSmbFileObject(".");
assertEquals("smb://myshare/shared/", smbFile.getCanonicalPath());
assertEquals("smb://myshare:445/shared/", smbFile.getPath());
smbSession.close();
}
@Test
public void testCreateSmbFileObjectwithMissingTrailingSlash3() throws IOException {
SmbShare smbShare = new SmbShare("smb://myshare/shared/");
public void testCreateSmbFileObjectWithMissingTrailingSlash3() throws IOException {
SmbConfig config = new SmbConfig();
config.setHost("myshare");
config.setPort(445);
config.setShareAndDir("shared/");
SmbShare smbShare = new SmbShare(config);
SmbSession smbSession = new SmbSession(smbShare);
SmbFile smbFile = smbSession.createSmbFileObject("../anotherShare");
assertEquals("smb://myshare/anotherShare/", smbFile.getCanonicalPath());
assertEquals("smb://myshare:445/anotherShare", smbFile.getPath());
smbSession.close();
}
}