GH-212: Smb with custom transport configuration

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

* Updated to be able to use min/max SMB version property settings

* cleanup of formatting of file

* Updated based on review comments

* Updated readme for new functionality

* Some polishing of readme and follow current naming conventions

* Updated, missed naming convention on Javadoc

* Updated per review comments

* Enhancement - exposing the jcifs.CIFSContext interface

* fixed check style build errors

* Added JUnit test
This commit is contained in:
Gregory Bragg
2019-05-01 14:44:42 -04:00
committed by Artem Bilan
parent d4f42cb6b8
commit 672a196da7
4 changed files with 214 additions and 6 deletions

View File

@@ -29,6 +29,7 @@ Put the following block into pom.xml if using Maven:
##### Version 1.2
* Ability to set the SMB min/max versions in the `SmbSessionFactory` via configuration in the JCIFS library
* Ability to use a custom implementation of the `jcifs.CIFSContext` interface in the `SmbSessionFactory`
## Overview
@@ -84,3 +85,21 @@ public SmbSessionFactory smbSessionFactory() {
return smbSession;
}
````
### Intializing SmbSessionFactory with a custom implementation of the jcifs.CIFSContext interface
NOTE: Setting of the SMB protocol min/max versions must be done in your implementation of jcifs.CIFSContext
````java
@Bean
public SmbSessionFactory smbSessionFactory(new myCIFSContext()) {
SmbSessionFactory smbSession = new SmbSessionFactory();
smbSession.setHost("myHost");
smbSession.setPort(445);
smbSession.setDomain("myDomain");
smbSession.setUsername("myUser");
smbSession.setPassword("myPassword");
smbSession.setShareAndDir("myShareAndDir");
return smbSession;
}
````

View File

