diff --git a/.gitignore b/.gitignore
index 0f182a0..5339548 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,20 @@
-*.class
-
-# Package Files #
-*.jar
-*.war
-*.ear
+*.iml
+*.ipr
+*.iws
+*.msg
+*/src/main/java/META-INF
+.classpath
+.DS_Store
+.gradle
+.idea
+.project
+.settings
+bin
+build
+build.log
+derby.log
+lib
+logs
+nohup.out
+out
+target
\ No newline at end of file
diff --git a/spring-integration-smb/log4j.properties b/spring-integration-smb/log4j.properties
new file mode 100644
index 0000000..c4672eb
--- /dev/null
+++ b/spring-integration-smb/log4j.properties
@@ -0,0 +1,8 @@
+log4j.rootCategory=WARN, stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n
+
+log4j.category.org.springframework=WARN
+log4j.category.org.springframework.integration=DEBUG
diff --git a/spring-integration-smb/pom.xml b/spring-integration-smb/pom.xml
new file mode 100644
index 0000000..6523c28
--- /dev/null
+++ b/spring-integration-smb/pom.xml
@@ -0,0 +1,253 @@
+
+
+ 4.0.0
+
+ org.springframework.integration
+ spring-integration-smb
+ 2.1.1.BUILD-SNAPSHOT
+ Spring Integration SMB Support
+
+
+
+
+ baag
+ baag-maven-root
+ 1.4-SNAPSHOT
+
+ ../../Skripte/Maven/Wurzel/
+
+
+
+ 3.0.5.RELEASE
+ 2.1.1.BUILD-SNAPSHOT
+
+
+
+
+
+ scm:svn:https://eiche.baag/svn/3rdparty/spring-integration-smb/trunk/
+ scm:svn:https://eiche.baag/svn/3rdparty/spring-integration-smb/trunk/
+ https://eiche.baag/svn/3rdparty/spring-integration-smb/trunk/
+
+
+
+
+ msp
+ Markus Spann
+ markus.spann@baaderbank.de
+ +1
+
+
+
+
+
+
+
+ src/main/java
+
+ **/*
+
+
+ **/*.java
+
+
+
+
+ src/main/resources
+
+ **/*
+
+
+
+
+
+
+ src/test/java
+
+ **/*
+
+
+ **/*.java
+
+
+
+
+ src/test/resources
+
+ **/*
+
+
+
+
+
+
+
+ maven-surefire-plugin
+
+ false
+ never
+
+ **/*Tests.java
+
+
+ **/*Abstract*.java
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-eclipse-plugin
+
+
+
+ org.springframework.ide.eclipse.core.springbuilder
+
+
+ org.springframework.ide.eclipse.core.springnature
+
+
+
+
+
+
+
+ com.mycila.maven-license-plugin
+ maven-license-plugin
+ 1.10.b1
+
+ ${basedir}
+ ${basedir}/src/main/resources/license_header.txt
+ true
+
+
+ src/**/*.java
+
+ true
+
+ ${project.inceptionYear}
+
+ UTF-8
+ true
+
+
+
+
+
+
+
+
+
+
+
+ org.samba.jcifs
+ jcifs
+ 1.3.17
+ compile
+
+
+
+ org.springframework
+ spring-core
+ ${spring.core.version}
+ compile
+
+
+ org.springframework
+ spring-context
+ ${spring.core.version}
+ compile
+
+
+ org.springframework
+ spring-context-support
+ ${spring.core.version}
+ compile
+
+
+ org.springframework
+ spring-beans
+ ${spring.core.version}
+ compile
+
+
+ org.springframework
+ spring-expression
+ ${spring.core.version}
+ compile
+
+
+ org.springframework.integration
+ spring-integration-core
+ ${spring.integration.version}
+ compile
+
+
+ org.springframework.integration
+ spring-integration-file
+ ${spring.integration.version}
+ compile
+
+
+ org.springframework.integration
+ spring-integration-test
+ ${spring.integration.version}
+ test
+
+
+ cglib
+ cglib-nodep
+ 2.2
+ test
+
+
+ log4j
+ log4j
+ 1.2.12
+ test
+
+
+ javax.activation
+ activation
+ 1.1.1
+ compile
+ true
+
+
+ org.springframework
+ spring-test
+ ${spring.core.version}
+ test
+
+
+
+ org.hamcrest
+ hamcrest-all
+ 1.1
+ test
+
+
+ org.mockito
+ mockito-all
+ 1.8.4
+ test
+
+
+ junit
+ junit-dep
+ 4.8.2
+ test
+
+
+
+
diff --git a/spring-integration-smb/src/main/java/org/springframework/integration/smb/config/SmbInboundChannelAdapterParser.java b/spring-integration-smb/src/main/java/org/springframework/integration/smb/config/SmbInboundChannelAdapterParser.java
new file mode 100644
index 0000000..29e0695
--- /dev/null
+++ b/spring-integration-smb/src/main/java/org/springframework/integration/smb/config/SmbInboundChannelAdapterParser.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright 2002-2012 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
+ *
+ * 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.springframework.integration.smb.config;
+
+import org.springframework.integration.file.config.AbstractRemoteFileInboundChannelAdapterParser;
+
+/**
+ * Parser for the SMB 'inbound-channel-adapter' element.
+ *
+ * @author Markus Spann
+ * @since 2.1.1
+ */
+public class SmbInboundChannelAdapterParser extends AbstractRemoteFileInboundChannelAdapterParser {
+
+ private static final String BASE_PACKAGE = "org.springframework.integration.smb";
+
+
+ @Override
+ protected String getMessageSourceClassname() {
+ return BASE_PACKAGE + ".inbound.SmbInboundFileSynchronizingMessageSource";
+ }
+
+ @Override
+ protected String getInboundFileSynchronizerClassname() {
+ return BASE_PACKAGE + ".inbound.SmbInboundFileSynchronizer";
+ }
+
+ @Override
+ protected String getSimplePatternFileListFilterClassname() {
+ return BASE_PACKAGE + ".filters.SmbSimplePatternFileListFilter";
+ }
+
+ @Override
+ protected String getRegexPatternFileListFilterClassname() {
+ return BASE_PACKAGE + ".filters.SmbRegexPatternFileListFilter";
+ }
+
+}
diff --git a/spring-integration-smb/src/main/java/org/springframework/integration/smb/config/SmbNamespaceHandler.java b/spring-integration-smb/src/main/java/org/springframework/integration/smb/config/SmbNamespaceHandler.java
new file mode 100644
index 0000000..cfef762
--- /dev/null
+++ b/spring-integration-smb/src/main/java/org/springframework/integration/smb/config/SmbNamespaceHandler.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2002-2012 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
+ *
+ * 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.springframework.integration.smb.config;
+
+import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHandler;
+import org.springframework.integration.file.config.RemoteFileOutboundChannelAdapterParser;
+
+/**
+ * Provides namespace support for using SMB.
+ *
+ * @author Markus Spann
+ * @since 2.1.1
+ */
+public class SmbNamespaceHandler extends AbstractIntegrationNamespaceHandler {
+
+ public void init() {
+ registerBeanDefinitionParser("inbound-channel-adapter", new SmbInboundChannelAdapterParser());
+ registerBeanDefinitionParser("outbound-channel-adapter", new RemoteFileOutboundChannelAdapterParser()); // TODO need implementation for SMB?
+ }
+
+}
diff --git a/spring-integration-smb/src/main/java/org/springframework/integration/smb/filters/SmbRegexPatternFileListFilter.java b/spring-integration-smb/src/main/java/org/springframework/integration/smb/filters/SmbRegexPatternFileListFilter.java
new file mode 100644
index 0000000..aa4a730
--- /dev/null
+++ b/spring-integration-smb/src/main/java/org/springframework/integration/smb/filters/SmbRegexPatternFileListFilter.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright 2002-2012 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
+ *
+ * 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.springframework.integration.smb.filters;
+
+import java.util.regex.Pattern;
+
+import jcifs.smb.SmbFile;
+
+import org.springframework.integration.file.filters.AbstractRegexPatternFileListFilter;
+
+/**
+ * Implementation of {@link AbstractRegexPatternFileListFilter} for SMB.
+ *
+ * @author Markus Spann
+ * @since 2.1.1
+ */
+public class SmbRegexPatternFileListFilter extends AbstractRegexPatternFileListFilter {
+
+ private final String toString;
+
+ public SmbRegexPatternFileListFilter(String _pattern) {
+ this(Pattern.compile(_pattern));
+ }
+
+ public SmbRegexPatternFileListFilter(Pattern _pattern) {
+ super(_pattern);
+ toString = getClass().getName() + "[pattern='" + _pattern + "']";
+ }
+
+ /**
+ * Gets the specified SMB file's name.
+ * @param _file SMB file object
+ * @return file name
+ * @see org.springframework.integration.file.filters.AbstractRegexPatternFileListFilter#getFilename(java.lang.Object)
+ */
+ @Override
+ protected String getFilename(SmbFile _file) {
+ return (_file != null) ? _file.getName() : null;
+ }
+
+ @Override
+ public String toString() {
+ return toString;
+ }
+
+}
diff --git a/spring-integration-smb/src/main/java/org/springframework/integration/smb/filters/SmbSimplePatternFileListFilter.java b/spring-integration-smb/src/main/java/org/springframework/integration/smb/filters/SmbSimplePatternFileListFilter.java
new file mode 100644
index 0000000..4d5bf3c
--- /dev/null
+++ b/spring-integration-smb/src/main/java/org/springframework/integration/smb/filters/SmbSimplePatternFileListFilter.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright 2002-2012 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
+ *
+ * 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.springframework.integration.smb.filters;
+
+import jcifs.smb.SmbFile;
+
+import org.springframework.integration.file.filters.AbstractSimplePatternFileListFilter;
+
+/**
+ * Implementation of {@link AbstractSimplePatternFileListFilter} for SMB.
+ *
+ * @author Markus Spann
+ */
+public class SmbSimplePatternFileListFilter extends AbstractSimplePatternFileListFilter {
+
+ private final String toString;
+
+ public SmbSimplePatternFileListFilter(String _pathPattern) {
+ super(_pathPattern);
+ toString = getClass().getName() + "[pattern='" + _pathPattern + "']";
+ }
+
+ /**
+ * Gets the specified SMB file's name.
+ * @param _file SMB file object
+ * @return file name
+ * @see org.springframework.integration.file.filters.AbstractSimplePatternFileListFilter#getFilename(java.lang.Object)
+ */
+ @Override
+ protected String getFilename(SmbFile _file) {
+ return (_file != null) ? _file.getName() : null;
+ }
+
+ @Override
+ public String toString() {
+ return toString;
+ }
+
+}
diff --git a/spring-integration-smb/src/main/java/org/springframework/integration/smb/inbound/SmbInboundFileSynchronizer.java b/spring-integration-smb/src/main/java/org/springframework/integration/smb/inbound/SmbInboundFileSynchronizer.java
new file mode 100644
index 0000000..b5f0bb9
--- /dev/null
+++ b/spring-integration-smb/src/main/java/org/springframework/integration/smb/inbound/SmbInboundFileSynchronizer.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright 2002-2012 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
+ *
+ * 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.springframework.integration.smb.inbound;
+
+import jcifs.smb.SmbFile;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.integration.file.remote.session.SessionFactory;
+import org.springframework.integration.file.remote.synchronizer.AbstractInboundFileSynchronizer;
+
+/**
+ * An implementation of {@link AbstractInboundFileSynchronizer} for SMB.
+ *
+ * @author Markus Spann
+ * @since 2.1.1
+ */
+public class SmbInboundFileSynchronizer extends AbstractInboundFileSynchronizer {
+
+ private final Log logger = LogFactory.getLog(SmbInboundFileSynchronizer.class);
+
+ private final String toString;
+
+ /**
+ * Create a synchronizer with the {@link SessionFactory} used to acquire
+ * {@link org.springframework.integration.file.remote.session.Session} instances.
+ */
+ public SmbInboundFileSynchronizer(SessionFactory _sessionFactory) {
+ super(_sessionFactory);
+ toString = getClass().getName() + "[sessionFactory=" + _sessionFactory + "]";
+ }
+
+ @Override
+ protected boolean isFile(SmbFile _file) {
+ try {
+ return _file != null && _file.isFile();
+ } catch (Exception _ex) {
+ logger.warn("Unable to get resource status [" + _file + "].", _ex);
+ }
+ return false;
+ }
+
+ @Override
+ protected String getFilename(SmbFile _file) {
+ return _file != null ? _file.getName() : null;
+ }
+
+ @Override
+ public String toString() {
+ return toString;
+ }
+
+}
diff --git a/spring-integration-smb/src/main/java/org/springframework/integration/smb/inbound/SmbInboundFileSynchronizingMessageSource.java b/spring-integration-smb/src/main/java/org/springframework/integration/smb/inbound/SmbInboundFileSynchronizingMessageSource.java
new file mode 100644
index 0000000..8407b99
--- /dev/null
+++ b/spring-integration-smb/src/main/java/org/springframework/integration/smb/inbound/SmbInboundFileSynchronizingMessageSource.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright 2002-2012 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
+ *
+ * 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.springframework.integration.smb.inbound;
+
+import java.io.File;
+import java.util.Comparator;
+
+import jcifs.smb.SmbFile;
+
+import org.springframework.integration.file.remote.synchronizer.AbstractInboundFileSynchronizer;
+import org.springframework.integration.file.remote.synchronizer.AbstractInboundFileSynchronizingMessageSource;
+
+/**
+ * A {@link org.springframework.integration.core.MessageSource} implementation for SMB.
+ *
+ * @author Markus Spann
+ * @since 2.1.1
+ */
+public class SmbInboundFileSynchronizingMessageSource extends AbstractInboundFileSynchronizingMessageSource {
+
+ // CHECKSTYLE:OFF
+ private final static String componentType = "smb:inbound-channel-adapter";
+ // CHECKSTYLE:ON
+ private final String toString;
+
+ public SmbInboundFileSynchronizingMessageSource(AbstractInboundFileSynchronizer _synchronizer) {
+ this(_synchronizer, null);
+ }
+
+ public SmbInboundFileSynchronizingMessageSource(AbstractInboundFileSynchronizer _synchronizer, Comparator _comparator) {
+ super(_synchronizer, _comparator);
+ toString = getClass().getName() + "[componentType=" + componentType + ", synchronizer=" + _synchronizer + ", comparator=" + _comparator + "]";
+ }
+
+ @Override
+ public String getComponentType() {
+ return componentType;
+ }
+
+ @Override
+ public String toString() {
+ return toString;
+ }
+
+}
diff --git a/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbConfig.java b/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbConfig.java
new file mode 100644
index 0000000..26a5856
--- /dev/null
+++ b/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbConfig.java
@@ -0,0 +1,183 @@
+/**
+ * Copyright 2002-2012 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
+ *
+ * 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.springframework.integration.smb.session;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+/**
+ * Data holder class for a SMB share configuration.
+ *
+ * SmbFile URLs syntax:
+ * smb://[[[domain;]username[:password]@]server[:port]/[[share/[dir/]file]]][?[param=value[param2=value2[...]]]
+ *
+ * @author Markus Spann
+ * @since 2.1.1
+ */
+public class SmbConfig {
+
+ private String host;
+ private int port;
+ private String domain;
+ private String username;
+ private String password;
+ private String shareAndDir;
+
+ private boolean replaceFile = false;
+ private boolean useTempFile = false;
+
+ public SmbConfig() {
+ }
+
+ public SmbConfig(String _host, int _port, String _domain, String _username, String _password, String _shareAndDir) throws UnsupportedEncodingException {
+ this();
+
+ setHost(_host);
+ setPort(_port);
+ setDomain(_domain);
+ setUsername(_username);
+ setPassword(_password);
+ setShareAndDir(_shareAndDir);
+ }
+
+ public void setHost(String _host) {
+ Assert.hasText(_host, "host must not be empty");
+ this.host = _host;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public void setPort(int _port) {
+ Assert.isTrue(_port >= 0, "port must be >= 0");
+ this.port = _port;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void setDomain(String _domain) {
+ Assert.notNull(_domain);
+ this.domain = _domain;
+ }
+
+ public String getDomain() {
+ return domain;
+ }
+
+ public void setUsername(String _username) {
+ Assert.hasText(_username, "username should be a non-empty string");
+ this.username = _username;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setPassword(String _password) {
+ Assert.notNull(_password, "password should not be null");
+ this.password = _password;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setShareAndDir(String _shareAndDir) {
+ Assert.notNull(_shareAndDir, "shareAndDir should not be null");
+ this.shareAndDir = _shareAndDir;
+ }
+
+ public String getShareAndDir() {
+ return shareAndDir;
+ }
+
+ public void setReplaceFile(boolean _replaceFile) {
+ this.replaceFile = _replaceFile;
+ }
+
+ public boolean isReplaceFile() {
+ return replaceFile;
+ }
+
+ void setUseTempFile(boolean _useTempFile) {
+ this.useTempFile = _useTempFile;
+ }
+
+ public boolean isUseTempFile() {
+ return useTempFile;
+ }
+
+ String getDomainUserPass(boolean _includePassword) {
+ String domainUserPass;
+ String user = _includePassword ? this.username : "********";
+ if (StringUtils.hasText(this.domain)) {
+ domainUserPass = String.format("%s;%s", this.domain, user);
+ } else {
+ domainUserPass = user;
+ }
+ if (StringUtils.hasText(this.password)) {
+ domainUserPass += ":" + this.password;
+ }
+ return domainUserPass;
+ }
+
+ String getHostPort() {
+ return this.host + (this.port > 0 ? String.format(":%d", this.port) : "");
+ }
+
+ /**
+ * Validates the object. Throws run-time exception if found to be invalid.
+ * @return the object
+ */
+ public final SmbConfig validate() {
+ Assert.hasText(getHost(), "host must not be empty in " + this);
+ Assert.isTrue(getPort() >= 0, "port must be >= 0 in " + this);
+ Assert.hasText(getShareAndDir(), "share must not be empty in " + this);
+ return this;
+ }
+
+ public final String getUrl() {
+ return getUrl(true);
+ }
+
+ public final String getUrl(boolean _includePassword) {
+ String domainUserPass = getDomainUserPass(_includePassword);
+ if (domainUserPass != null) {
+ try {
+ domainUserPass = URLEncoder.encode(domainUserPass, "UTF8");
+ // CHECKSTYLE:OFF
+ } catch (UnsupportedEncodingException _ex) {
+ // CHECKSTYLE:ON
+ }
+ }
+ return String.format("smb://%s@%s/%s", domainUserPass, getHostPort(), StringUtils.cleanPath(this.shareAndDir));
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName()
+ + "[url=" + getUrl(false)
+ + ", replaceFile=" + replaceFile
+ + "]";
+ }
+
+}
diff --git a/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbSession.java b/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbSession.java
new file mode 100644
index 0000000..372a344
--- /dev/null
+++ b/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbSession.java
@@ -0,0 +1,448 @@
+/**
+ * Copyright 2002-2012 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
+ *
+ * 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.springframework.integration.smb.session;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+
+import jcifs.smb.SmbException;
+import jcifs.smb.SmbFile;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.core.NestedIOException;
+import org.springframework.integration.file.remote.session.Session;
+import org.springframework.util.Assert;
+import org.springframework.util.FileCopyUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * Implementation of the {@link Session} interface for Server Message Block (SMB)
+ * also known as Common Internet File System (CIFS). The Samba project set out to
+ * create non-Windows implementations of SMB. Often Samba is thus used synonymously to SMB.
+ *
+ * SMB is an application-layer network protocol that manages shared access to files, printers
+ * and other networked resources.
+ *
+ * See Server Message Block
+ * for more details.
+ *
+ * Inspired by the sprint-integration-ftp implementation done by Mark Fisher and Oleg Zhurakousky.
+ *
+ * @author Markus Spann
+ * @author Mark Fisher
+ * @author Oleg Zhurakousky
+ *
+ * @since 2.1.1
+ */
+public class SmbSession implements Session {
+
+ private final Log logger = LogFactory.getLog(SmbSession.class);
+ private static final String FILE_SEPARATOR = System.getProperty("file.separator");
+
+ static {
+ configureJcifs();
+ }
+
+ private final SmbShare smbShare;
+
+ /**
+ * Constructor for an SMB session.
+ * @param _host server NetBIOS name, DNS name, or IP address, case-insensitive
+ * @param _port port
+ * @param _domain case-sensitive domain name
+ * @param _user case-sensitive user name
+ * @param _password case-sensitive password
+ * @param _shareAndDir server root SMB directory
+ * @param _replaceFile replace existing files if true
+ * @param _useTempFile make use temporary files when writing
+ * @throws IOException in case of I/O errors
+ */
+ SmbSession(String _host, int _port, String _domain, String _user, String _password, String _shareAndDir, boolean _replaceFile, boolean _useTempFile) throws IOException {
+ this(new SmbShare(new SmbConfig(_host, _port, _domain, _user, _password, _shareAndDir)));
+
+ smbShare.setReplaceFile(_replaceFile);
+ smbShare.setUseTempFile(_useTempFile);
+ }
+
+ /**
+ * Constructor for an SMB session.
+ * @param _smbShare SMB share resource
+ */
+ public SmbSession(SmbShare _smbShare) {
+ Assert.notNull(_smbShare, "smbShare must not be null");
+ smbShare = _smbShare;
+ logger.debug("New " + getClass().getName() + " created.");
+ }
+
+ /**
+ * Deletes the file or directory at the specified path.
+ * @param _path path to a remote file or directory
+ * @return true if delete successful, false if resource is non-existent
+ * @throws IOException on error conditions returned by a CIFS server
+ * @see org.springframework.integration.file.remote.session.Session#remove(java.lang.String)
+ */
+ public boolean remove(String _path) throws IOException {
+ Assert.hasText(_path, "path must not be empty");
+
+ boolean removed = false;
+ SmbFile removeFile = createSmbFileObject(_path);
+
+ if (removeFile.exists()) {
+ removeFile.delete();
+ removed = true;
+ }
+ if (!removed) {
+ logger.info("Could not remove non-existing resource [" + _path + "].");
+ } else if (logger.isInfoEnabled()) {
+ logger.info("Successfully removed resource [" + _path + "].");
+ }
+ return removed;
+ }
+
+ /**
+ * Returns the contents of the specified SMB resource as an array of SmbFile objects.
+ * In case the remote resource does not exist, an empty array is returned.
+ * @param _path path to a remote directory
+ * @return array of SmbFile objects
+ * @throws IOException on error conditions returned by a CIFS server or if the remote resource is not a directory.
+ * @see org.springframework.integration.file.remote.session.Session#list(java.lang.String)
+ */
+ public SmbFile[] list(String _path) throws IOException {
+ SmbFile[] files = new SmbFile[0];
+ try {
+ SmbFile smbDir = createSmbDirectoryObject(_path);
+ if (!smbDir.exists()) {
+ logger.warn("Remote directory [" + _path + "] does not exist. Cannot list resources.");
+ return files;
+ } else if (!smbDir.isDirectory()) {
+ throw new NestedIOException("Resource [" + _path + "] is not a directory. Cannot list resources.");
+ }
+
+ files = smbDir.listFiles();
+
+ } catch (SmbException _ex) {
+ throw new NestedIOException("Failed to list resources in [" + _path + "].", _ex);
+ }
+ String msg = "Successfully listed " + files.length + " resource(s) in [" + _path + "]";
+ if (logger.isDebugEnabled()) {
+ logger.debug(msg + ": " + Arrays.toString(files));
+ } else {
+ logger.info(msg + ".");
+ }
+
+ return files;
+ }
+
+ /**
+ * Reads the remote resource specified by path and copies its contents to the specified
+ * {@link OutputStream}.
+ * @param _path path to a remote file
+ * @param _outputStream output stream
+ * @throws IOException on error conditions returned by a CIFS server or if the remote resource is not a file.
+ * @see org.springframework.integration.file.remote.session.Session#read(java.lang.String, java.io.OutputStream)
+ */
+ public void read(String _path, OutputStream _outputStream) throws IOException {
+ Assert.hasText(_path, "path must not be empty");
+ Assert.notNull(_outputStream, "outputStream must not be null");
+
+ try {
+
+ SmbFile remoteFile = createSmbFileObject(_path);
+ if (!remoteFile.isFile()) {
+ throw new NestedIOException("Resource [" + _path + "] is not a file.");
+ }
+ FileCopyUtils.copy(remoteFile.getInputStream(), _outputStream);
+
+ } catch (SmbException _ex) {
+ throw new NestedIOException("Failed to read resource [" + _path + "].", _ex);
+ }
+ logger.info("Successfully read resource [" + _path + "].");
+ }
+
+ /**
+ * Writes contents of the specified {@link InputStream} to the remote resource
+ * specified by path. Remote directories are created implicitely as required.
+ * @param _inputStream input stream
+ * @param _path remote path (of a file) to write to
+ * @throws IOException on error conditions returned by a CIFS server
+ * @see org.springframework.integration.file.remote.session.Session#write(java.io.InputStream, java.lang.String)
+ */
+ public void write(InputStream _inputStream, String _path) throws IOException {
+ Assert.notNull(_inputStream, "inputStream must not be empty");
+ Assert.hasText(_path, "path must not be null");
+
+ try {
+
+ mkdirs(_path);
+
+ SmbFile targetFile = createSmbFileObject(_path);
+
+ if (smbShare.isUseTempFile()) {
+
+ String tempFileName = _path + smbShare.newTempFileSuffix();
+ SmbFile tempFile = createSmbFileObject(tempFileName);
+ tempFile.createNewFile();
+ Assert.isTrue(tempFile.canWrite(), "Temporary file [" + tempFileName + "] is not writable.");
+
+ FileCopyUtils.copy(_inputStream, tempFile.getOutputStream());
+
+ if (targetFile.exists() && smbShare.isReplaceFile()) {
+ targetFile.delete();
+ }
+ tempFile.renameTo(targetFile);
+
+ } else {
+
+ FileCopyUtils.copy(_inputStream, targetFile.getOutputStream());
+
+ }
+
+ } catch (SmbException _ex) {
+ throw new NestedIOException("Failed to write resource [" + _path + "].", _ex);
+ }
+ logger.info("Successfully wrote remote file [" + _path + "].");
+ }
+
+ /**
+ * Convenience method to write a local file object to a remote location.
+ * @see org.springframework.integration.smb.session.SmbSession.write(InputStream, String)
+ */
+ public SmbFile write(File _file, String _path) throws IOException {
+ return writeAndClose(new FileInputStream(_file), _path);
+ }
+
+ /**
+ * Convenience method to write a byte array to a remote location.
+ * @see org.springframework.integration.smb.session.SmbSession.write(InputStream, String)
+ */
+ public SmbFile write(byte[] _contents, String _path) throws IOException {
+ return writeAndClose(new ByteArrayInputStream(_contents), _path);
+ }
+
+ /**
+ * Creates the specified remote path if not yet exists.
+ * If the specified resource is a file rather than a path, creates all directories leading
+ * to that file.
+ * @param _path remote path to create
+ * @return always true (error states are express by exceptions)
+ * @throws IOException on error conditions returned by a CIFS server
+ * @see org.springframework.integration.file.remote.session.Session#mkdir(java.lang.String)
+ */
+ public boolean mkdir(String _path) throws IOException {
+ try {
+ SmbFile dir = createSmbDirectoryObject(_path);
+ if (!dir.exists()) {
+ dir.mkdirs();
+ logger.info("Successfully created remote directory [" + _path + "] in share [" + smbShare + "].");
+ } else {
+ logger.info("Remote directory [" + _path + "] exists in share [" + smbShare + "].");
+ }
+ return true;
+ } catch (SmbException _ex) {
+ throw new NestedIOException("Failed to create directory [" + _path + "].", _ex);
+ }
+ }
+
+ /**
+ * Checks whether the remote resource exists.
+ * @param _path remote path
+ * @return true if exists, false otherwise
+ * @throws IOException on error conditions returned by a CIFS server
+ * @see org.springframework.integration.file.remote.session.Session#exists(java.lang.String)
+ */
+ public boolean exists(String _path) throws IOException {
+ return createSmbFileObject(_path).exists();
+ }
+
+ /**
+ * Checks whether the remote resource is a file.
+ * @param _path remote path
+ * @return true if resource is a file, false otherwise
+ * @throws IOException on error conditions returned by a CIFS server
+ */
+ public boolean isFile(String _path) throws IOException {
+ SmbFile resource = createSmbFileObject(_path);
+ return resource.exists() && resource.isFile();
+ }
+
+ /**
+ * Checks whether the remote resource is a directory.
+ * @param _path remote path
+ * @return true if resource is a directory, false otherwise
+ * @throws IOException on error conditions returned by a CIFS server
+ */
+ public boolean isDirectory(String _path) throws IOException {
+ SmbFile resource = createSmbFileObject(_path);
+ return resource.exists() && resource.isDirectory();
+ }
+
+ /**
+ * Create all directories in the given remote path reference.
+ * @param _path path remote path, may be a file in which case the file name is ignored
+ * @return the path created or null
+ * @throws IOException on error conditions returned by a CIFS server
+ */
+ String mkdirs(String _path) throws IOException {
+ int idxPath = _path.lastIndexOf(FILE_SEPARATOR);
+ if (idxPath > -1) {
+ String path = _path.substring(0, idxPath + 1);
+ mkdir(path);
+ return path;
+ }
+ return null;
+ }
+
+ /**
+ * Renames a remote resource.
+ * @param _pathFrom remote source path
+ * @param _pathTo remote target path
+ * @throws IOException on error conditions returned by a CIFS server
+ * @see org.springframework.integration.file.remote.session.Session#rename(java.lang.String, java.lang.String)
+ */
+ public void rename(String _pathFrom, String _pathTo) throws IOException {
+ try {
+
+ SmbFile smbFileFrom = createSmbFileObject(_pathFrom);
+ SmbFile smbFileTo = createSmbFileObject(_pathTo);
+ if (smbShare.isReplaceFile() && smbFileTo.exists()) {
+ smbFileTo.delete();
+ }
+ smbFileFrom.renameTo(smbFileTo);
+
+ } catch (SmbException _ex) {
+ throw new NestedIOException("Failed to rename [" + _pathFrom + "] to [" + _pathTo + "].", _ex);
+ }
+ logger.info("Successfully renamed remote resource [" + _pathFrom + "] to [" + _pathTo + "].");
+
+ }
+
+ /**
+ * Closes this SMB session.
+ * @see org.springframework.integration.file.remote.session.Session#close()
+ */
+ public void close() {
+ smbShare.doClose();
+ }
+
+ /**
+ * Checks with this SMB session is open and ready for work by attempting
+ * to list remote files and checking for error conditions..
+ * @return true if the session is open, false otherwise
+ * @see org.springframework.integration.file.remote.session.Session#isOpen()
+ */
+ public boolean isOpen() {
+ if (!smbShare.isOpened()) {
+ return false;
+ }
+ try {
+ smbShare.listFiles();
+ } catch (Exception _ex) {
+ close();
+ }
+ return smbShare.isOpened();
+ }
+
+ /**
+ * Convenience method to write the specified input stream to a remote path and return
+ * the path as an SMB file object.
+ * @param _inputStream input stream
+ * @param _path remote path (of a file) to write to
+ * @return SMB file object
+ * @throws IOException on error conditions returned by a CIFS server
+ */
+ SmbFile writeAndClose(InputStream _inputStream, String _path) throws IOException {
+ write(_inputStream, _path);
+ _inputStream.close();
+ return createSmbFileObject(_path);
+ }
+
+ /**
+ * Factory method for new SmbFile objects under this session's share for the specified path.
+ * @param _path remote path
+ * @param _isDirectory Boolean object to indicate the path is a directory, may be null
+ * @return SmbFile object for path
+ * @throws IOException in case of I/O errors
+ */
+ private SmbFile createSmbFileObject(String _path, Boolean _isDirectory) throws IOException {
+ String path = StringUtils.cleanPath(_path);
+ if (!StringUtils.hasText(path)) {
+ return smbShare;
+ }
+
+ SmbFile smbFile = new SmbFile(smbShare, path);
+
+ boolean appendFileSeparator = !path.endsWith(FILE_SEPARATOR);
+ if (appendFileSeparator) {
+ try {
+ appendFileSeparator = smbFile.isDirectory() || (_isDirectory != null && _isDirectory);
+ } catch (Exception _ex) {
+ appendFileSeparator = false;
+ }
+ }
+ if (appendFileSeparator) {
+ smbFile = createSmbFileObject(path + FILE_SEPARATOR);
+ }
+ if (logger.isDebugEnabled()) {
+ logger.debug("Created new " + SmbFile.class.getName() + "[" + smbFile + "] for path [" + path + "].");
+ }
+ return smbFile;
+ }
+
+ /**
+ * Creates an SMB file object pointing to a remote file.
+ */
+ public SmbFile createSmbFileObject(String _path) throws IOException {
+ return createSmbFileObject(_path, null);
+ }
+
+ /**
+ * Creates an SMB file object pointing to a remote directory.
+ */
+ public SmbFile createSmbDirectoryObject(String _path) throws IOException {
+ return createSmbFileObject(_path, true);
+ }
+
+ /**
+ * Static configuration of the JCIFS library.
+ * The log level of this class is mapped to a suitable jcifs.util.loglevel
+ */
+ 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");
+ }
+ }
+ }
+
+}
diff --git a/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbSessionFactory.java b/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbSessionFactory.java
new file mode 100644
index 0000000..cf89a49
--- /dev/null
+++ b/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbSessionFactory.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright 2002-2012 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
+ *
+ * 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.springframework.integration.smb.session;
+
+import java.io.IOException;
+
+import jcifs.smb.SmbFile;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.integration.file.remote.session.SessionFactory;
+
+/**
+ * The SMB session factory.
+ *
+ * @author Markus Spann
+ * @since 2.1.1
+ */
+public class SmbSessionFactory extends SmbConfig implements SessionFactory {
+
+ private final Log logger = LogFactory.getLog(this.getClass());
+
+ public SmbSessionFactory() {
+ logger.debug("New " + getClass().getName() + " created.");
+ }
+
+ public final SmbSession getSession() {
+ try {
+ return createSession();
+ } catch (Exception _ex) {
+ throw new IllegalStateException("Failed to create session.", _ex);
+ }
+ }
+
+ protected SmbSession createSession() throws IOException {
+ SmbShare smbShare = new SmbShare((SmbConfig) this);
+ smbShare.setReplaceFile(this.isReplaceFile());
+ smbShare.setUseTempFile(this.isUseTempFile());
+
+ logger.info(String.format("SMB share init: %s/%s", getHostPort(), getShareAndDir()));
+
+ smbShare.init();
+ logger.debug("SMB share initialized.");
+
+ return new SmbSession(smbShare);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString();
+ }
+
+}
diff --git a/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbShare.java b/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbShare.java
new file mode 100644
index 0000000..4021901
--- /dev/null
+++ b/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbShare.java
@@ -0,0 +1,104 @@
+/**
+ * Copyright 2002-2012 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
+ *
+ * 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.springframework.integration.smb.session;
+
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import jcifs.smb.SmbException;
+import jcifs.smb.SmbFile;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.core.NestedIOException;
+import org.springframework.util.Assert;
+
+public class SmbShare extends SmbFile {
+
+ private final Log logger = LogFactory.getLog(SmbShare.class);
+
+ private final AtomicBoolean open = new AtomicBoolean(false);
+
+ private final AtomicBoolean replaceFile = new AtomicBoolean(false);
+
+ private final AtomicBoolean useTempFile = new AtomicBoolean(false);
+
+ public SmbShare(String _url) throws IOException {
+ super(_url);
+ }
+
+ public SmbShare(SmbConfig _smbConfig) throws IOException {
+ this(_smbConfig.validate().getUrl());
+ }
+
+ public void init() throws NestedIOException {
+ boolean canRead = false;
+ try {
+ if (!exists()) {
+ logger.info("SMB root directory does not exist. Creating it.");
+ mkdirs();
+ }
+ canRead = canRead();
+ } catch (SmbException _ex) {
+ throw new NestedIOException("Unable to initialize share: " + this, _ex);
+ }
+ Assert.isTrue(canRead, "Share is not accessible " + this);
+ open.set(true);
+ }
+
+ public boolean isReplaceFile() {
+ return replaceFile.get();
+ }
+
+ public void setReplaceFile(boolean _replace) {
+ this.replaceFile.set(_replace);
+ }
+
+ public boolean isUseTempFile() {
+ return useTempFile.get();
+ }
+
+ public void setUseTempFile(boolean _useTempFile) {
+ this.useTempFile.set(_useTempFile);
+ }
+
+ /**
+ * Checks whether the share is accessible.
+ * Note: jcifs.smb.SmbFile defines a package-protected method isOpen().
+ * @return true if open
+ */
+ boolean isOpened() {
+ return open.get();
+ }
+
+ /**
+ * Set the open state to closed.
+ * Note: jcifs.smb.SmbFile defines a package-protected method close().
+ */
+ void doClose() {
+ open.set(false);
+ }
+
+ public String newTempFileSuffix() {
+ return "-" + Long.toHexString(Double.doubleToLongBits(Math.random())) + ".tmp";
+ }
+
+ @Override
+ public String toString() {
+ return super.toString();
+ }
+
+}
diff --git a/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbUtils.java b/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbUtils.java
new file mode 100644
index 0000000..cb94da6
--- /dev/null
+++ b/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbUtils.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright 2002-2012 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
+ *
+ * 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.springframework.integration.smb.session;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+
+public abstract class SmbUtils {
+
+ private SmbUtils() {
+ }
+
+ /**
+ * Read the specified file into a byte array.
+ * @param _file file
+ * @return byte array of file contents
+ * @throws IOException
+ */
+ public static byte[] readFile(File _file) throws IOException {
+ FileInputStream stream = new FileInputStream(_file);
+ try {
+ FileChannel fc = stream.getChannel();
+ MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
+ return bb.array();
+
+ /* Instead of using default, pass in a decoder. */
+ // return Charset.defaultCharset().decode(bb).toString();
+
+ } finally {
+ stream.close();
+ }
+ }
+
+}
diff --git a/spring-integration-smb/src/main/resources/META-INF/spring.handlers b/spring-integration-smb/src/main/resources/META-INF/spring.handlers
new file mode 100644
index 0000000..3ccaf0c
--- /dev/null
+++ b/spring-integration-smb/src/main/resources/META-INF/spring.handlers
@@ -0,0 +1 @@
+http\://www.springframework.org/schema/integration/smb=org.springframework.integration.smb.config.SmbNamespaceHandler
diff --git a/spring-integration-smb/src/main/resources/META-INF/spring.schemas b/spring-integration-smb/src/main/resources/META-INF/spring.schemas
new file mode 100644
index 0000000..1f406d7
--- /dev/null
+++ b/spring-integration-smb/src/main/resources/META-INF/spring.schemas
@@ -0,0 +1,2 @@
+http\://www.springframework.org/schema/integration/smb/spring-integration-smb-2.0.xsd=org/springframework/integration/smb/config/spring-integration-smb-2.0.xsd
+http\://www.springframework.org/schema/integration/smb/spring-integration-smb.xsd=org/springframework/integration/smb/config/spring-integration-smb-2.0.xsd
diff --git a/spring-integration-smb/src/main/resources/META-INF/spring.tooling b/spring-integration-smb/src/main/resources/META-INF/spring.tooling
new file mode 100644
index 0000000..b448ab4
--- /dev/null
+++ b/spring-integration-smb/src/main/resources/META-INF/spring.tooling
@@ -0,0 +1,4 @@
+# Tooling related information for the integration smb namespace
+http\://www.springframework.org/schema/integration/smb@name=integration smb Namespace
+http\://www.springframework.org/schema/integration/smb@prefix=int-smb
+http\://www.springframework.org/schema/integration/smb@icon=org/springframework/integration/smb/config/spring-integration-smb.gif
diff --git a/spring-integration-smb/src/main/resources/license_header.txt b/spring-integration-smb/src/main/resources/license_header.txt
new file mode 100644
index 0000000..49a6b55
--- /dev/null
+++ b/spring-integration-smb/src/main/resources/license_header.txt
@@ -0,0 +1,13 @@
+Copyright 2002-2012 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
+
+ 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.
diff --git a/spring-integration-smb/src/main/resources/org/springframework/integration/smb/config/spring-integration-smb-2.0.xsd b/spring-integration-smb/src/main/resources/org/springframework/integration/smb/config/spring-integration-smb-2.0.xsd
new file mode 100644
index 0000000..627fcc0
--- /dev/null
+++ b/spring-integration-smb/src/main/resources/org/springframework/integration/smb/config/spring-integration-smb-2.0.xsd
@@ -0,0 +1,244 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Identifies directory path (e.g., "/temp/mytransfers/") where file will be transferred to
+
+
+
+
+
+
+ Allows you to provide SpEL expression which will compute directory path
+ where file will be transferred to (e.g., "headers.['remote_dir'] + '/myTransfers'");
+
+
+
+
+
+
+ Allows you to provide remote file/directory separator character. DEFAULT: '/'
+
+
+
+
+
+
+ Extension used when uploading files. We change it right after we know it's uploaded.
+
+
+
+
+
+
+ Allows you to specify a reference to
+ [org.springframework.integration.file.FileNameGenerator] bean.
+
+
+
+
+
+
+
+
+
+
+
+ Allows you to provide SpEL expression which will compute file name of
+ the remote file (e.g., assuming payload is java.io.File "payload.getName() + '.transfered'");
+
+
+
+
+
+
+ Specifies the order for invocation when this endpoint is connected as a
+subscriber to a channel. This is particularly relevant when that channel
+is using a "failover" dispatching strategy, or when a failure in the delivery to one subscriber should signal that
+the message should not be sent to subscribers with a higher 'order' attribute. It has no effect when this
+endpoint itself is a Polling Consumer for a channel with a queue.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Allows you to provide file name pattern to determine the file names that needs to be scanned
+ and is based on simple pattern matching algorithm (e.g., "*.txt, fo*.txt" etc.)
+
+
+
+
+
+
+ Allows you to provide Regular Expression to determine the file names that needs to be scanned.
+ (e.g., "f[o]+\.txt" etc.)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Allows you to specify a reference to
+ [org.springframework.integration.file.filters.FileListFilter] bean.
+
+
+
+
+
+
+ Extension used when downloading files. We change it right after we know it's downloaded.
+
+
+
+
+
+
+ Identifies directory path (e.g., "/temp/mytransfers") where file will be transferred FROM.
+
+
+
+
+
+
+ Allows you to provide remote file/directory separator character. DEFAULT: '/'
+
+
+
+
+
+
+ Identifies directory path (e.g., "/local/mytransfers") where file will be transferred TO.
+
+
+
+
+
+
+ Tells this adapter if local directory must be auto-created if it doesn''t exist. Default is TRUE.
+
+
+
+
+
+
+ Specify whether to delete the remote source file after copying.
+ By default, the remote files will NOT be deleted.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Identifies channel attached to this adapter. Depending on the type of the adapter
+ this channel could be the receiving channel (e.g., outbound-channel-adapter) or channel where
+ messages will be sent to by this adapter (e.g., inbound-channel-adapter).
+
+
+
+
+
+
+ Allows you to specify Charset (e.g., US-ASCII, ISO-8859-1, UTF-8). [UTF-8] is the default.
+
+
+
+
+
+
diff --git a/spring-integration-smb/src/main/resources/org/springframework/integration/smb/config/spring-integration-smb.gif b/spring-integration-smb/src/main/resources/org/springframework/integration/smb/config/spring-integration-smb.gif
new file mode 100644
index 0000000..66a798e
Binary files /dev/null and b/spring-integration-smb/src/main/resources/org/springframework/integration/smb/config/spring-integration-smb.gif differ
diff --git a/spring-integration-smb/src/test/java/org/springframework/integration/smb/AbstractBaseTest.java b/spring-integration-smb/src/test/java/org/springframework/integration/smb/AbstractBaseTest.java
new file mode 100644
index 0000000..aac00a8
--- /dev/null
+++ b/spring-integration-smb/src/test/java/org/springframework/integration/smb/AbstractBaseTest.java
@@ -0,0 +1,283 @@
+/**
+ * Copyright 2002-2012 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
+ *
+ * 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.springframework.integration.smb;
+
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Method;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.util.FileCopyUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * Assorted test utils for the library.
+ * @author Markus Spann
+ */
+public abstract class AbstractBaseTest {
+
+ /** Instance logger. */
+ private final Log logger = LogFactory.getLog(this.getClass());
+
+ protected final Log getLogger() {
+ return logger;
+ }
+
+ // CHECKSTYLE:OFF
+ @Rule // requires JUnit 4.7 or later
+ public final TestName testMethodName = new TestName();
+ // CHECKSTYLE:ON
+
+ private String getTestMethodName() {
+ return getClass().getSimpleName() + '.' + testMethodName.getMethodName() + "()";
+ }
+
+ @Before
+ public final void logTestBegin() {
+ getLogger().info("BGN - Test " + getTestMethodName());
+ }
+
+ @After
+ public final void logTestEnd() {
+ getLogger().info("END - Test " + getTestMethodName());
+ }
+
+ /**
+ * Constructs the Spring application context XML file name from simple class name and suffix '-context.xml'.
+ * @param _suffix optional suffix
+ * @return application context XML file name
+ */
+ protected final String getApplicationContextXmlFile(String _suffix) {
+ String fn = getClass().getSimpleName();
+ if (StringUtils.hasText(_suffix)) {
+ fn += _suffix;
+ }
+ fn += "-context.xml";
+ getLogger().debug("Returning application context xml file [" + fn + "] for class [" + getClass().getName() + "].");
+ return fn;
+ }
+
+ /**
+ * Constructs the Spring application context XML file name from simple class name.
+ * @return application context XML file name
+ */
+ protected final String getApplicationContextXmlFile() {
+ return getApplicationContextXmlFile(null);
+ }
+
+ protected final ClassPathXmlApplicationContext getApplicationContext() {
+ return new ClassPathXmlApplicationContext(getApplicationContextXmlFile(), getClass());
+ }
+
+ /**
+ * Writes the specified input stream to file.
+ * @param _inputStream input stream
+ * @param _path output file path
+ * @throws IOException in case of I/O errors
+ */
+ public static void writeToFile(InputStream _inputStream, String _path) throws IOException {
+ FileOutputStream fos = new FileOutputStream(_path);
+ try {
+ FileCopyUtils.copy(_inputStream, fos);
+ } finally {
+ fos.close();
+ }
+ }
+
+ /**
+ * Writes the specified byte array to the output stream.
+ * @param _bytes byte array
+ * @param _outputStream output stream
+ * @throws IOException in case of I/O errors
+ */
+ public static void writeToFile(byte[] _bytes, OutputStream _outputStream) throws IOException {
+ FileCopyUtils.copy(_bytes, _outputStream);
+ }
+
+ /**
+ * Writes the specified byte array to the an output file.
+ * @param _bytes byte array
+ * @param _fileName output file
+ * @throws IOException in case of I/O errors
+ */
+ public static void writeToFile(byte[] _bytes, String _fileName) throws IOException {
+ FileOutputStream fos = new FileOutputStream(_fileName);
+ writeToFile(_bytes, fos);
+ fos.close();
+ }
+
+ /**
+ * Creates a new file of the given name.
+ * If the file exists, it will be deleted.
+ * The file will be deleted on exit of the JVM.
+ * @param _fileName file name
+ * @return file object
+ */
+ protected File createNewFile(String _fileName) {
+ File file = new File(_fileName);
+ file.deleteOnExit();
+ if (file.exists()) {
+ file.delete();
+ }
+
+ getLogger().debug("File object [" + _fileName + "] created: " + file.getAbsolutePath());
+
+ assertFileNotExists(file);
+ return file;
+ }
+
+ /**
+ * Deletes one or more files or directories.
+ * @param _files file or directories
+ */
+ protected void delete(String... _files) {
+ for (String fileName : _files) {
+ if (fileName == null) {
+ continue;
+ }
+ File file = new File(fileName);
+ if (file.exists()) {
+ getLogger().debug("Deleting file [" + fileName + "].");
+ if (!file.delete()) {
+ file.deleteOnExit();
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks if a directory exists, if not creates it and adds it to the DeleteOnExit hook.
+ * @param _dir directory
+ */
+ protected void ensureExists(String _dir) {
+ File dir = new File(_dir);
+ if (!dir.exists()) {
+ dir.mkdirs();
+ dir.deleteOnExit();
+ }
+ }
+
+ /**
+ * Retrieves class name and method name at the specified stacktrace index.
+ * @param _index stacktrace index
+ * @return fully qualified method name
+ */
+ private static String getStackTraceString(int _index) {
+ StackTraceElement[] arrStackTraceElems = new Throwable().fillInStackTrace().getStackTrace();
+ final int lIndex = Math.min(arrStackTraceElems.length - 1, Math.max(0, _index));
+ return arrStackTraceElems[lIndex].getClassName() + "." + arrStackTraceElems[lIndex].getMethodName();
+ }
+
+ /**
+ * Gets the current method name.
+ * @return method name
+ */
+ public static String getMethodName() {
+ return getStackTraceString(2);
+ }
+
+ /**
+ * Gets the calling method name.
+ * @return method name
+ */
+ public static String getCallingMethodName() {
+ return getStackTraceString(3);
+ }
+
+ /**
+ * Asserts that the specified file exists.
+ * @param _file file object
+ * @return the file object
+ */
+ public static final File assertFileExists(File _file) {
+ return assertFileExists(_file, true);
+ }
+
+ public static final File assertFileNotExists(File _file) {
+ return assertFileExists(_file, false);
+ }
+
+ /**
+ * Asserts that the specified file exists or does not exists.
+ * @param _file file object
+ * @param _exists true if should exist, false otherwise
+ * @return the file object
+ */
+ private static File assertFileExists(File _file, boolean _exists) {
+ assertNotNull("File object is null.", _file);
+ if (_exists) {
+ assertTrue("File [" + _file.getAbsolutePath() + "] does not exist.", _file.exists());
+ } else {
+ assertTrue("File [" + _file.getAbsolutePath() + "] exists.", !_file.exists());
+ }
+ return _file;
+ }
+
+ public static final File assertFileExists(String _file) {
+ return assertFileExists(new File(_file));
+ }
+
+ /**
+ * Invokes one or more test methods on the specified test class.
+ * Catches exceptions during test setup (using reflection) and test invocation.
+ * @param _testClass test class object
+ * @param _methodNames String method names to invoke in order, no parameters expected
+ */
+ protected static void runTests(Class extends AbstractBaseTest> _testClass, String... _methodNames) {
+ AbstractBaseTest test;
+ Method[] methods = new Method[_methodNames.length];
+ String methodName = null;
+
+ try {
+ test = _testClass.newInstance();
+ for (int i = 0; i < _methodNames.length; i++) {
+ methodName = _methodNames[i];
+ methods[i] = _testClass.getMethod(methodName, (Class>[]) null);
+ }
+ } catch (Exception _ex) {
+ System.err.println("Test setup failed for " + _testClass + "." + methodName + "().");
+ _ex.printStackTrace();
+ return;
+ }
+
+ Method method = null;
+ try {
+
+ for (int i = 0; i < methods.length; i++) {
+ method = methods[i];
+ method.invoke(test, (Object[]) null);
+ }
+
+ } catch (Exception _ex) {
+ System.err.println("Test execution failed for " + _testClass + "." + method.getName() + ".");
+ _ex.printStackTrace();
+ }
+
+ }
+
+}
diff --git a/spring-integration-smb/src/test/java/org/springframework/integration/smb/SmbMessageHistoryTests-context.xml b/spring-integration-smb/src/test/java/org/springframework/integration/smb/SmbMessageHistoryTests-context.xml
new file mode 100644
index 0000000..3e4fcb9
--- /dev/null
+++ b/spring-integration-smb/src/test/java/org/springframework/integration/smb/SmbMessageHistoryTests-context.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-integration-smb/src/test/java/org/springframework/integration/smb/SmbMessageHistoryTests.java b/spring-integration-smb/src/test/java/org/springframework/integration/smb/SmbMessageHistoryTests.java
new file mode 100644
index 0000000..1da73c1
--- /dev/null
+++ b/spring-integration-smb/src/test/java/org/springframework/integration/smb/SmbMessageHistoryTests.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2002-2012 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
+ *
+ * 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.springframework.integration.smb;
+
+import static junit.framework.Assert.assertEquals;
+
+import org.junit.Test;
+import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
+
+/**
+ * @author Markus Spann
+ *
+ */
+public class SmbMessageHistoryTests extends AbstractBaseTest {
+ @Test
+ public void testMessageHistory() throws Exception {
+ SourcePollingChannelAdapter adapter = getApplicationContext().getBean("smbInboundChannelAdapter", SourcePollingChannelAdapter.class);
+ assertEquals("smbInboundChannelAdapter", adapter.getComponentName());
+ assertEquals("smb:inbound-channel-adapter", adapter.getComponentType());
+ }
+}
diff --git a/spring-integration-smb/src/test/java/org/springframework/integration/smb/SmbParserInboundTests-context.xml b/spring-integration-smb/src/test/java/org/springframework/integration/smb/SmbParserInboundTests-context.xml
new file mode 100644
index 0000000..bab68cd
--- /dev/null
+++ b/spring-integration-smb/src/test/java/org/springframework/integration/smb/SmbParserInboundTests-context.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-integration-smb/src/test/java/org/springframework/integration/smb/SmbParserInboundTests-fail-context.xml b/spring-integration-smb/src/test/java/org/springframework/integration/smb/SmbParserInboundTests-fail-context.xml
new file mode 100644
index 0000000..47ed218
--- /dev/null
+++ b/spring-integration-smb/src/test/java/org/springframework/integration/smb/SmbParserInboundTests-fail-context.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-integration-smb/src/test/java/org/springframework/integration/smb/SmbParserInboundTests.java b/spring-integration-smb/src/test/java/org/springframework/integration/smb/SmbParserInboundTests.java
new file mode 100644
index 0000000..04181c7
--- /dev/null
+++ b/spring-integration-smb/src/test/java/org/springframework/integration/smb/SmbParserInboundTests.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright 2002-2012 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
+ *
+ * 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.springframework.integration.smb;
+
+import java.io.File;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+/**
+ * @author Markus Spann
+ *
+ */
+public class SmbParserInboundTests extends AbstractBaseTest {
+
+ @Before
+ public void prepare() {
+ ensureExists("test-temp/remote-10");
+ cleanUp();
+ }
+
+ @Test
+ public void testLocalFilesAutoCreationTrue() throws Exception {
+ assertFileNotExists(new File("test-temp/local-10"));
+ new ClassPathXmlApplicationContext(getApplicationContextXmlFile(), this.getClass());
+ assertFileExists(new File("test-temp/local-10"));
+ assertFileNotExists(new File("test-temp/local-6"));
+ }
+
+ @Test(expected = BeanCreationException.class)
+ public void testLocalFilesAutoCreationFalse() throws Exception {
+ assertFileNotExists(new File("test-temp/local-6"));
+ new ClassPathXmlApplicationContext(getApplicationContextXmlFile("-fail"), this.getClass());
+ }
+
+ @After
+ public void cleanUp() {
+ delete("test-temp/local-10", "test-temp/local-6");
+ }
+
+ public static void main(String[] _args) {
+ new SmbParserInboundTests().cleanUp();
+ runTests(SmbParserInboundTests.class, "testLocalFilesAutoCreationTrue", "testLocalFilesAutoCreationFalse");
+ }
+
+}
diff --git a/spring-integration-smb/src/test/java/org/springframework/integration/smb/config/SmbInboundChannelAdapterParserTests-context.xml b/spring-integration-smb/src/test/java/org/springframework/integration/smb/config/SmbInboundChannelAdapterParserTests-context.xml
new file mode 100644
index 0000000..ad996a5
--- /dev/null
+++ b/spring-integration-smb/src/test/java/org/springframework/integration/smb/config/SmbInboundChannelAdapterParserTests-context.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-integration-smb/src/test/java/org/springframework/integration/smb/config/SmbInboundChannelAdapterParserTests.java b/spring-integration-smb/src/test/java/org/springframework/integration/smb/config/SmbInboundChannelAdapterParserTests.java
new file mode 100644
index 0000000..1d603eb
--- /dev/null
+++ b/spring-integration-smb/src/test/java/org/springframework/integration/smb/config/SmbInboundChannelAdapterParserTests.java
@@ -0,0 +1,120 @@
+/**
+ * Copyright 2002-2012 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
+ *
+ * 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.springframework.integration.smb.config;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.util.Comparator;
+import java.util.Map;
+
+import org.junit.Test;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
+import org.springframework.integration.file.remote.session.CachingSessionFactory;
+import org.springframework.integration.smb.AbstractBaseTest;
+import org.springframework.integration.smb.filters.SmbSimplePatternFileListFilter;
+import org.springframework.integration.smb.inbound.SmbInboundFileSynchronizer;
+import org.springframework.integration.smb.inbound.SmbInboundFileSynchronizingMessageSource;
+import org.springframework.integration.smb.session.SmbSession;
+import org.springframework.integration.smb.session.SmbSessionFactory;
+import org.springframework.integration.test.util.TestUtils;
+
+/**
+ * @author Markus Spann
+ */
+public class SmbInboundChannelAdapterParserTests extends AbstractBaseTest {
+
+ @SuppressWarnings("unchecked")
+ @Test(timeout = 10000)
+ public void testSmbInboundChannelAdapterComplete() throws Exception{
+ ApplicationContext ac =
+ new ClassPathXmlApplicationContext(getApplicationContextXmlFile(), this.getClass());
+ SourcePollingChannelAdapter adapter = ac.getBean("smbInbound", SourcePollingChannelAdapter.class);
+ Comparator comparator = TestUtils.getPropertyValue(adapter, "source.fileSource.toBeReceived.q.comparator", Comparator.class);
+ assertNotNull(comparator);
+ assertEquals("smbInbound", adapter.getComponentName());
+ assertEquals("smb:inbound-channel-adapter", adapter.getComponentType());
+ assertNotNull(TestUtils.getPropertyValue(adapter, "poller"));
+ assertEquals(ac.getBean("smbChannel"), TestUtils.getPropertyValue(adapter, "outputChannel"));
+ SmbInboundFileSynchronizingMessageSource inbound =
+ (SmbInboundFileSynchronizingMessageSource) TestUtils.getPropertyValue(adapter, "source");
+
+ SmbInboundFileSynchronizer fisync =
+ (SmbInboundFileSynchronizer) TestUtils.getPropertyValue(inbound, "synchronizer");
+ assertEquals(".working.tmp", TestUtils.getPropertyValue(fisync, "temporaryFileSuffix", String.class));
+ String remoteFileSeparator = (String) TestUtils.getPropertyValue(fisync, "remoteFileSeparator");
+ assertNotNull(remoteFileSeparator);
+ assertEquals("", remoteFileSeparator);
+ SmbSimplePatternFileListFilter filter = (SmbSimplePatternFileListFilter) TestUtils.getPropertyValue(fisync, "filter");
+ assertNotNull(filter);
+ Object sessionFactory = TestUtils.getPropertyValue(fisync, "sessionFactory");
+ assertTrue(SmbSessionFactory.class.isAssignableFrom(sessionFactory.getClass()));
+ }
+
+ @Test(timeout = 10000)
+ public void cachingSessionFactoryByDefault() throws Exception{
+ ApplicationContext ac = new ClassPathXmlApplicationContext(getApplicationContextXmlFile(), this.getClass());
+ SourcePollingChannelAdapter adapter = ac.getBean("simpleAdapter", SourcePollingChannelAdapter.class);
+ Object sessionFactory = TestUtils.getPropertyValue(adapter, "source.synchronizer.sessionFactory");
+ assertEquals(CachingSessionFactory.class, sessionFactory.getClass());
+ SmbInboundFileSynchronizer fisync =
+ TestUtils.getPropertyValue(adapter, "source.synchronizer", SmbInboundFileSynchronizer.class);
+ String remoteFileSeparator = (String) TestUtils.getPropertyValue(fisync, "remoteFileSeparator");
+ assertNotNull(remoteFileSeparator);
+ assertEquals("/", remoteFileSeparator);
+ }
+
+ @Test(timeout = 10000)
+ public void testSmbInboundChannelAdapterCompleteNoId() throws Exception{
+ ApplicationContext ac =
+ new ClassPathXmlApplicationContext(getApplicationContextXmlFile(), this.getClass());
+ Map spcas = ac.getBeansOfType(SourcePollingChannelAdapter.class);
+ SourcePollingChannelAdapter adapter = null;
+ for (String key : spcas.keySet()) {
+ if (!key.equals("smbInbound") && !key.equals("simpleAdapter")){
+ adapter = spcas.get(key);
+ }
+ }
+ assertNotNull(adapter);
+ }
+
+
+ public static class TestSessionFactoryBean implements FactoryBean {
+
+ public SmbSessionFactory getObject() throws Exception {
+ SmbSessionFactory smbFactory = mock(SmbSessionFactory.class);
+ SmbSession session = mock(SmbSession.class);
+ when(smbFactory.getSession()).thenReturn(session);
+ return smbFactory;
+ }
+
+ public Class> getObjectType() {
+ return SmbSessionFactory.class;
+ }
+
+ public boolean isSingleton() {
+ return true;
+ }
+ }
+
+}
diff --git a/spring-integration-smb/src/test/java/org/springframework/integration/smb/config/SmbInboundChannelAdapterSample-context.xml b/spring-integration-smb/src/test/java/org/springframework/integration/smb/config/SmbInboundChannelAdapterSample-context.xml
new file mode 100644
index 0000000..e9efed5
--- /dev/null
+++ b/spring-integration-smb/src/test/java/org/springframework/integration/smb/config/SmbInboundChannelAdapterSample-context.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-integration-smb/src/test/java/org/springframework/integration/smb/config/SmbInboundOutboundSample.java b/spring-integration-smb/src/test/java/org/springframework/integration/smb/config/SmbInboundOutboundSample.java
new file mode 100644
index 0000000..ae5e88f
--- /dev/null
+++ b/spring-integration-smb/src/test/java/org/springframework/integration/smb/config/SmbInboundOutboundSample.java
@@ -0,0 +1,137 @@
+/**
+ * Copyright 2002-2012 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
+ *
+ * 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.springframework.integration.smb.config;
+
+import static junit.framework.Assert.assertTrue;
+
+import java.io.File;
+
+import org.junit.Test;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.integration.MessageChannel;
+import org.springframework.integration.endpoint.EventDrivenConsumer;
+import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
+import org.springframework.integration.file.remote.handler.FileTransferringMessageHandler;
+import org.springframework.integration.message.GenericMessage;
+import org.springframework.integration.smb.AbstractBaseTest;
+import org.springframework.integration.smb.inbound.SmbInboundFileSynchronizingMessageSource;
+import org.springframework.integration.smb.session.SmbSession;
+import org.springframework.integration.smb.session.SmbSessionFactory;
+import org.springframework.integration.test.util.TestUtils;
+
+/**
+ * System tests that perform SMB access without any mocking.
+ * These tests are annotated with '@Ignore', as they requires real SMB share configured
+ * in the application context/smbClientFactory in order to succeed.
+ * The test cases create directories and files autonomously and perform clean-up
+ * on a best effort basis.
+ *
+ * @author Markus Spann
+ */
+public class SmbInboundOutboundSample extends AbstractBaseTest {
+
+ private static final String INBOUND_APPLICATION_CONTEXT_XML = "SmbInboundChannelAdapterSample-context.xml";
+ private static final String OUTBOUND_APPLICATION_CONTEXT_XML = "SmbOutboundChannelAdapterSample-context.xml";
+
+ @org.junit.Ignore("Actual SMB share must be configured in file [" + INBOUND_APPLICATION_CONTEXT_XML + "].")
+ @Test
+ public void testSmbInboundChannelAdapter() throws Exception {
+ String testLocalDir = "test-temp/local-4/";
+ String testRemoteDir = "test-temp/remote-4/";
+
+ ApplicationContext ac = new ClassPathXmlApplicationContext(INBOUND_APPLICATION_CONTEXT_XML, this.getClass());
+
+ Object consumer = ac.getBean("smbInboundChannelAdapter");
+ assertTrue(consumer instanceof SourcePollingChannelAdapter);
+ Object messageSource = TestUtils.getPropertyValue(consumer, "source");
+ assertTrue(messageSource instanceof SmbInboundFileSynchronizingMessageSource);
+
+ // retrieve the session factory bean to place a couple of test files remotely using a new session
+ SmbSessionFactory smbSessionFactory = ac.getBean("smbSessionFactory", SmbSessionFactory.class);
+ smbSessionFactory.setReplaceFile(true);
+ SmbSession smbSession = smbSessionFactory.getSession();
+
+ // place text files onto the share
+ smbSession.mkdir(testRemoteDir);
+
+ String[] fileNames = createTestFileNames(5);
+ for (int i = 0; i < fileNames.length; i++) {
+ smbSession.write(("File [" + fileNames[i] + "] written by test case [" + getMethodName() + "].").getBytes(), testRemoteDir + fileNames[i]);
+ }
+
+ // allow time for the files to arrive locally
+ Thread.sleep(5000);
+
+ // confirm the local presence of all test files
+ for (int i = 0; i < fileNames.length; i++) {
+ assertFileExists(testLocalDir + fileNames[i]).deleteOnExit();
+ }
+
+ }
+
+ @org.junit.Ignore("Actual SMB share must be configured in file [" + OUTBOUND_APPLICATION_CONTEXT_XML + "].")
+ @Test
+ public void testSmbOutboundChannelAdapter() throws Exception {
+ String testRemoteDir = "test-temp/remote-8/";
+ String testLocalDir = "test-temp/local-8/";
+ new File(testLocalDir).mkdirs();
+
+ String[] fileNames = createTestFileNames(5);
+ for (int i = 0; i < fileNames.length; i++) {
+ writeToFile(("File [" + fileNames[i] + "] written by test case [" + getMethodName() + "].").getBytes(), testLocalDir + fileNames[i]);
+ }
+
+ ApplicationContext ac = new ClassPathXmlApplicationContext(OUTBOUND_APPLICATION_CONTEXT_XML, this.getClass());
+
+ Object consumer = ac.getBean("smbOutboundChannelAdapter");
+ assertTrue(consumer instanceof EventDrivenConsumer);
+ Object messageSource = TestUtils.getPropertyValue(consumer, "handler");
+ assertTrue(messageSource instanceof FileTransferringMessageHandler);
+
+ MessageChannel smbChannel = ac.getBean("smbOutboundChannel", MessageChannel.class);
+
+ for (int i = 0; i < fileNames.length; i++) {
+ smbChannel.send(new GenericMessage(new File(testLocalDir + fileNames[i])));
+ }
+
+ Thread.sleep(3000);
+
+ // retrieve the session factory bean to check the test files are present in the remote location
+ SmbSessionFactory smbSessionFactory = ac.getBean("smbSessionFactory", SmbSessionFactory.class);
+ smbSessionFactory.setReplaceFile(false);
+ SmbSession smbSession = smbSessionFactory.getSession();
+
+ for (int i = 0; i < fileNames.length; i++) {
+ String remoteFile = testRemoteDir + fileNames[i];
+ assertTrue("Remote file [" + remoteFile + "] does not exist.", smbSession.exists(remoteFile));
+ }
+
+ }
+
+ private String[] createTestFileNames(int _nbTestFiles) {
+ String[] fileNames = new String[_nbTestFiles];
+ for (int i = 0; i < fileNames.length; i++) {
+ fileNames[i] = "test-file-" + i + ".txt";
+ }
+ return fileNames;
+ }
+
+ public static void main(String[] _args) {
+ runTests(SmbInboundOutboundSample.class, "testSmbOutboundChannelAdapter", "testSmbInboundChannelAdapter");
+ }
+
+}
diff --git a/spring-integration-smb/src/test/java/org/springframework/integration/smb/config/SmbOutboundChannelAdapterParserTests-context.xml b/spring-integration-smb/src/test/java/org/springframework/integration/smb/config/SmbOutboundChannelAdapterParserTests-context.xml
new file mode 100644
index 0000000..fdea976
--- /dev/null
+++ b/spring-integration-smb/src/test/java/org/springframework/integration/smb/config/SmbOutboundChannelAdapterParserTests-context.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-integration-smb/src/test/java/org/springframework/integration/smb/config/SmbOutboundChannelAdapterParserTests.java b/spring-integration-smb/src/test/java/org/springframework/integration/smb/config/SmbOutboundChannelAdapterParserTests.java
new file mode 100644
index 0000000..99b0204
--- /dev/null
+++ b/spring-integration-smb/src/test/java/org/springframework/integration/smb/config/SmbOutboundChannelAdapterParserTests.java
@@ -0,0 +1,89 @@
+/**
+ * Copyright 2002-2012 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
+ *
+ * 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.springframework.integration.smb.config;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+import static org.junit.Assert.assertSame;
+
+import java.util.Iterator;
+import java.util.Set;
+
+import org.junit.Test;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.integration.channel.PublishSubscribeChannel;
+import org.springframework.integration.core.MessageHandler;
+import org.springframework.integration.endpoint.EventDrivenConsumer;
+import org.springframework.integration.file.remote.session.CachingSessionFactory;
+import org.springframework.integration.smb.AbstractBaseTest;
+import org.springframework.integration.smb.session.SmbSessionFactory;
+import org.springframework.integration.test.util.TestUtils;
+
+/**
+ * @author Markus Spann
+ * @since 2.1.1
+ */
+public class SmbOutboundChannelAdapterParserTests extends AbstractBaseTest {
+
+ @Test
+ public void testSmbOutboundChannelAdapterComplete() throws Exception {
+ ApplicationContext ac = getApplicationContext();
+
+ Object consumer = ac.getBean("smbOutboundChannelAdapter");
+ assertTrue(consumer instanceof EventDrivenConsumer);
+
+ PublishSubscribeChannel channel = ac.getBean("smbPubSubChannel", PublishSubscribeChannel.class);
+ assertEquals(channel, TestUtils.getPropertyValue(consumer, "inputChannel"));
+ assertEquals("smbOutboundChannelAdapter", ((EventDrivenConsumer) consumer).getComponentName());
+
+ Object messageHandler = TestUtils.getPropertyValue(consumer, "handler");
+ String remoteFileSeparator = (String) TestUtils.getPropertyValue(messageHandler, "remoteFileSeparator");
+ assertNotNull(remoteFileSeparator);
+ assertEquals(".working.tmp", TestUtils.getPropertyValue(messageHandler, "temporaryFileSuffix", String.class));
+ assertEquals(".", remoteFileSeparator);
+ assertEquals(ac.getBean("fileNameGenerator"), TestUtils.getPropertyValue(messageHandler, "fileNameGenerator"));
+ assertEquals("UTF-8", TestUtils.getPropertyValue(messageHandler, "charset"));
+ assertNotNull(TestUtils.getPropertyValue(messageHandler, "temporaryDirectory"));
+
+ Object sessionFactoryProp = TestUtils.getPropertyValue(messageHandler, "sessionFactory");
+ assertEquals(SmbSessionFactory.class, sessionFactoryProp.getClass());
+
+ SmbSessionFactory smbSessionFactory = (SmbSessionFactory) sessionFactoryProp;
+ assertEquals("localhost", TestUtils.getPropertyValue(smbSessionFactory, "host"));
+ assertEquals(0, TestUtils.getPropertyValue(smbSessionFactory, "port"));
+ assertEquals(23, TestUtils.getPropertyValue(messageHandler, "order"));
+
+ // verify subscription order
+ @SuppressWarnings("unchecked")
+ Set handlers = (Set) TestUtils.getPropertyValue(TestUtils.getPropertyValue(channel, "dispatcher"), "handlers");
+ Iterator iterator = handlers.iterator();
+ assertSame(TestUtils.getPropertyValue(ac.getBean("smbOutboundChannelAdapter2"), "handler"), iterator.next());
+ assertSame(messageHandler, iterator.next());
+ }
+
+ @Test
+ public void cachingByDefault() {
+ ApplicationContext ac = new ClassPathXmlApplicationContext(getApplicationContextXmlFile(), this.getClass());
+ Object adapter = ac.getBean("simpleAdapter");
+ Object sfProperty = TestUtils.getPropertyValue(adapter, "handler.sessionFactory");
+ assertEquals(CachingSessionFactory.class, sfProperty.getClass());
+ Object innerSfProperty = TestUtils.getPropertyValue(sfProperty, "sessionFactory");
+ assertEquals(SmbSessionFactory.class, innerSfProperty.getClass());
+ }
+
+}
diff --git a/spring-integration-smb/src/test/java/org/springframework/integration/smb/config/SmbOutboundChannelAdapterSample-context.xml b/spring-integration-smb/src/test/java/org/springframework/integration/smb/config/SmbOutboundChannelAdapterSample-context.xml
new file mode 100644
index 0000000..b0bff06
--- /dev/null
+++ b/spring-integration-smb/src/test/java/org/springframework/integration/smb/config/SmbOutboundChannelAdapterSample-context.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
diff --git a/spring-integration-smb/src/test/java/org/springframework/integration/smb/inbound/SmbInboundRemoteFileSystemSynchronizerTest.java b/spring-integration-smb/src/test/java/org/springframework/integration/smb/inbound/SmbInboundRemoteFileSystemSynchronizerTest.java
new file mode 100644
index 0000000..436dd48
--- /dev/null
+++ b/spring-integration-smb/src/test/java/org/springframework/integration/smb/inbound/SmbInboundRemoteFileSystemSynchronizerTest.java
@@ -0,0 +1,134 @@
+/**
+ * Copyright 2002-2012 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
+ *
+ * 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.springframework.integration.smb.inbound;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import jcifs.smb.SmbFile;
+
+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.integration.Message;
+import org.springframework.integration.smb.AbstractBaseTest;
+import org.springframework.integration.smb.filters.SmbRegexPatternFileListFilter;
+import org.springframework.integration.smb.session.SmbSession;
+import org.springframework.integration.smb.session.SmbSessionFactory;
+
+/**
+ * @author Markus Spann
+ * @since 2.1.1
+ */
+public class SmbInboundRemoteFileSystemSynchronizerTest extends AbstractBaseTest {
+
+ private SmbSession smbSession;
+ private SmbSessionFactory smbSessionFactory;
+ private String testLocalDir = "test-temp/local-9/";
+ private String testRemoteDir = "test-temp/remote-9/";
+
+ @Before
+ public void prepare() {
+ delete(testLocalDir);
+ ensureExists(testRemoteDir);
+
+ smbSession = mock(SmbSession.class);
+ smbSessionFactory = new TestSmbSessionFactory();
+ smbSessionFactory.setHost("localhost");
+ smbSessionFactory.setPort(0);
+ smbSessionFactory.setDomain("");
+ smbSessionFactory.setUsername("sambaguest");
+ smbSessionFactory.setPassword("sambaguest");
+ smbSessionFactory.setShareAndDir("smb-share/");
+ smbSessionFactory.setReplaceFile(true);
+ }
+
+ @Test
+ public void testCopyFileToLocalDir() throws Exception {
+ File localDirectoy = new File(testLocalDir);
+ assertFileNotExists(localDirectoy);
+
+ SmbInboundFileSynchronizer synchronizer = spy(new SmbInboundFileSynchronizer(smbSessionFactory));
+ synchronizer.setDeleteRemoteFiles(true);
+ synchronizer.setRemoteDirectory(testRemoteDir);
+ synchronizer.setFilter(new SmbRegexPatternFileListFilter(".*\\.test$"));
+
+ SmbInboundFileSynchronizingMessageSource messageSource = new SmbInboundFileSynchronizingMessageSource(synchronizer);
+ messageSource.setAutoCreateLocalDirectory(true);
+
+ messageSource.setLocalDirectory(localDirectoy);
+ messageSource.afterPropertiesSet();
+
+ String[] testFiles = new String[] {"a.test", "b.test"};
+
+ for (String testFile : testFiles) {
+ Message message = messageSource.receive();
+ assertNotNull(message);
+ assertEquals(testFile, message.getPayload().getName());
+ assertFileExists(new File(testLocalDir + "/" + testFile));
+ }
+
+ Message nothing = messageSource.receive();
+ assertNull(nothing);
+
+ // two times because on the third receive (above) the internal queue will be empty
+ verify(synchronizer, times(2)).synchronizeToLocalDirectory(localDirectoy);
+ }
+
+ class TestSmbSessionFactory extends SmbSessionFactory {
+
+ @Override
+ protected SmbSession createSession() {
+ try {
+ List smbFiles = new ArrayList();
+ for (String fileName : new File(testRemoteDir).list()) {
+ SmbFile file = smbSession.createSmbFileObject(fileName);
+ smbFiles.add(file);
+
+ doAnswer(new Answer