GH-3043: Add FileHeaders.REMOTE_HOST header (#3044)

* GH-3043: Add FileHeaders.REMOTE_HOST header

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

* Populate a `FileHeaders.REMOTE_HOST` from the
`AbstractRemoteFileStreamingMessageSource` and "get"-based commands
in the `AbstractRemoteFileOutboundGateway`
* Extract the value from the a `Session.getHost()` contract
* The `AbstractInboundFileSynchronizingMessageSource` cannot be
addressed with this because the real message is already based on the
locally stored file
* Adjust some affected tests according our code style requirements

* * Add remote file info support into `AbstractInboundFileSynchronizingMessageSource`
* Introduce a `MetadataStore` functionality into the `AbstractInboundFileSynchronizer`
to gather a remote file info an save it in the URI style against local file
* Retrieve such an info in the `AbstractInboundFileSynchronizingMessageSource`
during local file polling
* Introduce `protocol()` contract for the `AbstractInboundFileSynchronizer`
to build a proper URI in the metadata for external readers to distinguish
remote files properly
* Document the feature

* * Fix some typos in Docs

* * Rename property and header constant to the `HOST_PORT` pair
* Fix typos in Docs
* Add  `remote-file-metadata-store` and `metadata-store-prefix` into XSD
of (S)FTP Inbound Channel Adapters
* Add `remoteFileMetadataStore` and `metadataStorePrefix` options
into `RemoteFileInboundChannelAdapterSpec` for Java DSL
This commit is contained in:
Artem Bilan
2019-08-28 08:58:14 -04:00
committed by Gary Russell
parent ff15d5265d
commit a756e6334d
33 changed files with 605 additions and 297 deletions

View File

@@ -32,6 +32,7 @@ import org.springframework.integration.metadata.SimpleMetadataStore;
* @author Mark Fisher
* @author Artem Bilan
* @author Gary Russell
*
* @since 2.0
*/
public class FtpInboundFileSynchronizer extends AbstractInboundFileSynchronizer<FTPFile> {
@@ -62,4 +63,9 @@ public class FtpInboundFileSynchronizer extends AbstractInboundFileSynchronizer<
return file.getTimestamp().getTimeInMillis();
}
@Override
protected String protocol() {
return "ftp";
}
}

View File

@@ -38,13 +38,14 @@ import org.springframework.util.ObjectUtils;
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Artem Bilan
*
* @since 2.0
*/
public class FtpSession implements Session<FTPFile> {
private static final String SERVER_REPLIED_WITH = "'. Server replied with: ";
private static final Log LOGGER = LogFactory.getLog(FtpSession.class);
private final Log logger = LogFactory.getLog(this.getClass());
private static final String SERVER_REPLIED_WITH = "'. Server replied with: ";
private final FTPClient client;
@@ -86,7 +87,9 @@ public class FtpSession implements Session<FTPFile> {
throw new IOException("Failed to copy '" + path +
SERVER_REPLIED_WITH + this.client.getReplyString());
}
this.logger.info("File has been successfully transferred from: " + path);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("File has been successfully transferred from: " + path);
}
}
@Override
@@ -109,8 +112,8 @@ public class FtpSession implements Session<FTPFile> {
}
if (this.client.completePendingCommand()) {
int replyCode = this.client.getReplyCode();
if (this.logger.isDebugEnabled()) {
this.logger.debug(this + " finalizeRaw - reply code: " + replyCode);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(this + " finalizeRaw - reply code: " + replyCode);
}
return FTPReply.isPositiveCompletion(replyCode);
}
@@ -126,8 +129,8 @@ public class FtpSession implements Session<FTPFile> {
throw new IOException("Failed to write to '" + path
+ SERVER_REPLIED_WITH + this.client.getReplyString());
}
if (this.logger.isInfoEnabled()) {
this.logger.info("File has been successfully transferred to: " + path);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("File has been successfully transferred to: " + path);
}
}
@@ -140,8 +143,8 @@ public class FtpSession implements Session<FTPFile> {
throw new IOException("Failed to append to '" + path
+ SERVER_REPLIED_WITH + this.client.getReplyString());
}
if (this.logger.isInfoEnabled()) {
this.logger.info("File has been successfully appended to: " + path);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("File has been successfully appended to: " + path);
}
}
@@ -150,17 +153,15 @@ public class FtpSession implements Session<FTPFile> {
try {
if (this.readingRaw.get()) {
if (!finalizeRaw()) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Finalize on readRaw() returned false for " + this);
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("Finalize on readRaw() returned false for " + this);
}
}
}
this.client.disconnect();
}
catch (Exception e) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("failed to disconnect FTPClient", e);
}
LOGGER.warn("failed to disconnect FTPClient", e);
}
}
@@ -183,8 +184,8 @@ public class FtpSession implements Session<FTPFile> {
throw new IOException("Failed to rename '" + pathFrom +
"' to " + pathTo + SERVER_REPLIED_WITH + this.client.getReplyString());
}
if (this.logger.isInfoEnabled()) {
this.logger.info("File has been successfully renamed from: " + pathFrom + " to " + pathTo);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("File has been successfully renamed from: " + pathFrom + " to " + pathTo);
}
}
@@ -228,6 +229,10 @@ public class FtpSession implements Session<FTPFile> {
return this.client;
}
@Override
public String getHostPort() {
return this.client.getRemoteAddress().getHostName() + ':' + this.client.getRemotePort();
}
@Override
public boolean test() {

View File

@@ -177,6 +177,29 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="remote-file-metadata-store" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type
type="org.springframework.integration.metadata.MetadataStore" />
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
Reference to a MetadataStore for saving remote files information between
synchronization and polling.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="metadata-store-prefix" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Specify a prefix for metadata store to distinguish keys from another places
where the same shared store is used.
By default, the remote a component name is used.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attributeGroup ref="tempSuffixGroup" />
</xsd:extension>
</xsd:complexContent>