@@ -23,7 +23,9 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.util.Assert;
import jcifs.CIFSContext;
import jcifs.smb.SmbFile;
/**
@@ -36,10 +38,23 @@ public class SmbSessionFactory extends SmbConfig implements SessionFactory<SmbFi
private static Log logger = LogFactory.getLog(SmbSessionFactory.class);
private CIFSContext context = null;
public SmbSessionFactory() {
logger.debug("New " + getClass().getName() + " created.");
}
/**
* Initializes the SMB session factory with a custom client context configuration.
* @param _context that holds the client configuration, shared services as well as the active credentials
* @since 1.2
*/
public SmbSessionFactory(CIFSContext _context) {
Assert.notNull(_context, "_context can't be null");
this.context = _context;
logger.debug("New " + getClass().getName() + " created with CIFSContext.");
}
@Override
public final SmbSession getSession() {
try {
@@ -51,13 +66,20 @@ public class SmbSessionFactory extends SmbConfig implements SessionFactory<SmbFi
}
protected SmbSession createSession() throws IOException {
Properties props = new Properties();
props.setProperty("jcifs.smb.client.minVersion", this.getSmbMinVersion().name());
props.setProperty("jcifs.smb.client.maxVersion", this.getSmbMaxVersion().name());
SmbShare smbShare;
if (this.context != null) {
smbShare = new SmbShare(this, this.context);
}
else {
Properties props = new Properties();
props.setProperty("jcifs.smb.client.minVersion", this.getSmbMinVersion().name());
props.setProperty("jcifs.smb.client.maxVersion", this.getSmbMaxVersion().name());
SmbShare smbShare = new SmbShare(this, props);
smbShare.setReplaceFile(isReplaceFile());
smbShare.setUseTempFile(isUseTempFile());
smbShare = new SmbShare(this, props);
}
smbShare.setReplaceFile(this.isReplaceFile());
smbShare.setUseTempFile(this.isUseTempFile());
if (logger.isInfoEnabled()) {
logger.info(String.format("SMB share init: %s/%s", getHostPort(), getShareAndDir()));

View File

@@ -27,6 +27,7 @@ import org.springframework.core.NestedIOException;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import jcifs.CIFSContext;
import jcifs.config.PropertyConfiguration;
import jcifs.context.BaseContext;
import jcifs.context.SingletonContext;
@@ -71,6 +72,17 @@ public class SmbShare extends SmbFile {
_smbConfig.getDomain(), _smbConfig.getUsername(), _smbConfig.getPassword())));
}
/**
* Initializes the jCIFS library with a custom client context configuration.
* @param _smbConfig the SMB share configuration
* @param _context that holds the client configuration, shared services as well as the active credentials
* @throws IOException if an invalid SMB URL was constructed by jCIFS
* @since 1.2
*/
public SmbShare(SmbConfig _smbConfig, CIFSContext _context) throws IOException {
super(StringUtils.cleanPath(_smbConfig.validate().getUrl()), _context);
}
/**
* Initializes the jCIFS library with custom properties such as
* 'jcifs.smb.client.minVersion' and 'jcifs.smb.client.maxVersion'

View File

@@ -0,0 +1,155 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://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.springframework.integration.smb.session;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.integration.file.remote.handler.FileTransferringMessageHandler;
import org.springframework.integration.smb.AbstractBaseTests;
import org.springframework.messaging.support.GenericMessage;
import jcifs.CIFSContext;
import jcifs.context.SingletonContext;
import jcifs.smb.SmbFile;
/**
* @author Gregory Bragg
*/
public class SmbSessionFactoryWithCIFSContextTests extends AbstractBaseTests {
private SmbSession smbSession;
private SmbSessionFactory smbSessionFactory;
@Before
public void prepare() throws Exception {
smbSession = mock(SmbSession.class);
smbSessionFactory = new TestSmbSessionFactory(SingletonContext.getInstance());
assertNotNull("TestSmbSessionFactory object is null.", smbSessionFactory);
smbSessionFactory.setHost("localhost");
smbSessionFactory.setPort(445);
smbSessionFactory.setDomain("");
smbSessionFactory.setUsername("sambaguest");
smbSessionFactory.setPassword("sambaguest");
smbSessionFactory.setShareAndDir("smb-share/");
smbSessionFactory.setReplaceFile(true);
}
@Test
public void testHandleFileContentMessage() {
File file = createNewFile("remote-target-dir/handlerContent.test");
FileTransferringMessageHandler<?> handler = new FileTransferringMessageHandler<SmbFile>(smbSessionFactory);
handler.setRemoteDirectoryExpression(new LiteralExpression("remote-target-dir"));
handler.setFileNameGenerator(message -> "handlerContent.test");
handler.setAutoCreateDirectory(true);
handler.setBeanFactory(mock(BeanFactory.class));
handler.afterPropertiesSet();
handler.handleMessage(new GenericMessage<String>("hello"));
assertFileExists(file);
}
class TestSmbSessionFactory extends SmbSessionFactory {
private CIFSContext context = null;
protected TestSmbSessionFactory(CIFSContext _context) {
assertNotNull("CIFSContext object is null.", _context);
this.context = _context;
}
@Override
protected SmbSession createSession() {
try {
// test for a constructor with a CIFSContext
SmbShare smbShare = new SmbShare(this, this.context);
assertNotNull("SmbShare object is null.", smbShare);
assertThat(smbShare.toString()).isEqualTo("smb://sambaguest:sambaguest@localhost:445/smb-share/");
// the rest has been copied from SmbSendingMessageHandlerTests
when(smbSession.remove(Mockito.anyString())).thenReturn(true);
when(smbSession.list(Mockito.anyString())).thenReturn(new SmbFile[0]);
doAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock _invocation) throws Throwable {
String path = (String) _invocation.getArguments()[0];
OutputStream os = (OutputStream) _invocation.getArguments()[1];
writeToFile((this.getClass().getSimpleName() + " : TEST : " + path).getBytes(), os);
return null;
}
}).when(smbSession).read(Mockito.anyString(), Mockito.any(OutputStream.class));
doAnswer(_invocation -> {
InputStream inputStream = (InputStream) _invocation.getArguments()[0];
String path = (String) _invocation.getArguments()[1];
writeToFile(inputStream, path);
return null;
}).when(smbSession)
.write(Mockito.any(InputStream.class), Mockito.anyString());
// when(smbSession.write(Mockito.any(byte[].class), Mockito.anyString())).thenReturn(null);
// when(smbSession.write(Mockito.any(File.class), Mockito.anyString())).thenReturn(null);
doAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock _invocation) {
String path = (String) _invocation.getArguments()[0];
new File(path).mkdirs();
return null;
}
}).when(smbSession).mkdir(Mockito.anyString());
doAnswer(_invocation -> {
String pathFrom = (String) _invocation.getArguments()[0];
String pathTo = (String) _invocation.getArguments()[1];
new File(pathFrom).renameTo(new File(pathTo));
return null;
}).when(smbSession)
.rename(Mockito.anyString(), Mockito.anyString());
doNothing().when(smbSession).close();
when(smbSession.isOpen()).thenReturn(true);
return smbSession;
}
catch (Exception _ex) {
throw new RuntimeException("Failed to create mock session.", _ex);
}
}
}
}