From 0c7063588ccb735e10d9887e30b5b6539284dbca Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Mon, 16 Jan 2017 13:04:55 -0500 Subject: [PATCH] GH-168: Upgrade to SI-4.3.6 and others Fixes GH-168 (https://github.com/spring-projects/spring-integration-extensions/issues/168) --- spring-integration-smb/.travis.yml | 12 + spring-integration-smb/README.md | 2 +- spring-integration-smb/build.gradle | 147 +++++------ spring-integration-smb/gradle.properties | 2 +- .../gradle/wrapper/gradle-wrapper.jar | Bin 49875 -> 54208 bytes .../gradle/wrapper/gradle-wrapper.properties | 4 +- spring-integration-smb/gradlew | 74 +++--- spring-integration-smb/gradlew.bat | 14 +- spring-integration-smb/publish-maven.gradle | 36 ++- spring-integration-smb/settings.gradle | 1 + spring-integration-smb/src/api/overview.html | 6 +- .../src/checkstyle/checkstyle-header.txt | 17 ++ .../checkstyle/checkstyle-suppressions.xml | 13 + .../src/checkstyle/checkstyle.xml | 188 ++++++++++++++ spring-integration-smb/src/dist/readme.txt | 6 +- .../SmbInboundChannelAdapterParser.java | 30 ++- .../smb/config/SmbNamespaceHandler.java | 12 +- .../SmbOutboundChannelAdapterParser.java | 33 +++ .../SmbRegexPatternFileListFilter.java | 34 +-- .../SmbSimplePatternFileListFilter.java | 30 +-- .../inbound/SmbInboundFileSynchronizer.java | 30 +-- ...InboundFileSynchronizingMessageSource.java | 23 +- .../integration/smb/session/SmbConfig.java | 49 ++-- .../smb/session/SmbRemoteFileTemplate.java | 37 +++ .../integration/smb/session/SmbSession.java | 230 ++++++++++++------ .../smb/session/SmbSessionFactory.java | 30 +-- .../integration/smb/session/SmbShare.java | 34 ++- .../integration/smb/session/SmbUtils.java | 12 +- .../main/resources/META-INF/spring.schemas | 4 +- .../main/resources/META-INF/spring.tooling | 2 +- .../src/main/resources/license_header.txt | 13 - ...1.0.xsd => spring-integration-smb-0.5.xsd} | 0 .../docbook/SIAdapterLowerPrefix.xml | 4 +- .../src/reference/docbook/index.xml | 4 +- ...ctBaseTest.java => AbstractBaseTests.java} | 49 ++-- .../smb/SmbMessageHistoryTests.java | 12 +- .../smb/SmbParserInboundTests.java | 13 +- .../SmbInboundChannelAdapterParserTests.java | 32 +-- .../smb/config/SmbInboundOutboundSample.java | 27 +- .../SmbOutboundChannelAdapterParserTests.java | 42 ++-- ...undRemoteFileSystemSynchronizerTests.java} | 29 ++- ...ava => SmbSendingMessageHandlerTests.java} | 73 +++--- ...bSessionTest.java => SmbSessionTests.java} | 18 +- .../src/test/resources/log4j.properties | 5 +- 44 files changed, 908 insertions(+), 525 deletions(-) create mode 100644 spring-integration-smb/.travis.yml create mode 100644 spring-integration-smb/settings.gradle create mode 100644 spring-integration-smb/src/checkstyle/checkstyle-header.txt create mode 100644 spring-integration-smb/src/checkstyle/checkstyle-suppressions.xml create mode 100644 spring-integration-smb/src/checkstyle/checkstyle.xml create mode 100644 spring-integration-smb/src/main/java/org/springframework/integration/smb/config/SmbOutboundChannelAdapterParser.java create mode 100644 spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbRemoteFileTemplate.java delete mode 100644 spring-integration-smb/src/main/resources/license_header.txt rename spring-integration-smb/src/main/resources/org/springframework/integration/smb/config/{spring-integration-smb-1.0.xsd => spring-integration-smb-0.5.xsd} (100%) rename spring-integration-smb/src/test/java/org/springframework/integration/smb/{AbstractBaseTest.java => AbstractBaseTests.java} (88%) rename spring-integration-smb/src/test/java/org/springframework/integration/smb/inbound/{SmbInboundRemoteFileSystemSynchronizerTest.java => SmbInboundRemoteFileSystemSynchronizerTests.java} (90%) rename spring-integration-smb/src/test/java/org/springframework/integration/smb/outbound/{SmbSendingMessageHandlerTest.java => SmbSendingMessageHandlerTests.java} (76%) rename spring-integration-smb/src/test/java/org/springframework/integration/smb/session/{SmbSessionTest.java => SmbSessionTests.java} (94%) diff --git a/spring-integration-smb/.travis.yml b/spring-integration-smb/.travis.yml new file mode 100644 index 0000000..aa1ed9e --- /dev/null +++ b/spring-integration-smb/.travis.yml @@ -0,0 +1,12 @@ +language: java +jdk: oraclejdk8 +sudo: false +before_cache: + - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock +cache: + directories: + - $HOME/.gradle/caches/ + - $HOME/.gradle/wrapper/ +install: true +script: + - ./gradlew check --no-daemon diff --git a/spring-integration-smb/README.md b/spring-integration-smb/README.md index 9a5e40c..aecf634 100644 --- a/spring-integration-smb/README.md +++ b/spring-integration-smb/README.md @@ -3,7 +3,7 @@ Spring Integration Smb Support ## Introduction -This module add Spring Integration* support for [Server Message Block][] (SMB). +This module add Spring Integration support for [Server Message Block][] (SMB). [Server Message Block]: http://en.wikipedia.org/wiki/Server_Message_Block diff --git a/spring-integration-smb/build.gradle b/spring-integration-smb/build.gradle index 479fe42..8585eae 100644 --- a/spring-integration-smb/build.gradle +++ b/spring-integration-smb/build.gradle @@ -1,43 +1,62 @@ -description = 'Spring Integration SMB Support' - buildscript { repositories { - maven { url 'https://repo.springsource.org/plugins-snapshot' } + maven { url 'https://repo.spring.io/plugins-release' } } dependencies { - classpath 'org.springframework.build.gradle:docbook-reference-plugin:0.1.5' + classpath 'io.spring.gradle:dependency-management-plugin:1.0.0.RC2' + classpath 'io.spring.gradle:spring-io-plugin:0.0.6.RELEASE' + classpath 'io.spring.gradle:docbook-reference-plugin:0.3.1' } } -apply plugin: 'java' +plugins { + id 'java' + id 'eclipse' + id 'idea' + id 'jacoco' + id 'checkstyle' + id 'org.sonarqube' version '2.1' +} + +description = 'Spring Integration SMB Support' + apply from: "${rootProject.projectDir}/publish-maven.gradle" -apply plugin: 'eclipse' -apply plugin: 'idea' group = 'org.springframework.integration' repositories { - maven { url 'http://repo.springsource.org/libs-milestone' } - maven { url 'http://repo.springsource.org/plugins-release' } // for bundlor + if (version.endsWith('BUILD-SNAPSHOT') || project.hasProperty('platformVersion')) { + maven { url 'http://repo.spring.io/libs-snapshot' } + } + maven { url 'http://repo.spring.io/libs-milestone' } } -// ensure JDK 5 compatibility (GRADLE-18; INT-1578) -sourceCompatibility=1.6 -targetCompatibility=1.6 +if (project.hasProperty('platformVersion')) { + apply plugin: 'spring-io' + + dependencyManagement { + springIoTestRuntime { + imports { + mavenBom "io.spring.platform:platform-bom:${platformVersion}" + } + } + } +} + +compileJava { + sourceCompatibility = 1.7 + targetCompatibility = 1.7 +} + +compileTestJava { + sourceCompatibility = 1.8 + targetCompatibility = 1.8 +} ext { - aspectjVersion = '1.6.8' - cglibVersion = '2.2' - commonsNetVersion = '3.0.1' - groovyVersion = '1.8.5' - jacksonVersion = '1.9.2' - javaxActivationVersion = '1.1.1' - junitVersion = '4.11' - log4jVersion = '1.2.12' - mockitoVersion = '1.9.0' - - springVersion = '3.1.3.RELEASE' - springIntegrationVersion = '2.2.1.RELEASE' + jcifsVersion = '1.3.18.2' + log4jVersion = '1.2.17' + springIntegrationVersion = '4.3.6.RELEASE' idPrefix = 'smb' @@ -50,24 +69,7 @@ ext { } -dependencies { - - compile "org.springframework.integration:spring-integration-core:$springIntegrationVersion" - compile "org.springframework.integration:spring-integration-file:$springIntegrationVersion" - compile "org.springframework.integration:spring-integration-stream:$springIntegrationVersion" - compile "jcifs:jcifs:1.3.17" - compile "org.springframework:spring-context-support:$springVersion" - compile("javax.activation:activation:$javaxActivationVersion", optional) - testCompile "org.springframework.integration:spring-integration-test:$springIntegrationVersion" - -} - - -eclipse { - project { - natures += 'org.springframework.ide.eclipse.core.springnature' - } -} +eclipse.project.natures += 'org.springframework.ide.eclipse.core.springnature' sourceSets { test { @@ -77,31 +79,43 @@ sourceSets { } } -// See http://www.gradle.org/docs/current/userguide/dependency_management.html#sub:configurations -// and http://www.gradle.org/docs/current/dsl/org.gradle.api.artifacts.ConfigurationContainer.html -configurations { - jacoco //Configuration Group used by Sonar to provide Code Coverage using JaCoCo +jacoco { + toolVersion = "0.7.8" +} + +checkstyle { + configFile = file("$rootDir/src/checkstyle/checkstyle.xml") + toolVersion = "6.16.1" } -// dependencies that are common across all java projects dependencies { - testCompile "cglib:cglib-nodep:$cglibVersion" - testCompile "junit:junit-dep:$junitVersion" - testCompile "log4j:log4j:$log4jVersion" - testCompile "org.hamcrest:hamcrest-all:1.1" - testCompile "org.mockito:mockito-all:$mockitoVersion" - testCompile "org.springframework:spring-test:$springVersion" - jacoco group: "org.jacoco", name: "org.jacoco.agent", version: "0.5.6.201201232323", classifier: "runtime" + compile "org.codelibs:jcifs:$jcifsVersion" + compile "org.springframework.integration:spring-integration-file:$springIntegrationVersion" + compile "org.springframework.integration:spring-integration-stream:$springIntegrationVersion" + + testCompile "org.springframework.integration:spring-integration-test:$springIntegrationVersion" + testRuntime "log4j:log4j:$log4jVersion" } // enable all compiler warnings; individual projects may customize further -ext.xLintArg = '-Xlint:all' -[compileJava, compileTestJava]*.options*.compilerArgs = [xLintArg] +[compileJava, compileTestJava]*.options*.compilerArgs = ['-Xlint:all,-options,-processing'] + +jacocoTestReport { + reports { + xml.enabled false + csv.enabled false + html.destination "${buildDir}/reports/jacoco/html" + } +} test { // suppress all console output during testing unless running `gradle -i` logging.captureStandardOutput(LogLevel.INFO) - jvmArgs "-javaagent:${configurations.jacoco.asPath}=destfile=${buildDir}/jacoco.exec,includes=*" + maxHeapSize = "1024m" + jacoco { + append = false + destinationFile = file("$buildDir/jacoco.exec") + } } task sourcesJar(type: Jar) { @@ -125,10 +139,8 @@ reference { sourceDir = file('src/reference/docbook') } -apply plugin: 'sonar-runner' - -sonarRunner { - sonarProperties { +sonarqube { + properties { property "sonar.jacoco.reportPath", "${buildDir.name}/jacoco.exec" property "sonar.links.homepage", linkHomepage property "sonar.links.ci", linkCi @@ -150,7 +162,7 @@ task api(type: Javadoc) { source = sourceSets.main.allJava classpath = project.sourceSets.main.compileClasspath - destinationDir = new File(buildDir, "api") + destinationDir = file("$buildDir/api") } task schemaZip(type: Zip) { @@ -163,15 +175,15 @@ task schemaZip(type: Zip) { def shortName = idPrefix.replaceFirst("${idPrefix}-", '') project.sourceSets.main.resources.find { - it.path.endsWith('META-INF/spring.schemas') + it.path.endsWith("META-INF${File.separator}spring.schemas") }?.withInputStream { schemas.load(it) } for (def key : schemas.keySet()) { File xsdFile = project.sourceSets.main.resources.find { - it.path.endsWith(schemas.get(key)) + it.path.replaceAll('\\\\', '/').endsWith(schemas.get(key)) } assert xsdFile != null - into ("integration/${shortName}") { + into("integration/${shortName}") { from xsdFile.path } } @@ -266,8 +278,3 @@ task dist(dependsOn: assemble) { group = 'Distribution' description = 'Builds -dist, -docs and -schema distribution archives.' } - -task wrapper(type: Wrapper) { - description = 'Generates gradlew[.bat] scripts' - gradleVersion = '1.6' -} diff --git a/spring-integration-smb/gradle.properties b/spring-integration-smb/gradle.properties index bebfcbc..5364470 100644 --- a/spring-integration-smb/gradle.properties +++ b/spring-integration-smb/gradle.properties @@ -1 +1 @@ -version=1.0.0.BUILD-SNAPSHOT +version=0.5.0.BUILD-SNAPSHOT diff --git a/spring-integration-smb/gradle/wrapper/gradle-wrapper.jar b/spring-integration-smb/gradle/wrapper/gradle-wrapper.jar index a7634b071cb255e91a4572934e55b8cd8877b3e4..cf7c6b97dbda487d57969cd015c02d20f8996941 100644 GIT binary patch delta 48258 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ⅇ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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -30,6 +48,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,31 +59,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -90,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -114,6 +113,7 @@ fi if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` @@ -154,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/spring-integration-smb/gradlew.bat b/spring-integration-smb/gradlew.bat index aec9973..e95643d 100644 --- a/spring-integration-smb/gradlew.bat +++ b/spring-integration-smb/gradlew.bat @@ -8,14 +8,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +46,9 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line diff --git a/spring-integration-smb/publish-maven.gradle b/spring-integration-smb/publish-maven.gradle index 06eb14e..fe5dd02 100644 --- a/spring-integration-smb/publish-maven.gradle +++ b/spring-integration-smb/publish-maven.gradle @@ -31,10 +31,10 @@ def customizePom(pom, gradleProject) { generatedPom.project { name = gradleProject.description description = gradleProject.description - url = 'https://github.com/SpringSource/spring-integration-extensions' + url = linkHomepage organization { - name = 'SpringSource' - url = 'http://springsource.org' + name = 'SpringIO' + url = 'http://spring.io' } licenses { license { @@ -43,19 +43,37 @@ def customizePom(pom, gradleProject) { distribution 'repo' } } + scm { - url = 'https://github.com/SpringSource/spring-integration-extensions' - connection = 'scm:git:git://github.com/SpringSource/spring-integration-extensions' - developerConnection = 'scm:git:git://github.com/SpringSource/spring-integration-extensions' + url = linkScmUrl + connection = 'scm:git:' + linkScmConnection + developerConnection = 'scm:git:' + linkScmDevConnection + } + + issueManagement { + system = "Jira" + url = linkIssue } developers { developer { - id = 'not specified' - name = 'Markus Spann' - email = 'not specified' + id = 'garyrussell' + name = 'Gary Russell' + email = 'grussell@pivotal.io' + roles = ["project lead"] + } + developer { + id = 'ghillert' + name = 'Gunnar Hillert' + email = 'ghillert@pivotal.io' + } + developer { + id = 'abilan' + name = 'Artem Bilan' + email = 'abilan@pivotal.io' } } } } } + diff --git a/spring-integration-smb/settings.gradle b/spring-integration-smb/settings.gradle new file mode 100644 index 0000000..8e5f6a5 --- /dev/null +++ b/spring-integration-smb/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'spring-integration-smb' diff --git a/spring-integration-smb/src/api/overview.html b/spring-integration-smb/src/api/overview.html index fb0198b..61bbef4 100644 --- a/spring-integration-smb/src/api/overview.html +++ b/spring-integration-smb/src/api/overview.html @@ -5,7 +5,7 @@ This document is the API specification for Spring Integration

diff --git a/spring-integration-smb/src/checkstyle/checkstyle-header.txt b/spring-integration-smb/src/checkstyle/checkstyle-header.txt new file mode 100644 index 0000000..f470e97 --- /dev/null +++ b/spring-integration-smb/src/checkstyle/checkstyle-header.txt @@ -0,0 +1,17 @@ +^\Q/*\E$ +^\Q * Copyright \E20\d\d(\-20\d\d)?\Q the original author or authors.\E$ +^\Q *\E$ +^\Q * Licensed under the Apache License, Version 2.0 (the "License");\E$ +^\Q * you may not use this file except in compliance with the License.\E$ +^\Q * You may obtain a copy of the License at\E$ +^\Q *\E$ +^\Q * http://www.apache.org/licenses/LICENSE-2.0\E$ +^\Q *\E$ +^\Q * Unless required by applicable law or agreed to in writing, software\E$ +^\Q * distributed under the License is distributed on an "AS IS" BASIS,\E$ +^\Q * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\E$ +^\Q * See the License for the specific language governing permissions and\E$ +^\Q * limitations under the License.\E$ +^\Q */\E$ +^$ +^.*$ diff --git a/spring-integration-smb/src/checkstyle/checkstyle-suppressions.xml b/spring-integration-smb/src/checkstyle/checkstyle-suppressions.xml new file mode 100644 index 0000000..2db9e2f --- /dev/null +++ b/spring-integration-smb/src/checkstyle/checkstyle-suppressions.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/spring-integration-smb/src/checkstyle/checkstyle.xml b/spring-integration-smb/src/checkstyle/checkstyle.xml new file mode 100644 index 0000000..543ba4d --- /dev/null +++ b/spring-integration-smb/src/checkstyle/checkstyle.xml @@ -0,0 +1,188 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-smb/src/dist/readme.txt b/spring-integration-smb/src/dist/readme.txt index 82afb88..84ec4dc 100644 --- a/spring-integration-smb/src/dist/readme.txt +++ b/spring-integration-smb/src/dist/readme.txt @@ -1,13 +1,13 @@ -Spring Integration Smb Adapter +Spring Integration Smb Adapters ----------------------------------- To find out what has changed since any earlier releases, see 'changelog.txt'. Please consult the documentation located within the 'docs/reference' directory of this release and also visit the official Spring Integration home at -http://www.springsource.org/spring-integration +http://spring.io/spring-integration There you will find links to the forum, issue tracker, and several other resources. -See https://github.com/SpringSource/spring-integration#readme for additional +See https://github.com/spring-projects/spring-integration#readme for additional information including instructions on building from source. 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 index 03f3e0b..ac1616c 100644 --- 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 @@ -1,5 +1,5 @@ -/** - * Copyright 2002-2012 the original author or authors. +/* + * Copyright 2012-2017 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. @@ -13,39 +13,43 @@ * 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; +import org.springframework.integration.file.filters.FileListFilter; +import org.springframework.integration.file.remote.synchronizer.InboundFileSynchronizer; +import org.springframework.integration.smb.filters.SmbRegexPatternFileListFilter; +import org.springframework.integration.smb.filters.SmbSimplePatternFileListFilter; +import org.springframework.integration.smb.inbound.SmbInboundFileSynchronizer; +import org.springframework.integration.smb.inbound.SmbInboundFileSynchronizingMessageSource; /** * Parser for the SMB 'inbound-channel-adapter' element. * * @author Markus Spann - * @since 1.0 + * @author Artem Bilan */ public class SmbInboundChannelAdapterParser extends AbstractRemoteFileInboundChannelAdapterParser { - private static final String BASE_PACKAGE = "org.springframework.integration.smb"; - - @Override protected String getMessageSourceClassname() { - return BASE_PACKAGE + ".inbound.SmbInboundFileSynchronizingMessageSource"; + return SmbInboundFileSynchronizingMessageSource.class.getName(); } @Override - protected String getInboundFileSynchronizerClassname() { - return BASE_PACKAGE + ".inbound.SmbInboundFileSynchronizer"; + protected Class getInboundFileSynchronizerClass() { + return SmbInboundFileSynchronizer.class; } @Override - protected String getSimplePatternFileListFilterClassname() { - return BASE_PACKAGE + ".filters.SmbSimplePatternFileListFilter"; + protected Class> getSimplePatternFileListFilterClass() { + return SmbSimplePatternFileListFilter.class; } @Override - protected String getRegexPatternFileListFilterClassname() { - return BASE_PACKAGE + ".filters.SmbRegexPatternFileListFilter"; + protected Class> getRegexPatternFileListFilterClass() { + return SmbRegexPatternFileListFilter.class; } } 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 index ab0b041..0ebc9d4 100644 --- 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 @@ -1,5 +1,5 @@ -/** - * Copyright 2002-2012 the original author or authors. +/* + * Copyright 2012-2017 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. @@ -13,22 +13,22 @@ * 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 1.0 + * @author Artem Bilan */ 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? + registerBeanDefinitionParser("inbound-channel-adapter", new SmbInboundChannelAdapterParser()); + registerBeanDefinitionParser("outbound-channel-adapter", new SmbOutboundChannelAdapterParser()); } } diff --git a/spring-integration-smb/src/main/java/org/springframework/integration/smb/config/SmbOutboundChannelAdapterParser.java b/spring-integration-smb/src/main/java/org/springframework/integration/smb/config/SmbOutboundChannelAdapterParser.java new file mode 100644 index 0000000..1a90882 --- /dev/null +++ b/spring-integration-smb/src/main/java/org/springframework/integration/smb/config/SmbOutboundChannelAdapterParser.java @@ -0,0 +1,33 @@ +/* + * Copyright 2017 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.RemoteFileOutboundChannelAdapterParser; +import org.springframework.integration.file.remote.RemoteFileOperations; +import org.springframework.integration.smb.session.SmbRemoteFileTemplate; + +/** + * @author Artem Bilan + */ +public class SmbOutboundChannelAdapterParser extends RemoteFileOutboundChannelAdapterParser { + + @Override + protected Class> getTemplateClass() { + return SmbRemoteFileTemplate.class; + } + +} 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 index 1feab84..539358a 100644 --- 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 @@ -1,5 +1,5 @@ -/** - * Copyright 2002-2012 the original author or authors. +/* + * Copyright 2012-2017 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. @@ -13,47 +13,39 @@ * 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; +import jcifs.smb.SmbFile; + /** * Implementation of {@link AbstractRegexPatternFileListFilter} for SMB. * * @author Markus Spann - * @since 1.0 */ public class SmbRegexPatternFileListFilter extends AbstractRegexPatternFileListFilter { - private final String toString; - - public SmbRegexPatternFileListFilter(String _pattern) { - this(Pattern.compile(_pattern)); + public SmbRegexPatternFileListFilter(String pattern) { + this(Pattern.compile(pattern)); } - public SmbRegexPatternFileListFilter(Pattern _pattern) { - super(_pattern); - toString = getClass().getName() + "[pattern='" + _pattern + "']"; + public SmbRegexPatternFileListFilter(Pattern pattern) { + super(pattern); } /** * Gets the specified SMB file's name. - * @param _file SMB file object + * @param file SMB file object * @return file name - * @see org.springframework.integration.file.filters.AbstractRegexPatternFileListFilter#getFilename(java.lang.Object) + * @see AbstractRegexPatternFileListFilter#getFilename(java.lang.Object) */ @Override - protected String getFilename(SmbFile _file) { - return (_file != null) ? _file.getName() : null; - } - - @Override - public String toString() { - return toString; + protected String getFilename(SmbFile file) { + return (file != null ? file.getName() : null); } } 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 index ee89e20..4ffaef2 100644 --- 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 @@ -1,5 +1,5 @@ -/** - * Copyright 2002-2012 the original author or authors. +/* + * Copyright 2012-2017 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. @@ -13,42 +13,34 @@ * 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; +import jcifs.smb.SmbFile; + /** * Implementation of {@link AbstractSimplePatternFileListFilter} for SMB. * * @author Markus Spann - * @since 1.0 * */ public class SmbSimplePatternFileListFilter extends AbstractSimplePatternFileListFilter { - private final String toString; - - public SmbSimplePatternFileListFilter(String _pathPattern) { - super(_pathPattern); - toString = getClass().getName() + "[pattern='" + _pathPattern + "']"; + public SmbSimplePatternFileListFilter(String pathPattern) { + super(pathPattern); } /** * Gets the specified SMB file's name. - * @param _file SMB file object + * @param file SMB file object * @return file name - * @see org.springframework.integration.file.filters.AbstractSimplePatternFileListFilter#getFilename(java.lang.Object) + * @see AbstractSimplePatternFileListFilter#getFilename(java.lang.Object) */ @Override - protected String getFilename(SmbFile _file) { - return (_file != null) ? _file.getName() : null; - } - - @Override - public String toString() { - return toString; + protected String getFilename(SmbFile file) { + return (file != null) ? file.getName() : null; } } 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 index 6f4132e..5d83f54 100644 --- 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 @@ -1,5 +1,5 @@ -/** - * Copyright 2002-2012 the original author or authors. +/* + * Copyright 2012-2017 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. @@ -13,41 +13,37 @@ * 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; +import jcifs.smb.SmbFile; + /** * An implementation of {@link AbstractInboundFileSynchronizer} for SMB. * * @author Markus Spann - * @since 1.0 + * @author Artem Bilan */ 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. + * @param sessionFactory the {@link SessionFactory} to use. */ - public SmbInboundFileSynchronizer(SessionFactory _sessionFactory) { - super(_sessionFactory); - toString = getClass().getName() + "[sessionFactory=" + _sessionFactory + "]"; + public SmbInboundFileSynchronizer(SessionFactory sessionFactory) { + super(sessionFactory); } @Override protected boolean isFile(SmbFile _file) { try { return _file != null && _file.isFile(); - } catch (Exception _ex) { + } + catch (Exception _ex) { logger.warn("Unable to get resource status [" + _file + "].", _ex); } return false; @@ -59,8 +55,8 @@ public class SmbInboundFileSynchronizer extends AbstractInboundFileSynchronizer< } @Override - public String toString() { - return toString; + protected long getModified(SmbFile file) { + return file.getLastModified(); } } 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 index 1b8c0fb..62a7974 100644 --- 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 @@ -1,5 +1,5 @@ -/** - * Copyright 2002-2012 the original author or authors. +/* + * Copyright 2012-2017 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. @@ -13,16 +13,17 @@ * 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; +import jcifs.smb.SmbFile; + /** * A {@link org.springframework.integration.core.MessageSource} implementation for SMB. * @@ -31,26 +32,18 @@ import org.springframework.integration.file.remote.synchronizer.AbstractInboundF */ public class SmbInboundFileSynchronizingMessageSource extends AbstractInboundFileSynchronizingMessageSource { - private final static String componentType = "smb:inbound-channel-adapter"; - private final String toString; - public SmbInboundFileSynchronizingMessageSource(AbstractInboundFileSynchronizer _synchronizer) { this(_synchronizer, null); } - public SmbInboundFileSynchronizingMessageSource(AbstractInboundFileSynchronizer _synchronizer, Comparator _comparator) { + 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; + return "smb:inbound-channel-adapter"; } } 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 index e6b8d61..d183204 100644 --- 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 @@ -1,5 +1,5 @@ -/** - * Copyright 2002-2012 the original author or authors. +/* + * Copyright 2012-2017 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. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.smb.session; import java.io.UnsupportedEncodingException; @@ -32,22 +33,27 @@ import org.springframework.util.StringUtils; */ public class SmbConfig { - private String host; - private int port; - private String domain; - private String username; - private String password; - private String shareAndDir; + 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(); - + public SmbConfig(String _host, int _port, String _domain, String _username, String _password, String _shareAndDir) + throws UnsupportedEncodingException { setHost(_host); setPort(_port); setDomain(_domain); @@ -62,7 +68,7 @@ public class SmbConfig { } public String getHost() { - return host; + return this.host; } public void setPort(int _port) { @@ -71,7 +77,7 @@ public class SmbConfig { } public int getPort() { - return port; + return this.port; } public void setDomain(String _domain) { @@ -80,7 +86,7 @@ public class SmbConfig { } public String getDomain() { - return domain; + return this.domain; } public void setUsername(String _username) { @@ -89,7 +95,7 @@ public class SmbConfig { } public String getUsername() { - return username; + return this.username; } public void setPassword(String _password) { @@ -98,7 +104,7 @@ public class SmbConfig { } public String getPassword() { - return password; + return this.password; } public void setShareAndDir(String _shareAndDir) { @@ -107,7 +113,7 @@ public class SmbConfig { } public String getShareAndDir() { - return shareAndDir; + return this.shareAndDir; } public void setReplaceFile(boolean _replaceFile) { @@ -115,7 +121,7 @@ public class SmbConfig { } public boolean isReplaceFile() { - return replaceFile; + return this.replaceFile; } void setUseTempFile(boolean _useTempFile) { @@ -123,7 +129,7 @@ public class SmbConfig { } public boolean isUseTempFile() { - return useTempFile; + return this.useTempFile; } String getDomainUserPass(boolean _includePassword) { @@ -131,7 +137,8 @@ public class SmbConfig { String user = _includePassword ? this.username : "********"; if (StringUtils.hasText(this.domain)) { domainUserPass = String.format("%s;%s", this.domain, user); - } else { + } + else { domainUserPass = user; } if (StringUtils.hasText(this.password)) { @@ -176,7 +183,7 @@ public class SmbConfig { public String toString() { return getClass().getSimpleName() + "[url=" + getUrl(false) - + ", replaceFile=" + replaceFile + + ", replaceFile=" + this.replaceFile + "]"; } diff --git a/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbRemoteFileTemplate.java b/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbRemoteFileTemplate.java new file mode 100644 index 0000000..39d247b --- /dev/null +++ b/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbRemoteFileTemplate.java @@ -0,0 +1,37 @@ +/* + * Copyright 2017 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 org.springframework.integration.file.remote.RemoteFileTemplate; +import org.springframework.integration.file.remote.session.SessionFactory; + +import jcifs.smb.SmbFile; + +/** + * @author Artem Bilan + */ +public class SmbRemoteFileTemplate extends RemoteFileTemplate { + + /** + * Construct a {@link SmbRemoteFileTemplate} with the supplied session factory. + * @param sessionFactory the session factory. + */ + public SmbRemoteFileTemplate(SessionFactory sessionFactory) { + super(sessionFactory); + } + +} 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 index 5cdc373..7ea9ce2 100644 --- 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 @@ -1,5 +1,5 @@ -/** - * Copyright 2002-2012 the original author or authors. +/* + * Copyright 2012-2017 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. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.smb.session; import java.io.ByteArrayInputStream; @@ -23,17 +24,19 @@ 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; +import jcifs.smb.SmbException; +import jcifs.smb.SmbFile; +import jcifs.smb.SmbFileOutputStream; + /** * 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 @@ -45,19 +48,18 @@ import org.springframework.util.StringUtils; * See Server Message Block * for more details. * - * Inspired by the spring-integration-ftp implementation done by Mark Fisher - * and Oleg Zhurakousky. - * * @author Markus Spann * @author Mark Fisher * @author Oleg Zhurakousky + * @author Artem Bilan * - * @since 1.0 */ public class SmbSession implements Session { - private final Log logger = LogFactory.getLog(SmbSession.class); + private static final Log logger = LogFactory.getLog(SmbSession.class); + private static final String FILE_SEPARATOR = System.getProperty("file.separator"); + private static final String SMB_FILE_SEPARATOR = "/"; static { @@ -78,11 +80,12 @@ public class SmbSession implements Session { * @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 { + 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); + this.smbShare.setReplaceFile(_replaceFile); + this.smbShare.setUseTempFile(_useTempFile); } /** @@ -91,8 +94,10 @@ public class SmbSession implements Session { */ public SmbSession(SmbShare _smbShare) { Assert.notNull(_smbShare, "smbShare must not be null"); - smbShare = _smbShare; - logger.debug("New " + getClass().getName() + " created."); + this.smbShare = _smbShare; + if (logger.isDebugEnabled()) { + logger.debug("New " + getClass().getName() + " created."); + } } /** @@ -100,8 +105,8 @@ public class SmbSession implements Session { * @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) */ + @Override public boolean remove(String _path) throws IOException { Assert.hasText(_path, "path must not be empty"); @@ -112,9 +117,10 @@ public class SmbSession implements Session { removeFile.delete(); removed = true; } - if (!removed) { + if (!removed && logger.isInfoEnabled()) { logger.info("Could not remove non-existing resource [" + _path + "]."); - } else if (logger.isInfoEnabled()) { + } + else if (logger.isInfoEnabled()) { logger.info("Successfully removed resource [" + _path + "]."); } return removed; @@ -126,29 +132,34 @@ public class SmbSession implements Session { * @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) */ + @Override 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."); + if (logger.isWarnEnabled()) { + logger.warn("Remote directory [" + _path + "] does not exist. Cannot list resources."); + } return files; - } else if (!smbDir.isDirectory()) { + } + else if (!smbDir.isDirectory()) { throw new NestedIOException("Resource [" + _path + "] is not a directory. Cannot list resources."); } files = smbDir.listFiles(); - - } catch (SmbException _ex) { + } + 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 + "."); + logger.debug("Successfully listed " + files.length + " resource(s) in [" + _path + "]" + + ": " + Arrays.toString(files)); + } + else if (logger.isInfoEnabled()) { + logger.info("Successfully listed " + files.length + " resource(s) in [" + _path + "]" + "."); } return files; @@ -160,34 +171,36 @@ public class SmbSession implements Session { * @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) */ + @Override 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) { + } + catch (SmbException _ex) { throw new NestedIOException("Failed to read resource [" + _path + "].", _ex); } - logger.info("Successfully read resource [" + _path + "]."); + + if (logger.isInfoEnabled()) { + 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. + * specified by path. Remote directories are created implicitly 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) */ + @Override 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"); @@ -198,35 +211,39 @@ public class SmbSession implements Session { SmbFile targetFile = createSmbFileObject(_path); - if (smbShare.isUseTempFile()) { + if (this.smbShare.isUseTempFile()) { - String tempFileName = _path + smbShare.newTempFileSuffix(); + String tempFileName = _path + this.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()) { + if (targetFile.exists() && this.smbShare.isReplaceFile()) { targetFile.delete(); } tempFile.renameTo(targetFile); - - } else { - + } + else { FileCopyUtils.copy(_inputStream, targetFile.getOutputStream()); - } - } catch (SmbException _ex) { + } + catch (SmbException _ex) { throw new NestedIOException("Failed to write resource [" + _path + "].", _ex); } - logger.info("Successfully wrote remote file [" + _path + "]."); + if (logger.isInfoEnabled()) { + 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) + * @param _file the local file + * @param _path the remote path to write to + * @return the {@link SmbFile} for remote file + * @throws IOException the IO exception */ public SmbFile write(File _file, String _path) throws IOException { return writeAndClose(new FileInputStream(_file), _path); @@ -234,7 +251,10 @@ public class SmbSession implements Session { /** * Convenience method to write a byte array to a remote location. - * @see org.springframework.integration.smb.session.SmbSession#write(InputStream, String) + * @param _contents the {@code byte[]} to write + * @param _path the remote file to write to + * @return the {@link SmbFile} for remote file + * @throws IOException the IO exception */ public SmbFile write(byte[] _contents, String _path) throws IOException { return writeAndClose(new ByteArrayInputStream(_contents), _path); @@ -247,19 +267,25 @@ public class SmbSession implements Session { * @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) */ + @Override 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 + "]."); + if (logger.isInfoEnabled()) { + logger.info("Successfully created remote directory [" + _path + "] in share [" + this.smbShare + "]."); + } + } + else { + if (logger.isInfoEnabled()) { + logger.info("Remote directory [" + _path + "] exists in share [" + this.smbShare + "]."); + } } return true; - } catch (SmbException _ex) { + } + catch (SmbException _ex) { throw new NestedIOException("Failed to create directory [" + _path + "].", _ex); } } @@ -269,8 +295,8 @@ public class SmbSession implements Session { * @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) */ + @Override public boolean exists(String _path) throws IOException { return createSmbFileObject(_path).exists(); } @@ -313,54 +339,93 @@ public class SmbSession implements Session { 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) - */ + @Override public void rename(String _pathFrom, String _pathTo) throws IOException { try { SmbFile smbFileFrom = createSmbFileObject(_pathFrom); SmbFile smbFileTo = createSmbFileObject(_pathTo); - if (smbShare.isReplaceFile() && smbFileTo.exists()) { + if (this.smbShare.isReplaceFile() && smbFileTo.exists()) { smbFileTo.delete(); } smbFileFrom.renameTo(smbFileTo); - } catch (SmbException _ex) { + } + catch (SmbException _ex) { throw new NestedIOException("Failed to rename [" + _pathFrom + "] to [" + _pathTo + "].", _ex); } - logger.info("Successfully renamed remote resource [" + _pathFrom + "] to [" + _pathTo + "]."); + if (logger.isInfoEnabled()) { + logger.info("Successfully renamed remote resource [" + _pathFrom + "] to [" + _pathTo + "]."); + } } - /** - * Closes this SMB session. - * @see org.springframework.integration.file.remote.session.Session#close() - */ + @Override + public void append(InputStream inputStream, String destination) throws IOException { + SmbFile smbFile = createSmbFileObject(destination); + OutputStream fileOutputStream = new SmbFileOutputStream(smbFile, true); + FileCopyUtils.copy(inputStream, fileOutputStream); + } + + @Override + public boolean rmdir(String directory) throws IOException { + SmbFile dir = createSmbDirectoryObject(directory); + try { + dir.delete(); + } + catch (SmbException e) { + if (logger.isWarnEnabled()) { + logger.info("Failed to remove remote directory [" + directory + "]: " + e); + } + return false; + } + if (logger.isInfoEnabled()) { + logger.info("Successfully removed remote directory [" + directory + "]."); + } + return true; + } + + @Override + public InputStream readRaw(String source) throws IOException { + SmbFile remoteFile = createSmbFileObject(source); + if (!remoteFile.isFile()) { + throw new NestedIOException("Resource [" + source + "] is not a file."); + } + return remoteFile.getInputStream(); + } + + @Override + public boolean finalizeRaw() throws IOException { + return true; + } + + @Override + public Object getClientInstance() { + return this.smbShare; + } + + @Override public void close() { - smbShare.doClose(); + this.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() */ + @Override public boolean isOpen() { - if (!smbShare.isOpened()) { + if (!this.smbShare.isOpened()) { return false; } try { - smbShare.listFiles(); - } catch (Exception _ex) { + this.smbShare.listFiles(); + } + catch (Exception _ex) { close(); } - return smbShare.isOpened(); + return this.smbShare.isOpened(); } /** @@ -389,17 +454,18 @@ public class SmbSession implements Session { final String cleanedPath = StringUtils.cleanPath(path); if (!StringUtils.hasText(cleanedPath)) { - return smbShare; + return this.smbShare; } - SmbFile smbFile = new SmbFile(smbShare, cleanedPath); + SmbFile smbFile = new SmbFile(this.smbShare, cleanedPath); boolean appendFileSeparator = !cleanedPath.endsWith(SMB_FILE_SEPARATOR); if (appendFileSeparator) { try { - appendFileSeparator = smbFile.isDirectory() || (isDirectory != null && isDirectory); - } catch (SmbException ex) { - appendFileSeparator = false; + appendFileSeparator = smbFile.isDirectory() || (isDirectory != null && isDirectory); + } + catch (SmbException ex) { + appendFileSeparator = false; } } if (appendFileSeparator) { @@ -413,6 +479,9 @@ public class SmbSession implements Session { /** * Creates an SMB file object pointing to a remote file. + * @param _path the remote file path + * @return the {@link SmbFile} for remote path + * @throws IOException the IO exception */ public SmbFile createSmbFileObject(String _path) throws IOException { return createSmbFileObject(_path, null); @@ -420,6 +489,9 @@ public class SmbSession implements Session { /** * Creates an SMB file object pointing to a remote directory. + * @param _path the remote directory path + * @return the {@link SmbFile} for remote path + * @throws IOException the IO exception */ public SmbFile createSmbDirectoryObject(String _path) throws IOException { return createSmbFileObject(_path, true); @@ -441,9 +513,11 @@ public class SmbSession implements Session { Log log = LogFactory.getLog(SmbSession.class); if (log.isTraceEnabled()) { jcifs.Config.setProperty(sysPropLogLevel, "N"); - } else if (log.isDebugEnabled()) { + } + else if (log.isDebugEnabled()) { jcifs.Config.setProperty(sysPropLogLevel, "3"); - } else { + } + 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 index 58ec31b..5454f98 100644 --- 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 @@ -1,5 +1,5 @@ -/** - * Copyright 2002-2012 the original author or authors. +/* + * Copyright 2012-2017 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. @@ -13,16 +13,18 @@ * 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; +import jcifs.smb.SmbFile; + /** * The SMB session factory. * @@ -31,7 +33,7 @@ import org.springframework.integration.file.remote.session.SessionFactory; */ public class SmbSessionFactory extends SmbConfig implements SessionFactory { - private final Log logger = LogFactory.getLog(this.getClass()); + private static Log logger = LogFactory.getLog(SmbSessionFactory.class); public SmbSessionFactory() { logger.debug("New " + getClass().getName() + " created."); @@ -40,17 +42,20 @@ public class SmbSessionFactory extends SmbConfig implements SessionFactory Java Implementation - Each of the provided components will use the - org.springframework.smb.core.SmbExecutor + Each of the provided components use the + org.springframework.integration.smb.session.SmbSession class... diff --git a/spring-integration-smb/src/reference/docbook/index.xml b/spring-integration-smb/src/reference/docbook/index.xml index 53bc084..37060f4 100644 --- a/spring-integration-smb/src/reference/docbook/index.xml +++ b/spring-integration-smb/src/reference/docbook/index.xml @@ -23,7 +23,7 @@ Markus Spann - © SpringSource Inc., 2012 + Pivotal Software, Inc. All Rights Reserved., 2012-2017 @@ -35,7 +35,7 @@ For those who are already familiar with Spring Integration, this chapter - provides a brief overview of the new features of version 2.2. If you are + provides a brief overview of the new features of version 0.5. If you are interested in the changes and features, that were introduced in earlier versions, please take a look at chapter: 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/AbstractBaseTests.java similarity index 88% rename from spring-integration-smb/src/test/java/org/springframework/integration/smb/AbstractBaseTest.java rename to spring-integration-smb/src/test/java/org/springframework/integration/smb/AbstractBaseTests.java index ca75631..14bb03d 100644 --- a/spring-integration-smb/src/test/java/org/springframework/integration/smb/AbstractBaseTest.java +++ b/spring-integration-smb/src/test/java/org/springframework/integration/smb/AbstractBaseTests.java @@ -1,5 +1,5 @@ -/** - * Copyright 2002-2013 the original author or authors. +/* + * Copyright 2012-2017 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. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.smb; import static org.junit.Assert.assertNotNull; @@ -31,6 +32,7 @@ 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; @@ -39,16 +41,16 @@ import org.springframework.util.StringUtils; * Assorted test utils for the library. * @author Markus Spann */ -public abstract class AbstractBaseTest { +public abstract class AbstractBaseTests { /** Instance logger. */ - private final Log logger = LogFactory.getLog(this.getClass()); + private final Log logger = LogFactory.getLog(this.getClass()); protected final Log getLogger() { return logger; } - @Rule // requires JUnit 4.7 or later + @Rule public final TestName testMethodName = new TestName(); private String getTestMethodName() { @@ -102,7 +104,8 @@ public abstract class AbstractBaseTest { FileOutputStream fos = new FileOutputStream(_path); try { FileCopyUtils.copy(_inputStream, fos); - } finally { + } + finally { fos.close(); } } @@ -230,7 +233,8 @@ public abstract class AbstractBaseTest { assertNotNull("File object is null.", _file); if (_exists) { assertTrue("File [" + _file.getAbsolutePath() + "] does not exist.", _file.exists()); - } else { + } + else { assertTrue("File [" + _file.getAbsolutePath() + "] exists.", !_file.exists()); } return _file; @@ -246,34 +250,23 @@ public abstract class AbstractBaseTest { * @param _testClass test class object * @param _methodNames String method names to invoke in order, no parameters expected */ - protected static void runTests(Class _testClass, String... _methodNames) { - AbstractBaseTest test; + protected static void runTests(Class _testClass, String... _methodNames) + throws Exception { + AbstractBaseTests 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; + test = _testClass.newInstance(); + for (int i = 0; i < _methodNames.length; i++) { + methodName = _methodNames[i]; + methods[i] = _testClass.getMethod(methodName, (Class[]) null); } 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(); + for (int i = 0; i < methods.length; i++) { + method = methods[i]; + method.invoke(test, (Object[]) null); } } 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 index fd7e034..570dc5e 100644 --- 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 @@ -1,5 +1,5 @@ -/** - * Copyright 2002-2013 the original author or authors. +/* + * Copyright 2012-2017 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. @@ -13,21 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.smb; import static org.junit.Assert.assertEquals; import org.junit.Test; + import org.springframework.integration.endpoint.SourcePollingChannelAdapter; /** * @author Markus Spann * */ -public class SmbMessageHistoryTests extends AbstractBaseTest { +public class SmbMessageHistoryTests extends AbstractBaseTests { + @Test public void testMessageHistory() throws Exception { - SourcePollingChannelAdapter adapter = getApplicationContext().getBean("smbInboundChannelAdapter", SourcePollingChannelAdapter.class); + 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.java b/spring-integration-smb/src/test/java/org/springframework/integration/smb/SmbParserInboundTests.java index 291d1dd..9498ae5 100644 --- 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 @@ -1,5 +1,5 @@ -/** - * Copyright 2002-2012 the original author or authors. +/* + * Copyright 2012-2017 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. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.smb; import java.io.File; @@ -20,6 +21,7 @@ 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; @@ -27,7 +29,7 @@ import org.springframework.context.support.ClassPathXmlApplicationContext; * @author Markus Spann * */ -public class SmbParserInboundTests extends AbstractBaseTest { +public class SmbParserInboundTests extends AbstractBaseTests { @Before public void prepare() { @@ -46,7 +48,8 @@ public class SmbParserInboundTests extends AbstractBaseTest { @Test(expected = BeanCreationException.class) public void testLocalFilesAutoCreationFalse() throws Exception { assertFileNotExists(new File("test-temp/local-6")); - new ClassPathXmlApplicationContext(getApplicationContextXmlFile("-fail"), this.getClass()); + new ClassPathXmlApplicationContext(getApplicationContextXmlFile("-fail"), this.getClass()) + .close(); } @After @@ -54,7 +57,7 @@ public class SmbParserInboundTests extends AbstractBaseTest { delete("test-temp/local-10", "test-temp/local-6"); } - public static void main(String[] _args) { + public static void main(String[] _args) throws Exception { new SmbParserInboundTests().cleanUp(); runTests(SmbParserInboundTests.class, "testLocalFilesAutoCreationTrue", "testLocalFilesAutoCreationFalse"); } 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 index 208cfa1..eef2ea6 100644 --- 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 @@ -1,5 +1,5 @@ -/** - * Copyright 2002-2013 the original author or authors. +/* + * Copyright 2012-2017 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. @@ -13,10 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.smb.config; +import static org.hamcrest.Matchers.instanceOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -26,11 +29,11 @@ import java.util.concurrent.PriorityBlockingQueue; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.integration.endpoint.SourcePollingChannelAdapter; -import org.springframework.integration.file.remote.session.CachingSessionFactory; import org.springframework.integration.smb.filters.SmbSimplePatternFileListFilter; import org.springframework.integration.smb.inbound.SmbInboundFileSynchronizer; import org.springframework.integration.smb.inbound.SmbInboundFileSynchronizingMessageSource; @@ -43,6 +46,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @author Markus Spann * @author Gunnar Hillert + * @author Artem Bilan */ @ContextConfiguration @RunWith(SpringJUnit4ClassRunner.class) @@ -52,7 +56,7 @@ public class SmbInboundChannelAdapterParserTests { ApplicationContext applicationContext; @Test(timeout = 100000) - public void testSmbInboundChannelAdapterComplete() throws Exception{ + public void testSmbInboundChannelAdapterComplete() throws Exception { final SourcePollingChannelAdapter adapter = this.applicationContext.getBean("smbInbound", SourcePollingChannelAdapter.class); final PriorityBlockingQueue queue = TestUtils.getPropertyValue(adapter, "source.fileSource.toBeReceived", PriorityBlockingQueue.class); @@ -63,39 +67,39 @@ public class SmbInboundChannelAdapterParserTests { assertNotNull(TestUtils.getPropertyValue(adapter, "poller")); assertEquals(applicationContext.getBean("smbChannel"), TestUtils.getPropertyValue(adapter, "outputChannel")); SmbInboundFileSynchronizingMessageSource inbound = - (SmbInboundFileSynchronizingMessageSource) TestUtils.getPropertyValue(adapter, "source"); + (SmbInboundFileSynchronizingMessageSource) TestUtils.getPropertyValue(adapter, "source"); SmbInboundFileSynchronizer fisync = - (SmbInboundFileSynchronizer) TestUtils.getPropertyValue(inbound, "synchronizer"); + (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"); + Object sessionFactory = TestUtils.getPropertyValue(fisync, "remoteFileTemplate.sessionFactory"); assertTrue(SmbSessionFactory.class.isAssignableFrom(sessionFactory.getClass())); } - @Test(timeout = 10000) - public void cachingSessionFactoryByDefault() throws Exception{ + @Test + public void testNoCachingSessionFactoryByDefault() throws Exception { SourcePollingChannelAdapter adapter = applicationContext.getBean("simpleAdapter", SourcePollingChannelAdapter.class); - Object sessionFactory = TestUtils.getPropertyValue(adapter, "source.synchronizer.sessionFactory"); - assertEquals(CachingSessionFactory.class, sessionFactory.getClass()); + Object sessionFactory = TestUtils.getPropertyValue(adapter, "source.synchronizer.remoteFileTemplate.sessionFactory"); + assertThat(sessionFactory, instanceOf(SmbSessionFactory.class)); SmbInboundFileSynchronizer fisync = - TestUtils.getPropertyValue(adapter, "source.synchronizer", SmbInboundFileSynchronizer.class); + 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{ + public void testSmbInboundChannelAdapterCompleteNoId() throws Exception { Map spcas = applicationContext.getBeansOfType(SourcePollingChannelAdapter.class); SourcePollingChannelAdapter adapter = null; for (String key : spcas.keySet()) { - if (!key.equals("smbInbound") && !key.equals("simpleAdapter")){ + if (!key.equals("smbInbound") && !key.equals("simpleAdapter")) { adapter = spcas.get(key); } } 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 index d2b4468..16c6f21 100644 --- 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 @@ -1,5 +1,5 @@ -/** - * Copyright 2002-2013 the original author or authors. +/* + * Copyright 2012-2017 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. @@ -13,25 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.smb.config; import static org.junit.Assert.assertTrue; import java.io.File; +import org.junit.Ignore; 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.AbstractBaseTests; 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; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.support.GenericMessage; /** * System tests that perform SMB access without any mocking. @@ -43,12 +46,12 @@ import org.springframework.integration.test.util.TestUtils; * @author Markus Spann * @author Gunnar Hillert */ -public class SmbInboundOutboundSample extends AbstractBaseTest { +public class SmbInboundOutboundSample extends AbstractBaseTests { 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 + "].") + @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/"; @@ -71,7 +74,8 @@ public class SmbInboundOutboundSample extends AbstractBaseTest { 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]); + smbSession.write(("File [" + fileNames[i] + "] written by test case [" + getMethodName() + "].").getBytes(), + testRemoteDir + fileNames[i]); } // allow time for the files to arrive locally @@ -84,7 +88,7 @@ public class SmbInboundOutboundSample extends AbstractBaseTest { } - @org.junit.Ignore("Actual SMB share must be configured in file [" + OUTBOUND_APPLICATION_CONTEXT_XML + "].") + @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/"; @@ -93,7 +97,8 @@ public class SmbInboundOutboundSample extends AbstractBaseTest { 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]); + writeToFile(("File [" + fileNames[i] + "] written by test case [" + getMethodName() + "].").getBytes(), + testLocalDir + fileNames[i]); } ApplicationContext ac = new ClassPathXmlApplicationContext(OUTBOUND_APPLICATION_CONTEXT_XML, this.getClass()); @@ -131,7 +136,7 @@ public class SmbInboundOutboundSample extends AbstractBaseTest { return fileNames; } - public static void main(String[] _args) { + public static void main(String[] _args) throws Exception { runTests(SmbInboundOutboundSample.class, "testSmbOutboundChannelAdapter", "testSmbInboundChannelAdapter"); } 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 index eaac811..9b73422 100644 --- 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 @@ -1,5 +1,5 @@ -/** - * Copyright 2002-2013 the original author or authors. +/* + * Copyright 2012-2017 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. @@ -13,33 +13,34 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.smb.config; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; 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.AbstractBaseTests; import org.springframework.integration.smb.session.SmbSessionFactory; import org.springframework.integration.test.util.TestUtils; +import org.springframework.messaging.MessageHandler; /** * @author Markus Spann * @author Gunnar Hillert - * @since 1.0 + * @author Artem Bilan */ -public class SmbOutboundChannelAdapterParserTests extends AbstractBaseTest { +public class SmbOutboundChannelAdapterParserTests extends AbstractBaseTests { @Test public void testSmbOutboundChannelAdapterComplete() throws Exception { @@ -53,15 +54,14 @@ public class SmbOutboundChannelAdapterParserTests extends AbstractBaseTest { assertEquals("smbOutboundChannelAdapter", ((EventDrivenConsumer) consumer).getComponentName()); Object messageHandler = TestUtils.getPropertyValue(consumer, "handler"); - String remoteFileSeparator = (String) TestUtils.getPropertyValue(messageHandler, "remoteFileSeparator"); + String remoteFileSeparator = (String) TestUtils.getPropertyValue(messageHandler, "remoteFileTemplate.remoteFileSeparator"); assertNotNull(remoteFileSeparator); - assertEquals(".working.tmp", TestUtils.getPropertyValue(messageHandler, "temporaryFileSuffix", String.class)); + assertEquals(".working.tmp", TestUtils.getPropertyValue(messageHandler, "remoteFileTemplate.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")); + assertEquals(ac.getBean("fileNameGenerator"), TestUtils.getPropertyValue(messageHandler, "remoteFileTemplate.fileNameGenerator")); + assertEquals("UTF-8", TestUtils.getPropertyValue(messageHandler, "remoteFileTemplate.charset")); - Object sessionFactoryProp = TestUtils.getPropertyValue(messageHandler, "sessionFactory"); + Object sessionFactoryProp = TestUtils.getPropertyValue(messageHandler, "remoteFileTemplate.sessionFactory"); assertEquals(SmbSessionFactory.class, sessionFactoryProp.getClass()); SmbSessionFactory smbSessionFactory = (SmbSessionFactory) sessionFactoryProp; @@ -71,20 +71,20 @@ public class SmbOutboundChannelAdapterParserTests extends AbstractBaseTest { // verify subscription order @SuppressWarnings("unchecked") - Set handlers = (Set) TestUtils.getPropertyValue(TestUtils.getPropertyValue(channel, "dispatcher"), "handlers"); + Set handlers = (Set) TestUtils.getPropertyValue( + TestUtils.getPropertyValue(channel, "dispatcher"), "handlers"); Iterator iterator = handlers.iterator(); - assertSame(TestUtils.getPropertyValue(ac.getBean("smbOutboundChannelAdapter2"), "handler"), iterator.next()); + assertSame(TestUtils.getPropertyValue(ac.getBean("smbOutboundChannelAdapter2"), "handler"), + iterator.next()); assertSame(messageHandler, iterator.next()); } @Test - public void cachingByDefault() { + public void noCachingByDefault() { 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()); + Object sfProperty = TestUtils.getPropertyValue(adapter, "handler.remoteFileTemplate.sessionFactory"); + assertEquals(SmbSessionFactory.class, sfProperty.getClass()); } } 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/SmbInboundRemoteFileSystemSynchronizerTests.java similarity index 90% rename from spring-integration-smb/src/test/java/org/springframework/integration/smb/inbound/SmbInboundRemoteFileSystemSynchronizerTest.java rename to spring-integration-smb/src/test/java/org/springframework/integration/smb/inbound/SmbInboundRemoteFileSystemSynchronizerTests.java index 998cc05..c577d4d 100644 --- 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/SmbInboundRemoteFileSystemSynchronizerTests.java @@ -1,5 +1,5 @@ -/** - * Copyright 2002-2013 the original author or authors. +/* + * Copyright 2012-2017 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. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.smb.inbound; import static org.mockito.Mockito.doAnswer; @@ -24,27 +25,31 @@ import java.io.OutputStream; import java.util.ArrayList; import java.util.List; -import jcifs.smb.SmbFile; - import org.junit.Before; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; -import org.springframework.integration.smb.AbstractBaseTest; + +import org.springframework.integration.smb.AbstractBaseTests; import org.springframework.integration.smb.session.SmbSession; import org.springframework.integration.smb.session.SmbSessionFactory; +import jcifs.smb.SmbFile; + /** * @author Markus Spann * @author Gunnar Hillert * @since 1.0 */ -public class SmbInboundRemoteFileSystemSynchronizerTest extends AbstractBaseTest { +public class SmbInboundRemoteFileSystemSynchronizerTests extends AbstractBaseTests { + + private SmbSession smbSession; - private SmbSession smbSession; private SmbSessionFactory smbSessionFactory; - private String testLocalDir = "test-temp/local-9/"; - private String testRemoteDir = "test-temp/remote-9/"; + + private String testLocalDir = "test-temp/local-9/"; + + private String testRemoteDir = "test-temp/remote-9/"; @Before public void prepare() { @@ -105,6 +110,7 @@ public class SmbInboundRemoteFileSystemSynchronizerTest extends AbstractBaseTest smbFiles.add(file); doAnswer(new Answer() { + public Object answer(InvocationOnMock _invocation) throws Throwable { String path = (String) _invocation.getArguments()[0]; OutputStream os = (OutputStream) _invocation.getArguments()[1]; @@ -114,11 +120,12 @@ public class SmbInboundRemoteFileSystemSynchronizerTest extends AbstractBaseTest }).when(smbSession).read(Mockito.eq(testRemoteDir + "/" + fileName), Mockito.any(OutputStream.class)); } - when(smbSession.list(testRemoteDir)).thenReturn(smbFiles.toArray(new SmbFile[] {})); + when(smbSession.list(testRemoteDir)).thenReturn(smbFiles.toArray(new SmbFile[] { })); when(smbSession.remove(Mockito.anyString())).thenReturn(true); return smbSession; - } catch (Exception _ex) { + } + catch (Exception _ex) { throw new RuntimeException("Failed to create mock session.", _ex); } } diff --git a/spring-integration-smb/src/test/java/org/springframework/integration/smb/outbound/SmbSendingMessageHandlerTest.java b/spring-integration-smb/src/test/java/org/springframework/integration/smb/outbound/SmbSendingMessageHandlerTests.java similarity index 76% rename from spring-integration-smb/src/test/java/org/springframework/integration/smb/outbound/SmbSendingMessageHandlerTest.java rename to spring-integration-smb/src/test/java/org/springframework/integration/smb/outbound/SmbSendingMessageHandlerTests.java index 4904535..d2b6007 100644 --- a/spring-integration-smb/src/test/java/org/springframework/integration/smb/outbound/SmbSendingMessageHandlerTest.java +++ b/spring-integration-smb/src/test/java/org/springframework/integration/smb/outbound/SmbSendingMessageHandlerTests.java @@ -1,5 +1,5 @@ -/** - * Copyright 2002-2012 the original author or authors. +/* + * Copyright 2012-2017 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. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.smb.outbound; import static org.mockito.Mockito.doAnswer; @@ -24,28 +25,30 @@ import java.io.File; import java.io.InputStream; import java.io.OutputStream; -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.beans.factory.BeanFactory; import org.springframework.expression.common.LiteralExpression; -import org.springframework.integration.Message; -import org.springframework.integration.file.FileNameGenerator; 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.AbstractBaseTests; import org.springframework.integration.smb.session.SmbSession; import org.springframework.integration.smb.session.SmbSessionFactory; +import org.springframework.messaging.support.GenericMessage; + +import jcifs.smb.SmbFile; /** * @author Markus Spann + * @author Artem Bilan */ -public class SmbSendingMessageHandlerTest extends AbstractBaseTest { +public class SmbSendingMessageHandlerTests extends AbstractBaseTests { + + private SmbSession smbSession; - private SmbSession smbSession; private SmbSessionFactory smbSessionFactory; @Before @@ -66,12 +69,9 @@ public class SmbSendingMessageHandlerTest extends AbstractBaseTest { File file = createNewFile("remote-target-dir/handlerContent.test"); FileTransferringMessageHandler handler = new FileTransferringMessageHandler(smbSessionFactory); handler.setRemoteDirectoryExpression(new LiteralExpression("remote-target-dir")); - handler.setFileNameGenerator(new FileNameGenerator() { - public String generateFileName(Message message) { - return "handlerContent.test"; - } - }); + handler.setFileNameGenerator(message -> "handlerContent.test"); handler.setAutoCreateDirectory(true); + handler.setBeanFactory(mock(BeanFactory.class)); handler.afterPropertiesSet(); handler.handleMessage(new GenericMessage("hello")); assertFileExists(file); @@ -82,11 +82,8 @@ public class SmbSendingMessageHandlerTest extends AbstractBaseTest { File file = createNewFile("remote-target-dir/handlerContent.test"); FileTransferringMessageHandler handler = new FileTransferringMessageHandler(smbSessionFactory); handler.setRemoteDirectoryExpression(new LiteralExpression("remote-target-dir")); - handler.setFileNameGenerator(new FileNameGenerator() { - public String generateFileName(Message message) { - return "handlerContent.test"; - } - }); + handler.setFileNameGenerator(message -> "handlerContent.test"); + handler.setBeanFactory(mock(BeanFactory.class)); handler.afterPropertiesSet(); handler.handleMessage(new GenericMessage("hello".getBytes())); assertFileExists(file); @@ -116,6 +113,7 @@ public class SmbSendingMessageHandlerTest extends AbstractBaseTest { when(smbSession.list(Mockito.anyString())).thenReturn(new SmbFile[0]); doAnswer(new Answer() { + public Object answer(InvocationOnMock _invocation) throws Throwable { String path = (String) _invocation.getArguments()[0]; OutputStream os = (OutputStream) _invocation.getArguments()[1]; @@ -124,20 +122,19 @@ public class SmbSendingMessageHandlerTest extends AbstractBaseTest { } }).when(smbSession).read(Mockito.anyString(), Mockito.any(OutputStream.class)); - doAnswer(new Answer() { - public Object answer(InvocationOnMock _invocation) throws Throwable { - 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()); + 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() { + public Object answer(InvocationOnMock _invocation) throws Throwable { String path = (String) _invocation.getArguments()[0]; new File(path).mkdirs(); @@ -145,20 +142,20 @@ public class SmbSendingMessageHandlerTest extends AbstractBaseTest { } }).when(smbSession).mkdir(Mockito.anyString()); - doAnswer(new Answer() { - public Object answer(InvocationOnMock _invocation) throws Throwable { - 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()); + 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) { + } + catch (Exception _ex) { throw new RuntimeException("Failed to create mock session.", _ex); } } diff --git a/spring-integration-smb/src/test/java/org/springframework/integration/smb/session/SmbSessionTest.java b/spring-integration-smb/src/test/java/org/springframework/integration/smb/session/SmbSessionTests.java similarity index 94% rename from spring-integration-smb/src/test/java/org/springframework/integration/smb/session/SmbSessionTest.java rename to spring-integration-smb/src/test/java/org/springframework/integration/smb/session/SmbSessionTests.java index 0127c9f..9a2e3b2 100644 --- a/spring-integration-smb/src/test/java/org/springframework/integration/smb/session/SmbSessionTest.java +++ b/spring-integration-smb/src/test/java/org/springframework/integration/smb/session/SmbSessionTests.java @@ -1,5 +1,5 @@ -/** - * Copyright 2002-2013 the original author or authors. +/* + * Copyright 2012-2017 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. @@ -13,26 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.smb.session; +import static org.junit.Assert.assertEquals; + import java.io.IOException; -import jcifs.smb.SmbFile; -import static org.junit.Assert.assertEquals; - import org.junit.Test; +import jcifs.smb.SmbFile; + /** * * @author Gunnar Hillert * */ -public class SmbSessionTest { +public class SmbSessionTests { - /** - * Test Case reproduces INTEXT-37 (https://jira.springsource.org/browse/INTEXT-37) - * @throws IOException - */ @Test public void testCreateSmbFileObjectWithBackSlash1() throws IOException { diff --git a/spring-integration-smb/src/test/resources/log4j.properties b/spring-integration-smb/src/test/resources/log4j.properties index 0f8376e..4e677ad 100644 --- a/spring-integration-smb/src/test/resources/log4j.properties +++ b/spring-integration-smb/src/test/resources/log4j.properties @@ -4,6 +4,5 @@ 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 -log4j.category.org.springframework.integration.smb=DEBUG +log4j.category.org.springframework.integration=WARN +log4j.category.org.springframework.integration.smb=WARN

For further API reference and developer documentation, see the - Spring + Spring Integration reference documentation. That documentation contains more detailed, developer-targeted descriptions, with conceptual overviews, definitions of terms, @@ -14,8 +14,8 @@ This document is the API specification for Spring Integration

If you are interested in commercial training, consultancy, and - support for Spring Integration, please visit - http://www.springsource.com + support for Spring Integration, please visit + https://spring.io