Add XSD for new SMB components

* Add namespace handler support for new XSD configs
* Add JUnit tests for outbound gateway XML config
* Add JUnit tests for streaming inbound adapter XML config
* Add Javadoc to new parser classes as per PR feedback
* Some code clean up
This commit is contained in:
Gregory Bragg
2022-05-27 11:02:26 -04:00
committed by Artem Bilan
parent 0cf1dfee7e
commit 7174e8840b
9 changed files with 1196 additions and 118 deletions

View File

@@ -23,6 +23,7 @@ import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHa
*
* @author Markus Spann
* @author Artem Bilan
* @author Gregory Bragg
*
* @since 6.0
*/
@@ -30,7 +31,9 @@ public class SmbNamespaceHandler extends AbstractIntegrationNamespaceHandler {
public void init() {
registerBeanDefinitionParser("inbound-channel-adapter", new SmbInboundChannelAdapterParser());
registerBeanDefinitionParser("inbound-streaming-channel-adapter", new SmbStreamingInboundChannelAdapterParser());
registerBeanDefinitionParser("outbound-channel-adapter", new SmbOutboundChannelAdapterParser());
registerBeanDefinitionParser("outbound-gateway", new SmbOutboundGatewayParser());
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.smb.config;
import org.springframework.integration.file.config.AbstractRemoteFileOutboundGatewayParser;
import org.springframework.integration.file.remote.RemoteFileOperations;
import org.springframework.integration.smb.filters.SmbRegexPatternFileListFilter;
import org.springframework.integration.smb.filters.SmbSimplePatternFileListFilter;
import org.springframework.integration.smb.outbound.SmbOutboundGateway;
import org.springframework.integration.smb.session.SmbRemoteFileTemplate;
/**
* Parser for the SMB 'outbound-gateway' element.
*
* @author Gregory Bragg
*
* @since 6.0
*/
public class SmbOutboundGatewayParser extends AbstractRemoteFileOutboundGatewayParser {
@Override
public String getGatewayClassName() {
return SmbOutboundGateway.class.getName();
}
@Override
protected String getSimplePatternFileListFilterClassName() {
return SmbSimplePatternFileListFilter.class.getName();
}
@Override
protected String getRegexPatternFileListFilterClassName() {
return SmbRegexPatternFileListFilter.class.getName();
}
@Override
protected Class<? extends RemoteFileOperations<?>> getTemplateClass() {
return SmbRemoteFileTemplate.class;
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.smb.config;
import org.springframework.integration.core.MessageSource;
import org.springframework.integration.file.config.AbstractRemoteFileStreamingInboundChannelAdapterParser;
import org.springframework.integration.file.filters.AbstractPersistentAcceptOnceFileListFilter;
import org.springframework.integration.file.filters.FileListFilter;
import org.springframework.integration.file.remote.RemoteFileOperations;
import org.springframework.integration.smb.filters.SmbPersistentAcceptOnceFileListFilter;
import org.springframework.integration.smb.filters.SmbRegexPatternFileListFilter;
import org.springframework.integration.smb.filters.SmbSimplePatternFileListFilter;
import org.springframework.integration.smb.inbound.SmbStreamingMessageSource;
import org.springframework.integration.smb.session.SmbRemoteFileTemplate;
/**
* Parser for the SMB 'inbound-streaming-channel-adapter' element.
*
* @author Gregory Bragg
*
* @since 6.0
*/
public class SmbStreamingInboundChannelAdapterParser extends AbstractRemoteFileStreamingInboundChannelAdapterParser {
@Override
protected Class<? extends RemoteFileOperations<?>> getTemplateClass() {
return SmbRemoteFileTemplate.class;
}
@Override
protected Class<? extends MessageSource<?>> getMessageSourceClass() {
return SmbStreamingMessageSource.class;
}
@Override
protected Class<? extends FileListFilter<?>> getSimplePatternFileListFilterClass() {
return SmbSimplePatternFileListFilter.class;
}
@Override
protected Class<? extends FileListFilter<?>> getRegexPatternFileListFilterClass() {
return SmbRegexPatternFileListFilter.class;
}
@Override
protected Class<? extends AbstractPersistentAcceptOnceFileListFilter<?>> getPersistentAcceptOnceFileListFilterClass() {
return SmbPersistentAcceptOnceFileListFilter.class;
}
}

View File

@@ -3,77 +3,49 @@
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:integration="http://www.springframework.org/schema/integration"
xmlns:int-file="http://www.springframework.org/schema/integration/file"
targetNamespace="http://www.springframework.org/schema/integration/smb"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
elementFormDefault="qualified">
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
<xsd:import namespace="http://www.springframework.org/schema/tool"/>
<xsd:import namespace="http://www.springframework.org/schema/integration"
schemaLocation="https://www.springframework.org/schema/integration/spring-integration.xsd"/>
schemaLocation="https://www.springframework.org/schema/integration/spring-integration.xsd"/>
<xsd:import namespace="http://www.springframework.org/schema/integration/file"
schemaLocation="https://www.springframework.org/schema/integration/file/spring-integration-file.xsd"/>
<xsd:annotation>
<xsd:documentation><![CDATA[
<xsd:annotation>
<xsd:documentation><![CDATA[
The handler for namespace 'http://www.springframework.org/schema/integration/smb'
is set to 'org.springframework.integration.smb.config.SmbNamespaceHandler'
in file 'spring.handlers'. SmbNamespaceHandler sets the implementation
of 'inbound-channel-adapter' to class 'SmbInboundChannelAdapterParser'
etc.
]]></xsd:documentation>
</xsd:annotation>
</xsd:annotation>
<xsd:element name="outbound-channel-adapter">
<xsd:element name="outbound-channel-adapter">
<xsd:annotation>
<xsd:documentation><![CDATA[
Builds an outbound-channel-adapter that writes files to a remote
SMB endpoint.
SMB endpoint. Configures a Consumer Endpoint for the
'org.springframework.integration.smb.outbound.SmbMessageHandler'
used to issue SMB commands.
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="base-smb-adapter-type">
<xsd:extension base="base-outbound-adapter-type">
<xsd:all>
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element ref="integration:poller" minOccurs="0"/>
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType"
minOccurs="0"/>
</xsd:all>
<xsd:attribute name="remote-directory" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Identifies directory path (e.g., "/temp/mytransfers/")
where file will be transferred to.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="remote-directory-expression" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Allows you to provide SpEL expression which will
compute directory path where file will be
transferred to (e.g., "headers.['remote_dir'] + '/myTransfers'");
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="remote-file-separator" type="xsd:string" default="/">
<xsd:annotation>
<xsd:documentation>
Allows you to provide remote file/directory separator
character. DEFAULT: '/'
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="temporary-file-suffix" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Extension used when uploading files. We change
it right after we know it's uploaded.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="remote-filename-generator" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Allows you to specify a reference to a
[org.springframework.integration.file.FileNameGenerator] bean.
'org.springframework.integration.file.FileNameGenerator' bean.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
@@ -87,7 +59,7 @@
<xsd:documentation>
Allows you to provide SpEL expression which will
compute file name of the remote file (e.g., assuming
payload is java.io.File "payload.getName() + '.transfered'");
payload is java.io.File "payload.getName() + '.transferred'");
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@@ -106,6 +78,14 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="charset" type="xsd:string" default="UTF-8">
<xsd:annotation>
<xsd:documentation>
Allows you to specify Charset (e.g., US-ASCII, ISO-8859-1, UTF-8).
[UTF-8] is the default.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -116,36 +96,17 @@
<xsd:documentation><![CDATA[
Builds an inbound-channel-adapter that synchronizes a local directory
with the contents of a remote SMB endpoint. The adapter requires
either no or exactly one file selection pattern (may be simple
pattern or regular expression).
either no or exactly one file selection pattern (may be simple pattern or regular expression).
Creates a 'SourcePollingChannelAdapter' based on the
'org.springframework.integration.smb.inbound.SmbInboundFileSynchronizingMessageSource'.
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="base-smb-adapter-type">
<xsd:extension base="base-inbound-adapter-type">
<xsd:sequence>
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
<xsd:element ref="integration:poller" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="filename-pattern" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Allows you to provide file name pattern to determine
the file names that needs to be scanned and is
based on simple pattern matching algorithm
(e.g., "*.txt, fo*.txt" etc.)
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="filename-regex" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Allows you to provide Regular Expression to determine
the file names that needs to be scanned. (e.g., "f[o]+\.txt" etc.)
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="comparator" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
@@ -156,49 +117,12 @@
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="filter" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.file.filters.FileListFilter"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
Allows you to specify a reference to
[org.springframework.integration.file.filters.FileListFilter] bean.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="temporary-file-suffix" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Extension used when downloading files. We change
it right after we know it's downloaded.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="remote-directory" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation>
Identifies directory path (e.g., "/temp/mytransfers")
where file will be transferred FROM.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="remote-file-separator" type="xsd:string" default="/">
<xsd:annotation>
<xsd:documentation>
Allows you to provide remote file/directory separator
character. DEFAULT: '/'
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="local-filter" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type
type="org.springframework.integration.file.filters.FileListFilter" />
type="org.springframework.integration.file.filters.FileListFilter"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
@@ -229,7 +153,7 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="delete-remote-files" type="xsd:string">
<xsd:attribute name="delete-remote-files" type="xsd:string" default="false">
<xsd:annotation>
<xsd:documentation>
Specify whether to delete the remote source file after copying.
@@ -237,33 +161,476 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="charset" type="xsd:string" default="UTF-8">
<xsd:annotation>
<xsd:documentation>
Allows you to specify Charset (e.g., US-ASCII, ISO-8859-1, UTF-8).
[UTF-8] is the default.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attributeGroup ref="tempSuffixGroup"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="base-smb-adapter-type">
<xsd:element name="inbound-streaming-channel-adapter">
<xsd:annotation>
<xsd:documentation>
Configures a 'SourcePollingChannelAdapter' Endpoint for the
'org.springframework.integration.smb.inbound.SmbInboundStreamingMessageSource'.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="base-inbound-adapter-type">
<xsd:attribute name="comparator" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Specify a Comparator to be used when ordering Files. If none is provided, the
order in which files are processed is the order they are received from the
SMB server. The generic type of the Comparator must be 'SmbFileInfo'.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="outbound-gateway">
<xsd:annotation>
<xsd:documentation>
Configures a Consumer Endpoint for the
'org.springframework.integration.smb.outbound.SmbOutboundGateway'
used to issue SMB commands.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="base-outbound-adapter-type">
<xsd:all>
<xsd:element ref="integration:poller" minOccurs="0"/>
<xsd:element name="transactional" type="integration:transactionalType" minOccurs="0"/>
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType"
minOccurs="0"/>
</xsd:all>
<xsd:attribute name="command">
<xsd:annotation>
<xsd:documentation>
SMB command.
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:union memberTypes="int-file:remoteGatewayCommand xsd:string"/>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="session-callback" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type
type="org.springframework.integration.file.remote.MessageSessionCallback"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
The 'MessageSessionCallback' bean reference to perform custom operation(s) on 'Session'
with 'requestMessage'.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="command-options" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
SMB command options; for ls, -1 means just
return the file names
(otherwise file
metadata is returned, -dirs
means include directories (not included by
default),
-links means
include links (not included by default); for get, -P means
preserve
timestamp from remote file.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="expression" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
SpEL expression representing the path in the
command (e.g. ls path to list the files in directory path).
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="rename-expression" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
SpEL expression representing the path for the
new filename when using the 'mv' command.
Defaults to "headers.['file_renameTo']".
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="request-channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.messaging.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
Identifies the request channel attached to this gateway.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="reply-channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.messaging.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
Identifies the reply channel attached to this gateway.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="reply-timeout" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Allows you to specify how long this gateway will wait for
the reply message to be sent successfully to the reply channel
before throwing an exception. This attribute only applies when the
channel might block, for example when using a bounded queue channel that
is currently full.
Also, keep in mind that when sending to a DirectChannel, the
invocation will occur in the sender's thread. Therefore,
the failing of the send operation may be caused by other
components further downstream.
The "reply-timeout" attribute maps to the "sendTimeout" property of the
underlying 'MessagingTemplate' instance.
The attribute will default, if not specified, to '-1', meaning that
by default, the Gateway will wait indefinitely. The value is
specified in milliseconds.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="filter" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type
type="org.springframework.integration.file.filters.FileListFilter"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
Allows you to specify a reference to
[org.springframework.integration.file.filters.FileListFilter]
bean. This filter acts against the remote server view when using the 'ls'
or 'mget' commands.
Only one of 'filter', 'filename-pattern', or 'filename-regex' is allowed.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="filter-expression" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The SpEL expression to evaluate against file to accept it for processing or not.
Mutually exclusive with 'filter' attribute.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="filename-pattern" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Allows you to provide file name pattern to
determine the file names retrieved by the 'ls' and 'mget' commands
and is based on simple pattern matching algorithm (e.g., "*.txt, fo*.txt" etc.)
Only one of 'filter', 'filename-pattern', or 'filename-regex' is allowed.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="filename-regex" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Allows you to provide Regular Expression to
determine the file names retrieved by the 'ls' and 'mget' commands.
(e.g., "f[o]+\.txt" etc.)
Only one of 'filter', 'filename-pattern', or 'filename-regex' is allowed.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="mput-filter" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type
type="org.springframework.integration.file.filters.FileListFilter"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
Allows you to specify a reference to
[org.springframework.integration.file.filters.FileListFilter]
bean. This filter acts on the local file system when using the 'mput' command.
Only one of 'mput-filter', 'mput-pattern', or 'mput-regex' is allowed.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="mput-filter-expression" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The SpEL expression to evaluate against file to accept it for processing or not.
Mutually exclusive with 'mput-filter' attribute.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="mput-pattern" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Allows you to provide file name pattern to
determine the file names sent by the 'mput' command
and is based on simple pattern matching algorithm (e.g., "*.txt, fo*.txt" etc.)
Only one of 'mput-filter', 'mput-pattern', or 'mput-regex' is allowed.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="mput-regex" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Allows you to provide Regular Expression to
determine the file names sent by the 'mput' command (e.g., "f[o]+\.txt" etc.)
Only one of 'mput-filter', 'mput-pattern', or 'mput-regex' is allowed.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="local-filename-generator-expression" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Allows you to provide a SpEL expression to
generate the file name of the local (transferred) file.
The root object of the SpEL
evaluation is the request Message, but the name of the original
remote file is also provided as the 'remoteFileName' variable.
For example, a valid expression would be:
"#remoteFileName.toUpperCase() + headers.foo".
Only used with 'get' and 'mget' commands.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="local-directory" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Identifies directory path (e.g., "/local/mytransfers") where file will be
transferred TO.
This attribute is mutually exclusive with 'local-directory-expression'.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="local-directory-expression" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Specifies SpEL expression to
generate the directory path where file will be
transferred TO, when using 'get' and 'mget' commands.
The root object of the SpEL evaluation is the request Message,
but the name of the source
remote directory is also provided as the 'remoteDirectory' variable.
For example, a valid expression might be:
"'/local/' + #remoteDirectory.toUpperCase() + headers.foo".
Only used with 'get' and 'mget' commands.
This attribute is mutually exclusive with 'local-directory'.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="auto-create-local-directory" type="xsd:boolean">
<xsd:annotation>
<xsd:documentation>
Tells this adapter if local directory must be auto-created if it doesn't exist.
Default is TRUE.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="order" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Specifies the order for invocation when this
endpoint is connected as a subscriber to a channel.
This is particularly relevant when that channel is using a "failover"
dispatching strategy, or when a failure in the delivery to one
subscriber should signal that the message should not be sent to
subscribers with a higher 'order' attribute.
It has no effect when this endpoint itself is a Polling Consumer for a channel with
a queue.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="requires-reply" type="xsd:string" use="optional" default="true">
<xsd:annotation>
<xsd:documentation>
Specify whether this outbound gateway must return a non-null value. This value is
'true' by default, and a ReplyRequiredException will be thrown when
the underlying service returns a null value.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attributeGroup ref="int-file:remoteOutboundAttributeGroup"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="base-inbound-adapter-type">
<xsd:complexContent>
<xsd:extension base="base-adapter-type">
<xsd:sequence>
<xsd:element ref="integration:poller" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.messaging.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
Identifies channel attached to this adapter.
The channel to which messages will be sent by this adapter.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="filename-pattern" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Allows you to provide a file name pattern to
determine the file names that need to be scanned.
This is based on simple pattern matching (e.g., "*.txt, fo*.txt" etc.)
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="filename-regex" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Allows you to provide a Regular Expression to
determine the file names that need to be scanned (e.g., "f[o]+\.txt" etc.)
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="filter" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type
type="org.springframework.integration.file.filters.FileListFilter"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
Allows you to specify a reference to a
[org.springframework.integration.file.filters.FileListFilter]
bean. This filter is applied to files on the remote server and
only files that pass the filter are retrieved.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="filter-expression" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The SpEL expression to evaluate against file to accept it for processing or not.
Mutually exclusive with 'filter' attribute.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attributeGroup ref="integration:maxFetchGroup"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="base-outbound-adapter-type">
<xsd:complexContent>
<xsd:extension base="base-adapter-type">
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.messaging.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
Identifies channel attached to this adapter.
The channel to which messages will be sent by this adapter.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="temporary-remote-directory" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Identifies the remote temporary directory path (e.g., "/remote/temp/mytransfers")
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attributeGroup ref="tempSuffixGroup"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="base-adapter-type">
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="session-factory" type="xsd:string" use="required">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.smb.session.SmbSessionFactory"/>
<tool:expected-type
type="org.springframework.integration.file.remote.session.SessionFactory"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation><![CDATA[
Reference to a org.springframework.integration.smb.session.SmbSessionFactory bean.
Reference to an 'org.springframework.integration.file.remote.session.SessionFactory' bean with
an 'jcifs.smb.SmbFile' generic type parameter.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="charset" type="xsd:string" default="UTF-8">
<xsd:annotation>
<xsd:attribute name="remote-file-separator" type="xsd:string" default="/">
<xsd:annotation>
<xsd:documentation>
Allows you to specify Charset (e.g., US-ASCII, ISO-8859-1, UTF-8).
[UTF-8] is the default.
Allows you to provide remote file/directory separator character. DEFAULT: '/'
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attributeGroup ref="integration:channelAdapterAttributes" />
<xsd:attribute name="remote-directory" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Identifies the remote directory path (e.g., "/remote/mytransfers")
Mutually exclusive with 'remote-directory-expression'.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="remote-directory-expression" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Specify a SpEL expression which
will be used to evaluate the directory
path to where the files will be transferred
(e.g., "headers.['remote_dir'] + '/myTransfers'" for outbound endpoints)
There is no root object (message) for inbound endpoints
(e.g., "@someBean.fetchDirectory");
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attributeGroup ref="integration:smartLifeCycleAttributeGroup"/>
</xsd:complexType>
<xsd:attributeGroup name="tempSuffixGroup">
<xsd:attribute name="temporary-file-suffix" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Extension used when downloading files. We change it right after we know it's downloaded.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:attributeGroup>
</xsd:schema>

View File

@@ -0,0 +1,138 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int-smb="http://www.springframework.org/schema/integration/smb"
xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/smb https://www.springframework.org/schema/integration/smb/spring-integration-smb.xsd">
<bean id="sf" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="org.springframework.integration.file.remote.session.SessionFactory"/>
</bean>
<bean id="csf" class="org.springframework.integration.file.remote.session.CachingSessionFactory">
<constructor-arg ref="sf"/>
</bean>
<int:channel id="inbound1">
<int:queue/>
</int:channel>
<int:poller fixed-delay="1000" default="true"/>
<int-smb:outbound-gateway id="gateway1"
local-directory="local-test-dir"
session-factory="sf"
request-channel="inbound1"
reply-channel="outbound"
reply-timeout="777"
auto-create-local-directory="false"
auto-startup="false"
filename-pattern="*"
remote-file-separator="X"
command="ls"
command-options="-1 -f"
expression="payload"
order="1"
mput-regex=".*"
/>
<bean id="fooString" class="java.lang.String">
<constructor-arg value="foo" />
</bean>
<int-smb:outbound-gateway id="gateway2"
local-directory="local-test-dir"
session-factory="csf"
request-channel="inbound2"
reply-channel="outbound"
auto-create-local-directory="false"
auto-startup="false"
remote-file-separator="X"
command="get"
command-options="-P"
expression="payload"
order="2"
requires-reply="false"
local-filename-generator-expression="#remoteFileName.toUpperCase() + '.a' + @fooString"
mput-pattern="*"
/>
<int-smb:outbound-gateway id="gateway3"
session-factory="csf"
request-channel="inbound1"
reply-channel="outbound"
command="mv"
expression="payload"
rename-expression="'foo'"
order="1"
/>
<int-smb:outbound-gateway id="gateway4"
session-factory="csf"
request-channel="inbound1"
reply-channel="outbound"
command="mput"
expression="payload"
remote-directory="/foo"
remote-file-separator="X"
auto-create-directory="true"
remote-filename-generator="fileNameGenerator"
temporary-remote-directory="/bar"
rename-expression="'foo'"
order="1"
mput-filter="mputFilter"
/>
<bean id="fileNameGenerator" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="org.springframework.integration.file.FileNameGenerator"/>
</bean>
<bean id="mputFilter" class="org.springframework.integration.file.filters.RegexPatternFileListFilter">
<constructor-arg value="(.*1.txt|sub*)"/>
</bean>
<int-smb:outbound-gateway id="advised"
local-directory="local-test-dir"
session-factory="sf"
request-channel="inbound2"
reply-channel="outbound"
auto-create-local-directory="false"
auto-startup="false"
remote-file-separator="X"
command="get"
command-options="-P"
expression="payload"
requires-reply="false"
order="2">
<int-smb:request-handler-advice-chain>
<bean class="org.springframework.integration.smb.config.SmbOutboundGatewayParserTests$FooAdvice" />
</int-smb:request-handler-advice-chain>
</int-smb:outbound-gateway>
<int:channel id="outbound"/>
<int-smb:outbound-gateway id="noExpressionLS"
request-channel="nullChannel"
reply-channel="nullChannel"
session-factory="sf"
remote-directory="."
command="ls" />
<int-smb:outbound-gateway id="noExpressionPUT"
request-channel="nullChannel"
reply-channel="nullChannel"
session-factory="sf"
remote-directory="."
command="put" />
<int-smb:outbound-gateway id="noExpressionGET"
request-channel="nullChannel"
reply-channel="nullChannel"
session-factory="sf"
remote-directory="."
local-directory="."
command="get" />
</beans>

View File

@@ -0,0 +1,195 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.smb.config;
import static org.assertj.core.api.Assertions.assertThat;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.Expression;
import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.integration.file.FileNameGenerator;
import org.springframework.integration.file.filters.RegexPatternFileListFilter;
import org.springframework.integration.file.filters.SimplePatternFileListFilter;
import org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway.Command;
import org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway.Option;
import org.springframework.integration.file.remote.session.CachingSessionFactory;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.smb.outbound.SmbOutboundGateway;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.util.ReflectionUtils;
/**
* Unit tests follow original logic for testing SFTP module,
* adapted for SMB module.
*
* @author Gary Russell
* @author Gunnar Hillert
* @author Artem Bilan
* @author Gregory Bragg
*
* @since 6.0
*/
@SpringJUnitConfig
@DirtiesContext
public class SmbOutboundGatewayParserTests {
@Autowired
AbstractEndpoint gateway1;
@Autowired
AbstractEndpoint gateway2;
@Autowired
AbstractEndpoint gateway3;
@Autowired
AbstractEndpoint gateway4;
@Autowired
AbstractEndpoint advised;
@Autowired
AbstractEndpoint noExpressionLS;
@Autowired
AbstractEndpoint noExpressionPUT;
@Autowired
AbstractEndpoint noExpressionGET;
@Autowired
FileNameGenerator generator;
private static volatile int adviceCalled;
@Test
public void testGateway1() {
SmbOutboundGateway gateway = TestUtils.getPropertyValue(gateway1,
"handler", SmbOutboundGateway.class);
assertThat(TestUtils.getPropertyValue(gateway, "remoteFileTemplate.remoteFileSeparator")).isEqualTo("X");
assertThat(TestUtils.getPropertyValue(gateway, "remoteFileTemplate.sessionFactory")).isNotNull();
assertThat(TestUtils.getPropertyValue(gateway, "outputChannel")).isNotNull();
assertThat(TestUtils.getPropertyValue(gateway, "localDirectoryExpression.literalValue"))
.isEqualTo("local-test-dir");
assertThat((Boolean) TestUtils.getPropertyValue(gateway, "autoCreateLocalDirectory")).isFalse();
assertThat(TestUtils.getPropertyValue(gateway, "requiresReply", Boolean.class)).isTrue();
assertThat(TestUtils.getPropertyValue(gateway, "filter")).isNotNull();
assertThat(TestUtils.getPropertyValue(gateway, "command")).isEqualTo(Command.LS);
@SuppressWarnings("unchecked")
Set<Option> options = TestUtils.getPropertyValue(gateway, "options", Set.class);
assertThat(options.contains(Option.NAME_ONLY)).isTrue();
assertThat(options.contains(Option.NOSORT)).isTrue();
Long sendTimeout = TestUtils.getPropertyValue(gateway, "messagingTemplate.sendTimeout", Long.class);
assertThat(sendTimeout).isEqualTo(Long.valueOf(777));
assertThat(TestUtils.getPropertyValue(gateway, "mputFilter")).isInstanceOf(RegexPatternFileListFilter.class);
}
@Test
public void testGateway2() throws Exception {
SmbOutboundGateway gateway = TestUtils.getPropertyValue(gateway2,
"handler", SmbOutboundGateway.class);
assertThat(TestUtils.getPropertyValue(gateway, "remoteFileTemplate.remoteFileSeparator")).isEqualTo("X");
assertThat(TestUtils.getPropertyValue(gateway, "remoteFileTemplate.sessionFactory")).isNotNull();
assertThat(TestUtils
.getPropertyValue(gateway, "remoteFileTemplate.sessionFactory") instanceof CachingSessionFactory)
.isTrue();
assertThat(TestUtils.getPropertyValue(gateway, "outputChannel")).isNotNull();
assertThat(TestUtils.getPropertyValue(gateway, "localDirectoryExpression.literalValue"))
.isEqualTo("local-test-dir");
assertThat((Boolean) TestUtils.getPropertyValue(gateway, "autoCreateLocalDirectory")).isFalse();
assertThat(TestUtils.getPropertyValue(gateway, "command")).isEqualTo(Command.GET);
assertThat(TestUtils.getPropertyValue(gateway, "requiresReply", Boolean.class)).isFalse();
@SuppressWarnings("unchecked")
Set<String> options = TestUtils.getPropertyValue(gateway, "options", Set.class);
assertThat(options.contains(Option.PRESERVE_TIMESTAMP)).isTrue();
//INT-3129
assertThat(TestUtils.getPropertyValue(gateway, "localFilenameGeneratorExpression")).isNotNull();
final AtomicReference<Method> genMethod = new AtomicReference<>();
ReflectionUtils.doWithMethods(SmbOutboundGateway.class, method -> {
method.setAccessible(true);
genMethod.set(method);
}, method -> "generateLocalFileName".equals(method.getName()));
assertThat(genMethod.get().invoke(gateway, new GenericMessage<String>(""), "foo")).isEqualTo("FOO.afoo");
assertThat(TestUtils.getPropertyValue(gateway, "mputFilter")).isInstanceOf(SimplePatternFileListFilter.class);
}
@Test
public void testGatewayMv() {
SmbOutboundGateway gateway = TestUtils.getPropertyValue(gateway3, "handler", SmbOutboundGateway.class);
assertThat(TestUtils.getPropertyValue(gateway, "remoteFileTemplate.sessionFactory")).isNotNull();
assertThat(TestUtils.getPropertyValue(gateway, "outputChannel")).isNotNull();
assertThat(TestUtils.getPropertyValue(gateway, "command")).isEqualTo(Command.MV);
assertThat(TestUtils.getPropertyValue(gateway, "renameProcessor.expression.expression")).isEqualTo("'foo'");
}
@Test
public void testGatewayMPut() {
SmbOutboundGateway gateway = TestUtils.getPropertyValue(gateway4, "handler", SmbOutboundGateway.class);
assertThat(TestUtils.getPropertyValue(gateway, "remoteFileTemplate.sessionFactory")).isNotNull();
assertThat(TestUtils.getPropertyValue(gateway, "outputChannel")).isNotNull();
assertThat(TestUtils.getPropertyValue(gateway, "command")).isEqualTo(Command.MPUT);
assertThat(TestUtils.getPropertyValue(gateway, "renameProcessor.expression.expression")).isEqualTo("'foo'");
assertThat(TestUtils.getPropertyValue(gateway, "mputFilter")).isInstanceOf(RegexPatternFileListFilter.class);
assertThat(TestUtils.getPropertyValue(gateway, "remoteFileTemplate.fileNameGenerator")).isSameAs(generator);
assertThat(TestUtils
.getPropertyValue(gateway, "remoteFileTemplate.directoryExpressionProcessor.expression",
Expression.class)
.getExpressionString()).isEqualTo("/foo");
assertThat(TestUtils
.getPropertyValue(gateway, "remoteFileTemplate.temporaryDirectoryExpressionProcessor.expression",
Expression.class)
.getExpressionString()).isEqualTo("/bar");
}
@Test
public void advised() {
SmbOutboundGateway gateway = TestUtils.getPropertyValue(advised, "handler", SmbOutboundGateway.class);
gateway.handleMessage(new GenericMessage<>("foo"));
assertThat(adviceCalled).isEqualTo(1);
}
@Test
void noExpression() {
assertThat(TestUtils.getPropertyValue(this.noExpressionLS, "handler.fileNameProcessor")).isNull();
assertThat(TestUtils.getPropertyValue(this.noExpressionPUT, "handler.fileNameProcessor")).isNull();
assertThat(TestUtils.getPropertyValue(this.noExpressionGET,
"handler.fileNameProcessor.expression.expression")).isEqualTo("payload");
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) {
adviceCalled++;
return null;
}
}
}

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-smb="http://www.springframework.org/schema/integration/smb"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/smb https://www.springframework.org/schema/integration/smb/spring-integration-smb.xsd">
<bean id="smbSessionFactory"
class="org.springframework.integration.smb.config.SmbStreamingInboundChannelAdapterParserTests$TestSessionFactoryBean"/>
<bean id="csf" class="org.springframework.integration.file.remote.session.CachingSessionFactory">
<constructor-arg ref="smbSessionFactory"/>
</bean>
<int-smb:inbound-streaming-channel-adapter id="smbInbound"
channel="smbChannel"
session-factory="csf"
auto-startup="false"
phase="23"
filename-pattern="*.txt"
remote-file-separator="X"
comparator="comparator"
max-fetch-size="31"
remote-directory-expression="'foo/bar'">
<int:poller fixed-rate="1000" />
</int-smb:inbound-streaming-channel-adapter>
<int:channel id="smbChannel">
<int:queue/>
</int:channel>
<bean id="comparator" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="java.util.Comparator"/>
</bean>
<int-smb:inbound-streaming-channel-adapter id="contextLoadsWithNoComparator"
channel="smbChannel"
session-factory="csf"
auto-startup="false"
phase="23"
filter-expression="new org.springframework.util.AntPathMatcher().match('*.txt', filename)"
remote-file-separator="X"
remote-directory-expression="'foo/bar'">
<int:poller fixed-rate="1000" />
</int-smb:inbound-streaming-channel-adapter>
</beans>

View File

@@ -0,0 +1,122 @@
/*
* Copyright 2016-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.smb.config;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.Iterator;
import java.util.Set;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
import org.springframework.integration.file.filters.CompositeFileListFilter;
import org.springframework.integration.file.filters.ExpressionFileListFilter;
import org.springframework.integration.file.filters.FileListFilter;
import org.springframework.integration.file.remote.session.CachingSessionFactory;
import org.springframework.integration.smb.filters.SmbPersistentAcceptOnceFileListFilter;
import org.springframework.integration.smb.filters.SmbSimplePatternFileListFilter;
import org.springframework.integration.smb.inbound.SmbStreamingMessageSource;
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.test.annotation.DirtiesContext;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
/**
* Unit tests follow original logic for testing SFTP module,
* adapted for SMB module.
*
* @author Gary Russell
* @author Artem Bilan
* @author Gregory Bragg
*
* @since 6.0
*/
@SpringJUnitConfig
@DirtiesContext
public class SmbStreamingInboundChannelAdapterParserTests {
@Autowired
private SourcePollingChannelAdapter smbInbound;
@Autowired
private SourcePollingChannelAdapter contextLoadsWithNoComparator;
@Autowired
private MessageChannel smbChannel;
@Autowired
private CachingSessionFactory<?> csf;
@Test
public void testSmbInboundChannelAdapterComplete() {
assertThat(TestUtils.getPropertyValue(this.smbInbound, "autoStartup", Boolean.class)).isFalse();
assertThat(this.smbInbound.getComponentName()).isEqualTo("smbInbound");
assertThat(this.smbInbound.getComponentType()).isEqualTo("smb:inbound-streaming-channel-adapter");
assertThat(TestUtils.getPropertyValue(this.smbInbound, "outputChannel")).isSameAs(this.smbChannel);
SmbStreamingMessageSource source = TestUtils.getPropertyValue(smbInbound, "source",
SmbStreamingMessageSource.class);
assertThat(TestUtils.getPropertyValue(source, "comparator")).isNotNull();
assertThat(TestUtils.getPropertyValue(source, "remoteFileSeparator", String.class)).isEqualTo("X");
FileListFilter<?> filter = TestUtils.getPropertyValue(source, "filter", FileListFilter.class);
assertThat(filter).isNotNull();
assertThat(filter).isInstanceOf(CompositeFileListFilter.class);
Set<?> fileFilters = TestUtils.getPropertyValue(filter, "fileFilters", Set.class);
Iterator<?> filtersIterator = fileFilters.iterator();
assertThat(filtersIterator.next()).isInstanceOf(SmbSimplePatternFileListFilter.class);
assertThat(filtersIterator.next()).isInstanceOf(SmbPersistentAcceptOnceFileListFilter.class);
assertThat(TestUtils.getPropertyValue(source, "remoteFileTemplate.sessionFactory")).isSameAs(this.csf);
assertThat(TestUtils.getPropertyValue(source, "maxFetchSize")).isEqualTo(31);
source = TestUtils.getPropertyValue(this.contextLoadsWithNoComparator, "source",
SmbStreamingMessageSource.class);
assertThat(TestUtils.getPropertyValue(source, "filter")).isInstanceOf(ExpressionFileListFilter.class);
}
public static class TestSessionFactoryBean implements FactoryBean<SmbSessionFactory> {
@Override
public SmbSessionFactory getObject() {
SmbSessionFactory factory = mock(SmbSessionFactory.class);
SmbSession session = mock(SmbSession.class);
when(factory.getSession()).thenReturn(session);
return factory;
}
@Override
public Class<?> getObjectType() {
return SmbSessionFactory.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
}

View File

@@ -194,6 +194,28 @@ Since the session remains open, the consuming application is responsible for clo
The session is provided in the `closeableResource` header (`IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE`).
Standard framework components, such as the `FileSplitter` and `StreamTransformer`, automatically close the session.
See <<./file.adoc#file-splitter,File Splitter>> and <<./transformer.adoc#stream-transformer,Stream Transformer>> for more information about these components.
The following example shows how to configure an `inbound-streaming-channel-adapter`:
====
[source, xml]
----
<int-smb:inbound-streaming-channel-adapter id="smbInbound"
channel="smbChannel"
session-factory="sessionFactory"
filename-pattern="*.txt"
filename-regex=".*\.txt"
filter="filter"
filter-expression="@myFilterBean.check(#root)"
remote-file-separator="/"
comparator="comparator"
max-fetch-size="1"
remote-directory-expression="'foo/bar'">
<int:poller fixed-rate="1000" />
</int-smb:inbound-streaming-channel-adapter>
----
====
Only one of `filename-pattern`, `filename-regex`, `filter`, or `filter-expression` is allowed.
The `SmbStreamingMessageSource` adapter prevents duplicates for remote files with `SmbPersistentAcceptOnceFileListFilter` based on the in-memory `SimpleMetadataStore`.
By default, this filter is also applied with the filename pattern (or regex).
@@ -449,7 +471,33 @@ if (closeable != null) {
Framework components, such as the <<./file.adoc#file-splitter,File Splitter>> and <<./transformer.adoc#stream-transformer,Stream Transformer>>, automatically close the session after the data is transferred.
The following example shows how to consume a file as a stream:
====
[source, xml]
----
<int-smb:outbound-gateway session-factory="smbSessionFactory"
request-channel="inboundGetStream"
command="get"
command-options="-stream"
expression="payload"
remote-directory="smbTarget"
reply-channel="stream" />
<int-file:splitter input-channel="stream" output-channel="lines" />
----
====
NOTE: If you consume the input stream in a custom component, you must close the `Session`.
You can either do that in your custom code or route a copy of the message to a `service-activator` and use SpEL, as the following example shows:
====
[source, xml]
----
<int:service-activator input-channel="closeSession"
expression="headers['closeableResource'].close()" />
----
====
==== Using the `mget` Command
@@ -501,7 +549,25 @@ IMPORTANT: This means that existing keys in a persistent metadata store will not
For this reason, the property is `false` by default; this may change in a future release.
You can configure the `SmbSimplePatternFileListFilter` and `SmbRegexPatternFileListFilter` to always pass directories by setting the `alwaysAcceptDirectorties` to `true`.
Doing so allows recursion for a simple pattern.
Doing so allows recursion for a simple pattern, as the following examples show:
====
[source, xml]
----
<bean id="starDotTxtFilter"
class="org.springframework.integration.smb.filters.SmbSimplePatternFileListFilter">
<constructor-arg value="*.txt" />
<property name="alwaysAcceptDirectories" value="true" />
</bean>
<bean id="dotStarDotTxtFilter"
class="org.springframework.integration.smb.filters.SmbRegexPatternFileListFilter">
<constructor-arg value="^.*\.txt$" />
<property name="alwaysAcceptDirectories" value="true" />
</bean>
----
====
You can provide one of these filters by using the `filter` property on the gateway.
See also <<smb-partial>>.
@@ -577,6 +643,25 @@ This attribute is mutually exclusive with the `local-directory` attribute.
For all commands, the 'expression' property of the gateway holds the path on which the command acts.
For the `mget` command, the expression might evaluate to `*`, meaning to retrieve all files, `somedirectory/*`, and other values that end with `*`.
The following example shows a gateway configured for an `ls` command:
====
[source,xml]
----
<int-smb:outbound-gateway id="gateway1"
session-factory="smbSessionFactory"
request-channel="inbound1"
command="ls"
command-options="-1"
expression="payload"
reply-channel="toSplitter"/>
----
====
The payload of the message sent to the `toSplitter` channel is a list of `String` objects, each of which contains the name of a file.
If you omitted `command-options="-1"`, the payload would be a list of `FileInfo` objects.
You can provide options as a space-delimited list (for example, `command-options="-1 -dirs -links"`).
The `GET`, `MGET`, `PUT`, and `MPUT` commands support a `FileExistsMode` property (`mode` when using the namespace support).
This affects the behavior when the local file exists (`GET` and `MGET`) or the remote file exists (`PUT` and `MPUT`).
The supported modes are `REPLACE`, `APPEND`, `FAIL`, and `IGNORE`.