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:
committed by
Artem Bilan
parent
0cf1dfee7e
commit
7174e8840b
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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`.
|
||||
|
||||
Reference in New Issue
Block a user