INT-3412 (S)FTP Append, rmdir, Client Access
JIRA: https://jira.spring.io/browse/INT-3412 Initial commit - review only. TODO: - SFTP Tests - Namespace/Adapter support for file append - Docs INT-3412 Polishing - Addressed PR comments - Completed SFTP implementation - Added namespace/parser support for `FileExistsMode` (append, etc) - Added SFTP Tests - Created Embedded SFTP server for tests (similar to FTP) - Converted tests that needed a real server to use the embedded server INT-3412 Docs and Polish (PR Comments)
This commit is contained in:
committed by
Artem Bilan
parent
bc3db5d4a9
commit
403c91801d
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 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.
|
||||
@@ -25,6 +25,7 @@ import org.springframework.integration.config.xml.AbstractConsumerEndpointParser
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.file.filters.RegexPatternFileListFilter;
|
||||
import org.springframework.integration.file.filters.SimplePatternFileListFilter;
|
||||
import org.springframework.integration.file.remote.RemoteFileOperations;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -45,7 +46,8 @@ public abstract class AbstractRemoteFileOutboundGatewayParser extends AbstractCo
|
||||
@Override
|
||||
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
|
||||
|
||||
BeanDefinition templateDefinition = FileParserUtils.parseRemoteFileTemplate(element, parserContext, false);
|
||||
BeanDefinition templateDefinition = FileParserUtils.parseRemoteFileTemplate(element, parserContext, false,
|
||||
getTemplateClass());
|
||||
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(getGatewayClassName());
|
||||
|
||||
@@ -120,4 +122,6 @@ public abstract class AbstractRemoteFileOutboundGatewayParser extends AbstractCo
|
||||
|
||||
protected abstract String getGatewayClassName();
|
||||
|
||||
protected abstract Class<? extends RemoteFileOperations<?>> getTemplateClass();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2014 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.
|
||||
@@ -22,7 +22,7 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.file.DefaultFileNameGenerator;
|
||||
import org.springframework.integration.file.remote.RemoteFileTemplate;
|
||||
import org.springframework.integration.file.remote.RemoteFileOperations;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -39,8 +39,8 @@ public final class FileParserUtils {
|
||||
}
|
||||
|
||||
public static BeanDefinition parseRemoteFileTemplate(Element element, ParserContext parserContext,
|
||||
boolean atLeastOneRemoteDirectoryAttributeRequired) {
|
||||
BeanDefinitionBuilder templateBuilder = BeanDefinitionBuilder.genericBeanDefinition(RemoteFileTemplate.class);
|
||||
boolean atLeastOneRemoteDirectoryAttributeRequired, Class<? extends RemoteFileOperations<?>> templateClass) {
|
||||
BeanDefinitionBuilder templateBuilder = BeanDefinitionBuilder.genericBeanDefinition(templateClass);
|
||||
|
||||
templateBuilder.addConstructorArgReference(element.getAttribute("session-factory"));
|
||||
// configure MessageHandler properties
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 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.
|
||||
@@ -23,8 +23,11 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser;
|
||||
import org.springframework.integration.file.remote.RemoteFileOperations;
|
||||
import org.springframework.integration.file.remote.handler.FileTransferringMessageHandler;
|
||||
|
||||
import reactor.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Mark Fisher
|
||||
@@ -32,16 +35,23 @@ import org.springframework.integration.file.remote.handler.FileTransferringMessa
|
||||
* @author Gary Russell
|
||||
* @since 2.0
|
||||
*/
|
||||
public class RemoteFileOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser {
|
||||
public abstract class RemoteFileOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser {
|
||||
|
||||
@Override
|
||||
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder handlerBuilder = BeanDefinitionBuilder.genericBeanDefinition(FileTransferringMessageHandler.class);
|
||||
|
||||
BeanDefinition templateDefinition = FileParserUtils.parseRemoteFileTemplate(element, parserContext, true);
|
||||
BeanDefinition templateDefinition = FileParserUtils.parseRemoteFileTemplate(element, parserContext, true,
|
||||
getTemplateClass());
|
||||
|
||||
handlerBuilder.addConstructorArgValue(templateDefinition);
|
||||
String mode = element.getAttribute("mode");
|
||||
if (StringUtils.hasText(mode)) {
|
||||
handlerBuilder.addConstructorArgValue(mode);
|
||||
}
|
||||
return handlerBuilder.getBeanDefinition();
|
||||
}
|
||||
|
||||
protected abstract Class<? extends RemoteFileOperations<?>> getTemplateClass();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.integration.file.remote;
|
||||
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
|
||||
/**
|
||||
* {@code RemoteFileTemplate} callback with the underlying client instance providing
|
||||
* access to lower level methods.
|
||||
*
|
||||
* @author Gary Russell
|
||||
*
|
||||
* @param <C> The type of the underlying client object.
|
||||
* @param <T> The return type of the callback method.
|
||||
* @since 4.1
|
||||
*
|
||||
*/
|
||||
public interface ClientCallback<C, T> {
|
||||
|
||||
/**
|
||||
* Called within the context of a {@link Session}.
|
||||
* Perform some operation(s) on the client instance underlying the session. The caller will take
|
||||
* care of closing the session after this method exits. However, the implementation
|
||||
* is required to perform any clean up required by the client after performing
|
||||
* operations.
|
||||
* @param client The client instance.
|
||||
* @return The return value.
|
||||
*/
|
||||
T doWithClient(C client);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.integration.file.remote;
|
||||
|
||||
|
||||
/**
|
||||
* {@code RemoteFileTemplate} callback with the underlying client instance providing
|
||||
* access to lower level methods where no result is returned.
|
||||
*
|
||||
* @author Gary Russell
|
||||
*
|
||||
* @param <C> The type of the underlying client object.
|
||||
* @since 4.1
|
||||
*
|
||||
*/
|
||||
public abstract class ClientCallbackWithoutResult<C> implements ClientCallback<C, Object> {
|
||||
|
||||
@Override
|
||||
public Object doWithClient(C client) {
|
||||
doWithClientWithoutResult(client);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called within the context of a session.
|
||||
* Perform some operation(s) on the client instance underlying the session. The caller will take
|
||||
* care of closing the session after this method exits. However, the implementation
|
||||
* is required to perform any clean up required by the client after performing
|
||||
* operations.
|
||||
* @param client The client.
|
||||
*/
|
||||
protected abstract void doWithClientWithoutResult(C client);
|
||||
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.integration.file.remote;
|
||||
|
||||
import org.springframework.integration.file.support.FileExistsMode;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
/**
|
||||
@@ -30,9 +31,12 @@ public interface RemoteFileOperations<F> {
|
||||
* Send a file to a remote server, based on information in a message.
|
||||
*
|
||||
* @param message The message.
|
||||
* @param mode See {@link FileExistsMode} (optional; default REPLACE). A
|
||||
* vararg is used to make the argument optional; only the first will be
|
||||
* used if more than one is provided.
|
||||
* @return The remote path, or null if no local file was found.
|
||||
*/
|
||||
String send(Message<?> message);
|
||||
String send(Message<?> message, FileExistsMode... mode);
|
||||
|
||||
/**
|
||||
* Send a file to a remote server, based on information in a message.
|
||||
@@ -41,9 +45,33 @@ public interface RemoteFileOperations<F> {
|
||||
*
|
||||
* @param message The message.
|
||||
* @param subDirectory The sub directory.
|
||||
* @param mode See {@link FileExistsMode} (optional; default REPLACE). A
|
||||
* vararg is used to make the argument optional; only the first will be
|
||||
* used if more than one is provided.
|
||||
* @return The remote path, or null if no local file was found.
|
||||
*/
|
||||
String send(Message<?> message, String subDirectory);
|
||||
String send(Message<?> message, String subDirectory, FileExistsMode... mode);
|
||||
|
||||
/**
|
||||
* Send a file to a remote server, based on information in a message, appending.
|
||||
*
|
||||
* @param message The message.
|
||||
* @return The remote path, or null if no local file was found.
|
||||
* @since 4.1
|
||||
*/
|
||||
String append(Message<?> message);
|
||||
|
||||
/**
|
||||
* Send a file to a remote server, based on information in a message, appending.
|
||||
* The subDirectory is appended to the remote directory evaluated from
|
||||
* the message.
|
||||
*
|
||||
* @param message The message.
|
||||
* @param subDirectory The sub directory.
|
||||
* @return The remote path, or null if no local file was found.
|
||||
* @since 4.1
|
||||
*/
|
||||
String append(Message<?> message, String subDirectory);
|
||||
|
||||
/**
|
||||
* Retrieve a remote file as an InputStream.
|
||||
@@ -63,11 +91,20 @@ public interface RemoteFileOperations<F> {
|
||||
*/
|
||||
boolean get(Message<?> message, InputStreamCallback callback);
|
||||
|
||||
/**
|
||||
* Check if a file exists on the remote server.
|
||||
*
|
||||
* @param path The full path to the file.
|
||||
* @return true when the file exists.
|
||||
* @since 4.1
|
||||
*/
|
||||
boolean exists(String path);
|
||||
|
||||
/**
|
||||
* Remove a remote file.
|
||||
*
|
||||
* @param path The full path to the file.
|
||||
* @return true when successful
|
||||
* @return true when successful.
|
||||
*/
|
||||
boolean remove(String path);
|
||||
|
||||
@@ -84,9 +121,22 @@ public interface RemoteFileOperations<F> {
|
||||
* Reliably closes the session when the method exits.
|
||||
*
|
||||
* @param callback the SessionCallback.
|
||||
* @param <T> The type returned by {@link SessionCallback#doInSession(org.springframework.integration.file.remote.session.Session)}.
|
||||
* @param <T> The type returned by
|
||||
* {@link SessionCallback#doInSession(org.springframework.integration.file.remote.session.Session)}.
|
||||
* @return The result of the callback method.
|
||||
*/
|
||||
<T> T execute(SessionCallback<F, T> callback);
|
||||
|
||||
/**
|
||||
* Execute the callback's doWithClient method after obtaining a session's
|
||||
* client, providing access to low level methods.
|
||||
* Reliably closes the session when the method exits.
|
||||
*
|
||||
* @param callback the ClientCallback.
|
||||
* @param <T> The type returned by {@link ClientCallback#doWithClient(Object)}.
|
||||
* @return The result of the callback method.
|
||||
* @since 4.1
|
||||
*/
|
||||
<T, C> T executeWithClient(ClientCallback<C, T> callback);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2014 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.
|
||||
@@ -36,6 +36,7 @@ import org.springframework.integration.file.FileNameGenerator;
|
||||
import org.springframework.integration.file.remote.session.CachingSessionFactory;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
import org.springframework.integration.file.support.FileExistsMode;
|
||||
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageDeliveryException;
|
||||
@@ -60,7 +61,7 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
|
||||
/**
|
||||
* the {@link SessionFactory} for acquiring remote file Sessions.
|
||||
*/
|
||||
private final SessionFactory<F> sessionFactory;
|
||||
protected final SessionFactory<F> sessionFactory;
|
||||
|
||||
private volatile String temporaryFileSuffix =".writing";
|
||||
|
||||
@@ -178,13 +179,30 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
|
||||
}
|
||||
|
||||
@Override
|
||||
public String send(final Message<?> message) {
|
||||
return this.send(message, null);
|
||||
public String append(final Message<?> message) {
|
||||
return append(message, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String send(final Message<?> message, final String subDirectory) {
|
||||
public String append(final Message<?> message, String subDirectory) {
|
||||
return send(message, subDirectory, FileExistsMode.APPEND);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String send(Message<?> message, FileExistsMode... mode) {
|
||||
return send(message, null, mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String send(final Message<?> message, String subDirectory, FileExistsMode... mode) {
|
||||
FileExistsMode modeToUse = mode == null || mode.length < 1 ? FileExistsMode.REPLACE : mode[0];
|
||||
return send(message, subDirectory, modeToUse);
|
||||
}
|
||||
|
||||
private String send(final Message<?> message, final String subDirectory, final FileExistsMode mode) {
|
||||
Assert.notNull(this.directoryExpressionProcessor, "'remoteDirectoryExpression' is required");
|
||||
Assert.isTrue(!FileExistsMode.APPEND.equals(mode) || !this.useTemporaryFileName,
|
||||
"Cannot append when using a temporary file name");
|
||||
final StreamHolder inputStreamHolder = this.payloadToInputStream(message);
|
||||
if (inputStreamHolder != null) {
|
||||
return this.execute(new SessionCallback<F, String>() {
|
||||
@@ -211,7 +229,7 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
|
||||
}
|
||||
fileName = RemoteFileTemplate.this.fileNameGenerator.generateFileName(message);
|
||||
RemoteFileTemplate.this.sendFileToRemoteDirectory(inputStreamHolder.getStream(),
|
||||
temporaryRemoteDirectory, remoteDirectory, fileName, session);
|
||||
temporaryRemoteDirectory, remoteDirectory, fileName, session, mode);
|
||||
return remoteDirectory + fileName;
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
@@ -239,6 +257,11 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(String path) {
|
||||
throw new UnsupportedOperationException("exists() is not supported by the generic template");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(final String path) {
|
||||
return this.execute(new SessionCallback<F, Boolean>() {
|
||||
@@ -324,6 +347,11 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, C> T executeWithClient(ClientCallback<C, T> callback) {
|
||||
throw new UnsupportedOperationException("executeWithClient() is not supported by the generic template");
|
||||
}
|
||||
|
||||
private StreamHolder payloadToInputStream(Message<?> message) throws MessageDeliveryException {
|
||||
try {
|
||||
Object payload = message.getPayload();
|
||||
@@ -365,7 +393,7 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
|
||||
}
|
||||
|
||||
private void sendFileToRemoteDirectory(InputStream inputStream, String temporaryRemoteDirectory,
|
||||
String remoteDirectory, String fileName, Session<F> session) throws IOException {
|
||||
String remoteDirectory, String fileName, Session<F> session, FileExistsMode mode) throws IOException {
|
||||
|
||||
remoteDirectory = this.normalizeDirectoryPath(remoteDirectory);
|
||||
temporaryRemoteDirectory = this.normalizeDirectoryPath(temporaryRemoteDirectory);
|
||||
@@ -387,9 +415,29 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
|
||||
}
|
||||
|
||||
try {
|
||||
session.write(inputStream, tempFilePath);
|
||||
boolean rename = this.useTemporaryFileName;
|
||||
if (FileExistsMode.REPLACE.equals(mode)) {
|
||||
session.write(inputStream, tempFilePath);
|
||||
}
|
||||
else if (FileExistsMode.APPEND.equals(mode)) {
|
||||
session.append(inputStream, tempFilePath);
|
||||
}
|
||||
else {
|
||||
if (exists(remoteFilePath)) {
|
||||
if (FileExistsMode.FAIL.equals(mode)) {
|
||||
throw new MessagingException(
|
||||
"The destination file already exists at '" + remoteFilePath + "'.");
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("File not transferred to '" + remoteFilePath + "'; already exists.");
|
||||
}
|
||||
}
|
||||
rename = false;
|
||||
}
|
||||
}
|
||||
// then rename it to its final name if necessary
|
||||
if (useTemporaryFileName){
|
||||
if (rename) {
|
||||
session.rename(tempFilePath, remoteFilePath);
|
||||
}
|
||||
}
|
||||
@@ -432,5 +480,4 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ public abstract class SessionCallbackWithoutResult<F> implements SessionCallback
|
||||
|
||||
@Override
|
||||
public Object doInSession(Session<F> session) throws IOException {
|
||||
this.doInSessionWithoutResult(session);
|
||||
doInSessionWithoutResult(session);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.springframework.expression.Expression;
|
||||
import org.springframework.integration.file.FileNameGenerator;
|
||||
import org.springframework.integration.file.remote.RemoteFileTemplate;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
import org.springframework.integration.file.support.FileExistsMode;
|
||||
import org.springframework.integration.handler.AbstractMessageHandler;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -39,14 +40,22 @@ public class FileTransferringMessageHandler<F> extends AbstractMessageHandler {
|
||||
|
||||
private final RemoteFileTemplate<F> remoteFileTemplate;
|
||||
|
||||
private final FileExistsMode mode;
|
||||
|
||||
public FileTransferringMessageHandler(SessionFactory<F> sessionFactory) {
|
||||
Assert.notNull(sessionFactory, "sessionFactory must not be null");
|
||||
this.remoteFileTemplate = new RemoteFileTemplate<F>(sessionFactory);
|
||||
this.mode = FileExistsMode.REPLACE;
|
||||
}
|
||||
|
||||
public FileTransferringMessageHandler(RemoteFileTemplate<F> remoteFileTemplate) {
|
||||
this(remoteFileTemplate, FileExistsMode.REPLACE);
|
||||
}
|
||||
|
||||
public FileTransferringMessageHandler(RemoteFileTemplate<F> remoteFileTemplate, FileExistsMode mode) {
|
||||
Assert.notNull(remoteFileTemplate, "remoteFileTemplate must not be null");
|
||||
this.remoteFileTemplate = remoteFileTemplate;
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +107,7 @@ public class FileTransferringMessageHandler<F> extends AbstractMessageHandler {
|
||||
|
||||
@Override
|
||||
protected void handleMessageInternal(Message<?> message) throws Exception {
|
||||
this.remoteFileTemplate.send(message);
|
||||
this.remoteFileTemplate.send(message, this.mode);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -215,6 +215,11 @@ public class CachingSessionFactory<F> implements SessionFactory<F>, DisposableBe
|
||||
this.targetSession.write(inputStream, destination);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void append(InputStream inputStream, String destination) throws IOException {
|
||||
this.targetSession.append(inputStream, destination);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return this.targetSession.isOpen();
|
||||
@@ -230,6 +235,11 @@ public class CachingSessionFactory<F> implements SessionFactory<F>, DisposableBe
|
||||
return this.targetSession.mkdir(directory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rmdir(String directory) throws IOException {
|
||||
return this.targetSession.rmdir(directory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(String path) throws IOException{
|
||||
return this.targetSession.exists(path);
|
||||
@@ -254,6 +264,11 @@ public class CachingSessionFactory<F> implements SessionFactory<F>, DisposableBe
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getClientInstance() {
|
||||
return this.targetSession.getClientInstance();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,18 +30,36 @@ import java.io.OutputStream;
|
||||
* @author Gary Russell
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface Session<T> {
|
||||
public interface Session<F> {
|
||||
|
||||
boolean remove(String path) throws IOException;
|
||||
|
||||
T[] list(String path) throws IOException;
|
||||
F[] list(String path) throws IOException;
|
||||
|
||||
void read(String source, OutputStream outputStream) throws IOException;
|
||||
|
||||
void write(InputStream inputStream, String destination) throws IOException;
|
||||
|
||||
/**
|
||||
* Append to a file.
|
||||
* @param inputStream the stream.
|
||||
* @param destination the destination.
|
||||
* @throws IOException an IO Exception.
|
||||
* @since 4.1
|
||||
*/
|
||||
void append(InputStream inputStream, String destination) throws IOException;
|
||||
|
||||
boolean mkdir(String directory) throws IOException;
|
||||
|
||||
/**
|
||||
* Remove a remote directory.
|
||||
* @param directory The directory.
|
||||
* @return True if the directory was removed.
|
||||
* @throws IOException an IO exception.
|
||||
* @since 4.1
|
||||
*/
|
||||
boolean rmdir(String directory) throws IOException;
|
||||
|
||||
void rename(String pathFrom, String pathTo) throws IOException;
|
||||
|
||||
void close();
|
||||
@@ -57,6 +75,7 @@ public interface Session<T> {
|
||||
* @param source The path of the remote file.
|
||||
* @return The raw inputStream.
|
||||
* @throws IOException Any IOException.
|
||||
* @since 3.0
|
||||
*/
|
||||
InputStream readRaw(String source) throws IOException;
|
||||
|
||||
@@ -65,7 +84,19 @@ public interface Session<T> {
|
||||
* Required by some session providers.
|
||||
* @return true if successful.
|
||||
* @throws IOException Any IOException.
|
||||
* @since 3.0
|
||||
*/
|
||||
boolean finalizeRaw() throws IOException;
|
||||
|
||||
/**
|
||||
* Get the underlying client library's client instance for this session.
|
||||
* Returns an {@code Object} to avoid significant changes to -file, -ftp, -sftp
|
||||
* modules, which would be required
|
||||
* if we added another generic parameter. Implementations should narrow the
|
||||
* return type.
|
||||
* @return The client instance.
|
||||
* @since 4.1
|
||||
*/
|
||||
Object getClientInstance();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.integration.file.remote.session;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
|
||||
/**
|
||||
* Temporary factory bean to manage SessionFactory until deprecated 'cache-sessions' attribute
|
||||
* is removed.
|
||||
*
|
||||
* The attribute is now removed so we deprecate this class and log a message in case someone
|
||||
* is using it directly. It is no longer used by the framework.
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @since 2.1
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public class SessionFactoryFactoryBean<T> implements FactoryBean<SessionFactory<T>> {
|
||||
|
||||
Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private final SessionFactory<T> sessionFactory;
|
||||
|
||||
public SessionFactoryFactoryBean(SessionFactory<T> sessionFactory, boolean cacheSessions){
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Do not use this factory bean; "
|
||||
+ "instantiate the session factory directly; "
|
||||
+ "if cached sessions are required, wrap it in a CachingSessionFactory.");
|
||||
}
|
||||
if (cacheSessions && !(sessionFactory instanceof CachingSessionFactory)){
|
||||
this.sessionFactory = new CachingSessionFactory<T>(sessionFactory);
|
||||
}
|
||||
else {
|
||||
this.sessionFactory = sessionFactory;
|
||||
}
|
||||
}
|
||||
|
||||
public SessionFactory<T> getObject() throws Exception {
|
||||
return this.sessionFactory;
|
||||
}
|
||||
|
||||
|
||||
public Class<?> getObjectType() {
|
||||
return this.sessionFactory.getClass();
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -398,48 +398,7 @@ Only files matching this regular expression will be picked up by this adapter.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="mode">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
This attribute defaults to 'REPLACE' if not set explicitly.
|
||||
|
||||
The following options are available:
|
||||
|
||||
APPEND:
|
||||
|
||||
If append is specified, the data will be appended
|
||||
to the existing file if such file exists, otherwise the
|
||||
new file will be created as usual but once created the
|
||||
subsequent data will be appended to it. This attribute
|
||||
is mutualy exclusive with the 'temporary-file-suffix'
|
||||
since append is done to the actual file and not its
|
||||
temporary counterpart.
|
||||
|
||||
If set to APPEND, the component will also create a real
|
||||
instance of the LockRegistry to ensure that there are no
|
||||
collisions when multiple threads are writing to the same
|
||||
file.
|
||||
|
||||
FAIL:
|
||||
|
||||
If the target file exists, a MessageHandlingException
|
||||
is thrown.
|
||||
|
||||
IGNORE:
|
||||
|
||||
If the target file exists, the message payload is silently
|
||||
ignored.
|
||||
|
||||
REPLACE:
|
||||
|
||||
This is the default behavior when writing files. If the
|
||||
target file already exists, it will be overwritten.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:union memberTypes="mode xsd:string"/>
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
<xsd:attributeGroup ref="modeGroup" />
|
||||
<xsd:attribute name="delete-source-files" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
@@ -606,13 +565,13 @@ Only files matching this regular expression will be picked up by this adapter.
|
||||
<xsd:documentation><![CDATA[
|
||||
If append is specified, the data will be appended
|
||||
to the existing file if such file exists, otherwise the
|
||||
new file will be created as usual but once created the
|
||||
new file will be created as usual but, once created,
|
||||
subsequent data will be appended to it. This attribute
|
||||
is mutualy exclusive with the 'temporary-file-suffix'
|
||||
is mutualy exclusive with the use of a temporary file,
|
||||
since append is done to the actual file and not its
|
||||
temporary counterpart.
|
||||
|
||||
If set to APPEND, the component will also create a real
|
||||
If set to APPEND, the component will also use an
|
||||
instance of the LockRegistry to ensure that there are no
|
||||
collisions when multiple threads are writing to the same
|
||||
file.
|
||||
@@ -718,6 +677,52 @@ Only files matching this regular expression will be picked up by this adapter.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attributeGroup ref="modeGroup" />
|
||||
</xsd:attributeGroup>
|
||||
|
||||
<xsd:attributeGroup name="modeGroup">
|
||||
<xsd:attribute name="mode">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
This attribute defaults to 'REPLACE' if not set explicitly.
|
||||
|
||||
The following options are available:
|
||||
|
||||
APPEND:
|
||||
|
||||
If append is specified, the data will be appended
|
||||
to the existing file if such file exists, otherwise the
|
||||
new file will be created as usual but, once created, the
|
||||
subsequent data will be appended to it. This attribute
|
||||
is mutualy exclusive with the use of a temporary file,
|
||||
since append is done to the actual file and not its
|
||||
temporary counterpart.
|
||||
|
||||
If set to APPEND, the component will also use
|
||||
instance of the LockRegistry to ensure that there are no
|
||||
collisions when multiple threads are writing to the same
|
||||
file.
|
||||
|
||||
FAIL:
|
||||
|
||||
If the target file exists, a MessageHandlingException
|
||||
is thrown.
|
||||
|
||||
IGNORE:
|
||||
|
||||
If the target file exists, the message payload is silently
|
||||
ignored.
|
||||
|
||||
REPLACE:
|
||||
|
||||
This is the default behavior when writing files. If the
|
||||
target file already exists, it will be overwritten.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:union memberTypes="mode xsd:string"/>
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
</xsd:attributeGroup>
|
||||
|
||||
</xsd:schema>
|
||||
|
||||
@@ -159,19 +159,9 @@ public class RemoteFileOutboundGatewayTests {
|
||||
gw.afterPropertiesSet();
|
||||
new File(this.tmpDir + "/f1").delete();
|
||||
new File(this.tmpDir + "/f2").delete();
|
||||
when(sessionFactory.getSession()).thenReturn(new Session() {
|
||||
when(sessionFactory.getSession()).thenReturn(new TestSession() {
|
||||
int n;
|
||||
|
||||
@Override
|
||||
public boolean remove(String path) throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] list(String path) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(String source, OutputStream outputStream)
|
||||
throws IOException {
|
||||
@@ -184,49 +174,11 @@ public class RemoteFileOutboundGatewayTests {
|
||||
outputStream.write("testData".getBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(InputStream inputStream, String destination)
|
||||
throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mkdir(String directory) throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rename(String pathFrom, String pathTo)
|
||||
throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(String path) throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] listNames(String path) throws IOException {
|
||||
return new String[]{path1, path2};
|
||||
return new String[] { path1, path2 };
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream readRaw(String source) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean finalizeRaw() throws IOException {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
@SuppressWarnings("unchecked")
|
||||
Message<List<File>> out = (Message<List<File>>) gw
|
||||
@@ -246,16 +198,7 @@ public class RemoteFileOutboundGatewayTests {
|
||||
gw.setLocalDirectory(new File(this.tmpDir));
|
||||
gw.afterPropertiesSet();
|
||||
new File(this.tmpDir + "/f1").delete();
|
||||
when(sessionFactory.getSession()).thenReturn(new Session() {
|
||||
@Override
|
||||
public boolean remove(String path) throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] list(String path) throws IOException {
|
||||
return null;
|
||||
}
|
||||
when(sessionFactory.getSession()).thenReturn(new TestSession() {
|
||||
|
||||
@Override
|
||||
public void read(String source, OutputStream outputStream)
|
||||
@@ -263,49 +206,11 @@ public class RemoteFileOutboundGatewayTests {
|
||||
outputStream.write("testData".getBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(InputStream inputStream, String destination)
|
||||
throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mkdir(String directory) throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rename(String pathFrom, String pathTo)
|
||||
throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(String path) throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] listNames(String path) throws IOException {
|
||||
return new String[]{"f1"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream readRaw(String source) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean finalizeRaw() throws IOException {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
@SuppressWarnings("unchecked")
|
||||
Message<List<File>> out = (Message<List<File>>) gw
|
||||
@@ -326,16 +231,7 @@ public class RemoteFileOutboundGatewayTests {
|
||||
gw.afterPropertiesSet();
|
||||
new File(this.tmpDir + "/f1").delete();
|
||||
new File(this.tmpDir + "/f2").delete();
|
||||
when(sessionFactory.getSession()).thenReturn(new Session() {
|
||||
@Override
|
||||
public boolean remove(String path) throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] list(String path) throws IOException {
|
||||
return null;
|
||||
}
|
||||
when(sessionFactory.getSession()).thenReturn(new TestSession() {
|
||||
|
||||
@Override
|
||||
public void read(String source, OutputStream outputStream)
|
||||
@@ -343,49 +239,6 @@ public class RemoteFileOutboundGatewayTests {
|
||||
outputStream.write("testData".getBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(InputStream inputStream, String destination)
|
||||
throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mkdir(String directory) throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rename(String pathFrom, String pathTo)
|
||||
throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(String path) throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] listNames(String path) throws IOException {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream readRaw(String source) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean finalizeRaw() throws IOException {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
gw.handleRequestMessage(new GenericMessage<String>("testremote/*"));
|
||||
}
|
||||
@@ -733,13 +586,7 @@ public class RemoteFileOutboundGatewayTests {
|
||||
gw.setLocalDirectory(new File(this.tmpDir));
|
||||
gw.afterPropertiesSet();
|
||||
new File(this.tmpDir + "/f1").delete();
|
||||
when(sessionFactory.getSession()).thenReturn(new Session() {
|
||||
private boolean open = true;
|
||||
|
||||
@Override
|
||||
public boolean remove(String path) throws IOException {
|
||||
return false;
|
||||
}
|
||||
when(sessionFactory.getSession()).thenReturn(new TestSession() {
|
||||
|
||||
@Override
|
||||
public TestLsEntry[] list(String path) throws IOException {
|
||||
@@ -754,50 +601,6 @@ public class RemoteFileOutboundGatewayTests {
|
||||
outputStream.write("testfile".getBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(InputStream inputStream, String destination)
|
||||
throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mkdir(String directory) throws IOException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rename(String pathFrom, String pathTo)
|
||||
throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
open = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return open;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(String path) throws IOException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] listNames(String path) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream readRaw(String source) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean finalizeRaw() throws IOException {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
@SuppressWarnings("unchecked")
|
||||
Message<File> out = (Message<File>) gw.handleRequestMessage(new GenericMessage<String>("f1"));
|
||||
@@ -819,13 +622,7 @@ public class RemoteFileOutboundGatewayTests {
|
||||
gw.setLocalDirectory(new File(this.tmpDir));
|
||||
gw.afterPropertiesSet();
|
||||
new File(this.tmpDir + "/f1").delete();
|
||||
when(sessionFactory.getSession()).thenReturn(new Session() {
|
||||
private boolean open = true;
|
||||
|
||||
@Override
|
||||
public boolean remove(String path) throws IOException {
|
||||
return false;
|
||||
}
|
||||
when(sessionFactory.getSession()).thenReturn(new TestSession() {
|
||||
|
||||
@Override
|
||||
public TestLsEntry[] list(String path) throws IOException {
|
||||
@@ -839,50 +636,6 @@ public class RemoteFileOutboundGatewayTests {
|
||||
throw new RuntimeException("test remove .writing");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(InputStream inputStream, String destination)
|
||||
throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mkdir(String directory) throws IOException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rename(String pathFrom, String pathTo)
|
||||
throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
open = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return open;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(String path) throws IOException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] listNames(String path) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream readRaw(String source) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean finalizeRaw() throws IOException {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
try{
|
||||
gw.handleRequestMessage(new GenericMessage<String>("f1"));
|
||||
@@ -911,13 +664,7 @@ public class RemoteFileOutboundGatewayTests {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.add(Calendar.MONTH, -1);
|
||||
final Date modified = new Date(cal.getTime().getTime() / 1000 * 1000);
|
||||
when(sessionFactory.getSession()).thenReturn(new Session() {
|
||||
private boolean open = true;
|
||||
|
||||
@Override
|
||||
public boolean remove(String path) throws IOException {
|
||||
return false;
|
||||
}
|
||||
when(sessionFactory.getSession()).thenReturn(new TestSession() {
|
||||
|
||||
@Override
|
||||
public TestLsEntry[] list(String path) throws IOException {
|
||||
@@ -932,50 +679,6 @@ public class RemoteFileOutboundGatewayTests {
|
||||
outputStream.write("testfile".getBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(InputStream inputStream, String destination)
|
||||
throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mkdir(String directory) throws IOException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rename(String pathFrom, String pathTo)
|
||||
throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
open = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return open;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(String path) throws IOException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] listNames(String path) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream readRaw(String source) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean finalizeRaw() throws IOException {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
@SuppressWarnings("unchecked")
|
||||
Message<File> out = (Message<File>) gw.handleRequestMessage(new GenericMessage<String>("x/f1"));
|
||||
@@ -999,13 +702,7 @@ public class RemoteFileOutboundGatewayTests {
|
||||
(sessionFactory, "get", "payload");
|
||||
gw.setLocalDirectory(new File(this.tmpDir + "/x"));
|
||||
gw.afterPropertiesSet();
|
||||
when(sessionFactory.getSession()).thenReturn(new Session() {
|
||||
private boolean open = true;
|
||||
|
||||
@Override
|
||||
public boolean remove(String path) throws IOException {
|
||||
return false;
|
||||
}
|
||||
when(sessionFactory.getSession()).thenReturn(new TestSession() {
|
||||
|
||||
@Override
|
||||
public TestLsEntry[] list(String path) throws IOException {
|
||||
@@ -1020,50 +717,6 @@ public class RemoteFileOutboundGatewayTests {
|
||||
outputStream.write("testfile".getBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(InputStream inputStream, String destination)
|
||||
throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mkdir(String directory) throws IOException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rename(String pathFrom, String pathTo)
|
||||
throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
open = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return open;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(String path) throws IOException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] listNames(String path) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream readRaw(String source) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean finalizeRaw() throws IOException {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
gw.handleRequestMessage(new GenericMessage<String>("f1"));
|
||||
File out = new File(this.tmpDir + "/x/f1");
|
||||
@@ -1207,6 +860,88 @@ public class RemoteFileOutboundGatewayTests {
|
||||
|
||||
}
|
||||
|
||||
abstract class TestSession implements org.springframework.integration.file.remote.session.Session<TestLsEntry> {
|
||||
|
||||
private boolean open;
|
||||
|
||||
|
||||
@Override
|
||||
public boolean remove(String path) throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TestLsEntry[] list(String path) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(String source, OutputStream outputStream)
|
||||
throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(InputStream inputStream, String destination)
|
||||
throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void append(InputStream inputStream, String destination)
|
||||
throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mkdir(String directory) throws IOException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rmdir(String directory) throws IOException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rename(String pathFrom, String pathTo)
|
||||
throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
open = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return open;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(String path) throws IOException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] listNames(String path) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream readRaw(String source) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean finalizeRaw() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getClientInstance() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestRemoteFileOutboundGateway extends AbstractRemoteFileOutboundGateway<TestLsEntry> {
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
|
||||
@@ -147,11 +147,20 @@ public class CachingSessionFactoryTests {
|
||||
public void write(InputStream inputStream, String destination) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void append(InputStream inputStream, String destination) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mkdir(String directory) throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rmdir(String directory) throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rename(String pathFrom, String pathTo) throws IOException {
|
||||
}
|
||||
@@ -186,6 +195,11 @@ public class CachingSessionFactoryTests {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getClientInstance() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -120,11 +120,20 @@ public class AbstractRemoteFileSynchronizerTests {
|
||||
public void write(InputStream inputStream, String destination) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void append(InputStream inputStream, String destination) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mkdir(String directory) throws IOException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rmdir(String directory) throws IOException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rename(String pathFrom, String pathTo) throws IOException {
|
||||
}
|
||||
@@ -158,6 +167,11 @@ public class AbstractRemoteFileSynchronizerTests {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getClientInstance() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors
|
||||
* Copyright 2002-2014 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.
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.integration.ftp.config;
|
||||
|
||||
import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHandler;
|
||||
import org.springframework.integration.file.config.RemoteFileOutboundChannelAdapterParser;
|
||||
|
||||
/**
|
||||
* Provides namespace support for using FTP
|
||||
@@ -26,13 +25,15 @@ import org.springframework.integration.file.config.RemoteFileOutboundChannelAdap
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @since 2.0
|
||||
*/
|
||||
public class FtpNamespaceHandler extends AbstractIntegrationNamespaceHandler {
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
registerBeanDefinitionParser("inbound-channel-adapter", new FtpInboundChannelAdapterParser());
|
||||
registerBeanDefinitionParser("outbound-channel-adapter", new RemoteFileOutboundChannelAdapterParser());
|
||||
registerBeanDefinitionParser("outbound-channel-adapter", new FtpOutboundChannelAdapterParser());
|
||||
registerBeanDefinitionParser("outbound-gateway", new FtpOutboundGatewayParser());
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.integration.ftp.config;
|
||||
|
||||
import org.springframework.integration.file.config.RemoteFileOutboundChannelAdapterParser;
|
||||
import org.springframework.integration.file.remote.RemoteFileOperations;
|
||||
import org.springframework.integration.ftp.session.FtpRemoteFileTemplate;
|
||||
|
||||
/**
|
||||
* Parser for FTP Outbound Channel Adapters.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 4.1
|
||||
*
|
||||
*/
|
||||
public class FtpOutboundChannelAdapterParser extends RemoteFileOutboundChannelAdapterParser {
|
||||
|
||||
@Override
|
||||
protected Class<? extends RemoteFileOperations<?>> getTemplateClass() {
|
||||
return FtpRemoteFileTemplate.class;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 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.
|
||||
@@ -16,9 +16,11 @@
|
||||
package org.springframework.integration.ftp.config;
|
||||
|
||||
import org.springframework.integration.file.config.AbstractRemoteFileOutboundGatewayParser;
|
||||
import org.springframework.integration.file.remote.RemoteFileOperations;
|
||||
import org.springframework.integration.ftp.filters.FtpRegexPatternFileListFilter;
|
||||
import org.springframework.integration.ftp.filters.FtpSimplePatternFileListFilter;
|
||||
import org.springframework.integration.ftp.gateway.FtpOutboundGateway;
|
||||
import org.springframework.integration.ftp.session.FtpRemoteFileTemplate;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
@@ -28,6 +30,7 @@ import org.springframework.integration.ftp.gateway.FtpOutboundGateway;
|
||||
*/
|
||||
public class FtpOutboundGatewayParser extends AbstractRemoteFileOutboundGatewayParser {
|
||||
|
||||
@Override
|
||||
public String getGatewayClassName() {
|
||||
return FtpOutboundGateway.class.getName();
|
||||
}
|
||||
@@ -42,4 +45,9 @@ public class FtpOutboundGatewayParser extends AbstractRemoteFileOutboundGatewayP
|
||||
return FtpRegexPatternFileListFilter.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<? extends RemoteFileOperations<?>> getTemplateClass() {
|
||||
return FtpRemoteFileTemplate.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ import org.apache.commons.net.ftp.FTPClientConfig;
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
import org.apache.commons.net.ftp.FTPReply;
|
||||
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -164,7 +163,7 @@ public abstract class AbstractFtpSessionFactory<T extends FTPClient> implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public Session<FTPFile> getSession() {
|
||||
public FtpSession getSession() {
|
||||
try {
|
||||
return new FtpSession(this.createClient());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.integration.ftp.session;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
|
||||
import org.springframework.integration.file.remote.ClientCallback;
|
||||
import org.springframework.integration.file.remote.RemoteFileTemplate;
|
||||
import org.springframework.integration.file.remote.SessionCallback;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
|
||||
/**
|
||||
* FTP version of {@code RemoteFileTemplate} providing type-safe access to
|
||||
* the underlying FTPClient object.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 4.1
|
||||
*
|
||||
*/
|
||||
public class FtpRemoteFileTemplate extends RemoteFileTemplate<FTPFile> {
|
||||
|
||||
public FtpRemoteFileTemplate(SessionFactory<FTPFile> sessionFactory) {
|
||||
super(sessionFactory);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T, C> T executeWithClient(final ClientCallback<C, T> callback) {
|
||||
return doExecuteWithClient((ClientCallback<FTPClient, T>) callback);
|
||||
}
|
||||
|
||||
protected <T> T doExecuteWithClient(final ClientCallback<FTPClient, T> callback) {
|
||||
return execute(new SessionCallback<FTPFile, T>() {
|
||||
|
||||
@Override
|
||||
public T doInSession(Session<FTPFile> session) throws IOException {
|
||||
return callback.doWithClient((FTPClient) session.getClientInstance());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(final String path) {
|
||||
return executeWithClient(new ClientCallback<FTPClient, Boolean>() {
|
||||
|
||||
@Override
|
||||
public Boolean doWithClient(FTPClient client) {
|
||||
try {
|
||||
return client.getStatus(path) != null;
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new MessagingException("Failed to stat " + path, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -116,7 +116,7 @@ public class FtpSession implements Session<FTPFile> {
|
||||
@Override
|
||||
public void write(InputStream inputStream, String path) throws IOException {
|
||||
Assert.notNull(inputStream, "inputStream must not be null");
|
||||
Assert.hasText(path, "path must not be null");
|
||||
Assert.hasText(path, "path must not be null or empty");
|
||||
boolean completed = this.client.storeFile(path, inputStream);
|
||||
if (!completed) {
|
||||
throw new IOException("Failed to write to '" + path
|
||||
@@ -127,6 +127,20 @@ public class FtpSession implements Session<FTPFile> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void append(InputStream inputStream, String path) throws IOException {
|
||||
Assert.notNull(inputStream, "inputStream must not be null");
|
||||
Assert.hasText(path, "path must not be null or empty");
|
||||
boolean completed = this.client.appendFile(path, inputStream);
|
||||
if (!completed) {
|
||||
throw new IOException("Failed to append to '" + path
|
||||
+ "'. Server replied with: " + this.client.getReplyString());
|
||||
}
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("File has been successfully appended to: " + path);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
@@ -168,6 +182,12 @@ public class FtpSession implements Session<FTPFile> {
|
||||
return this.client.makeDirectory(remoteDirectory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rmdir(String directory) throws IOException {
|
||||
return this.client.removeDirectory(directory);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean exists(String path) throws IOException{
|
||||
Assert.hasText(path, "'path' must not be empty");
|
||||
@@ -187,4 +207,10 @@ public class FtpSession implements Session<FTPFile> {
|
||||
|
||||
return exists;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FTPClient getClientInstance() {
|
||||
return this.client;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ import org.springframework.integration.test.util.SocketUtils;
|
||||
* @since 3.0
|
||||
*/
|
||||
@Configuration
|
||||
public class TesFtpServer {
|
||||
public class TestFtpServer {
|
||||
|
||||
private final int ftpPort = SocketUtils.findAvailableServerSocket();
|
||||
|
||||
@@ -72,7 +72,7 @@ public class TesFtpServer {
|
||||
|
||||
private volatile FtpServer server;
|
||||
|
||||
public TesFtpServer(final String root) {
|
||||
public TestFtpServer(final String root) {
|
||||
this.ftpFolder = new TemporaryFolder() {
|
||||
|
||||
@Override
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 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.
|
||||
@@ -27,26 +27,30 @@ import static org.mockito.Mockito.when;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
|
||||
import org.springframework.integration.file.filters.FileListFilter;
|
||||
import org.springframework.integration.file.remote.session.CachingSessionFactory;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.integration.file.remote.synchronizer.AbstractInboundFileSynchronizer;
|
||||
import org.springframework.integration.ftp.filters.FtpSimplePatternFileListFilter;
|
||||
import org.springframework.integration.ftp.inbound.FtpInboundFileSynchronizer;
|
||||
import org.springframework.integration.ftp.inbound.FtpInboundFileSynchronizingMessageSource;
|
||||
import org.springframework.integration.ftp.session.DefaultFtpSessionFactory;
|
||||
import org.springframework.integration.ftp.session.FtpSession;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.ReflectionUtils.MethodCallback;
|
||||
|
||||
@@ -56,23 +60,39 @@ import org.springframework.util.ReflectionUtils.MethodCallback;
|
||||
* @author Gary Russell
|
||||
* @author Gunnar Hillert
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@DirtiesContext
|
||||
public class FtpInboundChannelAdapterParserTests {
|
||||
|
||||
@Autowired
|
||||
private SourcePollingChannelAdapter ftpInbound;
|
||||
|
||||
@Autowired
|
||||
private SourcePollingChannelAdapter simpleAdapterWithCachedSessions;
|
||||
|
||||
@Autowired
|
||||
private MessageChannel autoChannel;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("autoChannel.adapter")
|
||||
private SourcePollingChannelAdapter autoChannelAdapter;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
@Test
|
||||
public void testFtpInboundChannelAdapterComplete() throws Exception{
|
||||
ApplicationContext ac =
|
||||
new ClassPathXmlApplicationContext("FtpInboundChannelAdapterParserTests-context.xml", this.getClass());
|
||||
SourcePollingChannelAdapter adapter = ac.getBean("ftpInbound", SourcePollingChannelAdapter.class);
|
||||
assertFalse(TestUtils.getPropertyValue(adapter, "autoStartup", Boolean.class));
|
||||
PriorityBlockingQueue<?> blockingQueue = TestUtils.getPropertyValue(adapter, "source.fileSource.toBeReceived", PriorityBlockingQueue.class);
|
||||
assertFalse(TestUtils.getPropertyValue(ftpInbound, "autoStartup", Boolean.class));
|
||||
PriorityBlockingQueue<?> blockingQueue = TestUtils.getPropertyValue(ftpInbound, "source.fileSource.toBeReceived", PriorityBlockingQueue.class);
|
||||
Comparator<?> comparator = blockingQueue.comparator();
|
||||
assertNotNull(comparator);
|
||||
assertEquals("ftpInbound", adapter.getComponentName());
|
||||
assertEquals("ftp:inbound-channel-adapter", adapter.getComponentType());
|
||||
assertNotNull(TestUtils.getPropertyValue(adapter, "poller"));
|
||||
assertEquals(ac.getBean("ftpChannel"), TestUtils.getPropertyValue(adapter, "outputChannel"));
|
||||
assertEquals("ftpInbound", ftpInbound.getComponentName());
|
||||
assertEquals("ftp:inbound-channel-adapter", ftpInbound.getComponentType());
|
||||
assertNotNull(TestUtils.getPropertyValue(ftpInbound, "poller"));
|
||||
assertEquals(context.getBean("ftpChannel"), TestUtils.getPropertyValue(ftpInbound, "outputChannel"));
|
||||
FtpInboundFileSynchronizingMessageSource inbound =
|
||||
(FtpInboundFileSynchronizingMessageSource) TestUtils.getPropertyValue(adapter, "source");
|
||||
(FtpInboundFileSynchronizingMessageSource) TestUtils.getPropertyValue(ftpInbound, "source");
|
||||
|
||||
FtpInboundFileSynchronizer fisync =
|
||||
(FtpInboundFileSynchronizer) TestUtils.getPropertyValue(inbound, "synchronizer");
|
||||
@@ -86,7 +106,7 @@ public class FtpInboundChannelAdapterParserTests {
|
||||
assertNotNull(filter);
|
||||
Object sessionFactory = TestUtils.getPropertyValue(fisync, "remoteFileTemplate.sessionFactory");
|
||||
assertTrue(DefaultFtpSessionFactory.class.isAssignableFrom(sessionFactory.getClass()));
|
||||
FileListFilter<?> acceptAllFilter = ac.getBean("acceptAllFilter", FileListFilter.class);
|
||||
FileListFilter<?> acceptAllFilter = context.getBean("acceptAllFilter", FileListFilter.class);
|
||||
assertTrue(TestUtils.getPropertyValue(inbound, "fileSource.scanner.filter.fileFilters", Collection.class).contains(acceptAllFilter));
|
||||
final AtomicReference<Method> genMethod = new AtomicReference<Method>();
|
||||
ReflectionUtils.doWithMethods(AbstractInboundFileSynchronizer.class, new MethodCallback() {
|
||||
@@ -104,49 +124,26 @@ public class FtpInboundChannelAdapterParserTests {
|
||||
|
||||
@Test
|
||||
public void cachingSessionFactory() throws Exception{
|
||||
ApplicationContext ac = new ClassPathXmlApplicationContext(
|
||||
"FtpInboundChannelAdapterParserTests-context.xml", this.getClass());
|
||||
SourcePollingChannelAdapter adapter = ac.getBean("simpleAdapterWithCachedSessions", SourcePollingChannelAdapter.class);
|
||||
Object sessionFactory = TestUtils.getPropertyValue(adapter, "source.synchronizer.remoteFileTemplate.sessionFactory");
|
||||
Object sessionFactory = TestUtils.getPropertyValue(simpleAdapterWithCachedSessions, "source.synchronizer.remoteFileTemplate.sessionFactory");
|
||||
assertEquals(CachingSessionFactory.class, sessionFactory.getClass());
|
||||
FtpInboundFileSynchronizer fisync =
|
||||
TestUtils.getPropertyValue(adapter, "source.synchronizer", FtpInboundFileSynchronizer.class);
|
||||
TestUtils.getPropertyValue(simpleAdapterWithCachedSessions, "source.synchronizer", FtpInboundFileSynchronizer.class);
|
||||
String remoteFileSeparator = (String) TestUtils.getPropertyValue(fisync, "remoteFileSeparator");
|
||||
assertNotNull(remoteFileSeparator);
|
||||
assertEquals("/", remoteFileSeparator);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFtpInboundChannelAdapterCompleteNoId() throws Exception{
|
||||
ApplicationContext ac =
|
||||
new ClassPathXmlApplicationContext("FtpInboundChannelAdapterParserTests-context.xml", this.getClass());
|
||||
Map<String, SourcePollingChannelAdapter> spcas = ac.getBeansOfType(SourcePollingChannelAdapter.class);
|
||||
SourcePollingChannelAdapter adapter = null;
|
||||
for (String key : spcas.keySet()) {
|
||||
if (!key.equals("ftpInbound") && !key.equals("simpleAdapter")){
|
||||
adapter = spcas.get(key);
|
||||
}
|
||||
}
|
||||
assertNotNull(adapter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAutoChannel() {
|
||||
ApplicationContext context =
|
||||
new ClassPathXmlApplicationContext("FtpInboundChannelAdapterParserTests-context.xml", this.getClass());
|
||||
// Auto-created channel
|
||||
MessageChannel autoChannel = context.getBean("autoChannel", MessageChannel.class);
|
||||
SourcePollingChannelAdapter autoChannelAdapter = context.getBean("autoChannel.adapter", SourcePollingChannelAdapter.class);
|
||||
assertSame(autoChannel, TestUtils.getPropertyValue(autoChannelAdapter, "outputChannel"));
|
||||
}
|
||||
|
||||
public static class TestSessionFactoryBean implements FactoryBean<DefaultFtpSessionFactory> {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public DefaultFtpSessionFactory getObject() throws Exception {
|
||||
DefaultFtpSessionFactory factory = mock(DefaultFtpSessionFactory.class);
|
||||
Session session = mock(Session.class);
|
||||
FtpSession session = mock(FtpSession.class);
|
||||
when(factory.getSession()).thenReturn(session);
|
||||
return factory;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
auto-create-directory="false"
|
||||
remote-file-separator=""
|
||||
temporary-file-suffix=".foo"
|
||||
mode="APPEND"
|
||||
remote-filename-generator="fileNameGenerator"
|
||||
order="23"/>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 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.
|
||||
@@ -20,29 +20,34 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.integration.channel.PublishSubscribeChannel;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.file.DefaultFileNameGenerator;
|
||||
import org.springframework.integration.file.FileNameGenerator;
|
||||
import org.springframework.integration.file.remote.handler.FileTransferringMessageHandler;
|
||||
import org.springframework.integration.file.remote.session.CachingSessionFactory;
|
||||
import org.springframework.integration.file.support.FileExistsMode;
|
||||
import org.springframework.integration.ftp.session.DefaultFtpSessionFactory;
|
||||
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
|
||||
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
@@ -50,25 +55,47 @@ import org.springframework.integration.test.util.TestUtils;
|
||||
* @author Gunnar Hillert
|
||||
* @since 2.0
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@DirtiesContext
|
||||
public class FtpOutboundChannelAdapterParserTests {
|
||||
|
||||
private static volatile int adviceCalled;
|
||||
|
||||
@Autowired
|
||||
private EventDrivenConsumer simpleAdapter;
|
||||
|
||||
@Autowired
|
||||
private EventDrivenConsumer advisedAdapter;
|
||||
|
||||
@Autowired
|
||||
private EventDrivenConsumer withBeanExpressions;
|
||||
|
||||
@Autowired
|
||||
private EventDrivenConsumer ftpOutbound;
|
||||
|
||||
@Autowired
|
||||
private EventDrivenConsumer ftpOutbound2;
|
||||
|
||||
@Autowired
|
||||
private EventDrivenConsumer ftpOutbound3;
|
||||
|
||||
@Autowired
|
||||
private PublishSubscribeChannel ftpChannel;
|
||||
|
||||
@Autowired
|
||||
private FileNameGenerator fileNameGenerator;
|
||||
|
||||
@Test
|
||||
public void testFtpOutboundChannelAdapterComplete() throws Exception{
|
||||
ApplicationContext ac =
|
||||
new ClassPathXmlApplicationContext("FtpOutboundChannelAdapterParserTests-context.xml", this.getClass());
|
||||
Object consumer = ac.getBean("ftpOutbound");
|
||||
assertTrue(consumer instanceof EventDrivenConsumer);
|
||||
PublishSubscribeChannel channel = ac.getBean("ftpChannel", PublishSubscribeChannel.class);
|
||||
assertEquals(channel, TestUtils.getPropertyValue(consumer, "inputChannel"));
|
||||
assertEquals("ftpOutbound", ((EventDrivenConsumer)consumer).getComponentName());
|
||||
FileTransferringMessageHandler<?> handler = TestUtils.getPropertyValue(consumer, "handler", FileTransferringMessageHandler.class);
|
||||
assertEquals(ftpChannel, TestUtils.getPropertyValue(ftpOutbound, "inputChannel"));
|
||||
assertEquals("ftpOutbound", ftpOutbound.getComponentName());
|
||||
FileTransferringMessageHandler<?> handler = TestUtils.getPropertyValue(ftpOutbound, "handler", FileTransferringMessageHandler.class);
|
||||
String remoteFileSeparator = (String) TestUtils.getPropertyValue(handler, "remoteFileTemplate.remoteFileSeparator");
|
||||
assertNotNull(remoteFileSeparator);
|
||||
assertEquals(".foo", TestUtils.getPropertyValue(handler, "remoteFileTemplate.temporaryFileSuffix", String.class));
|
||||
assertEquals("", remoteFileSeparator);
|
||||
assertEquals(ac.getBean("fileNameGenerator"), TestUtils.getPropertyValue(handler, "remoteFileTemplate.fileNameGenerator"));
|
||||
assertEquals(this.fileNameGenerator, TestUtils.getPropertyValue(handler, "remoteFileTemplate.fileNameGenerator"));
|
||||
assertEquals("UTF-8", TestUtils.getPropertyValue(handler, "remoteFileTemplate.charset"));
|
||||
assertNotNull(TestUtils.getPropertyValue(handler, "remoteFileTemplate.directoryExpressionProcessor"));
|
||||
assertNotNull(TestUtils.getPropertyValue(handler, "remoteFileTemplate.temporaryDirectoryExpressionProcessor"));
|
||||
@@ -82,13 +109,15 @@ public class FtpOutboundChannelAdapterParserTests {
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<MessageHandler> handlers = (Set<MessageHandler>) TestUtils
|
||||
.getPropertyValue(
|
||||
TestUtils.getPropertyValue(channel, "dispatcher"),
|
||||
TestUtils.getPropertyValue(ftpChannel, "dispatcher"),
|
||||
"handlers");
|
||||
Iterator<MessageHandler> iterator = handlers.iterator();
|
||||
assertSame(TestUtils.getPropertyValue(ac.getBean("ftpOutbound2"), "handler"), iterator.next());
|
||||
assertSame(TestUtils.getPropertyValue(this.ftpOutbound2, "handler"), iterator.next());
|
||||
assertSame(handler, iterator.next());
|
||||
assertEquals(FileExistsMode.APPEND, TestUtils.getPropertyValue(ftpOutbound, "handler.mode"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
@Test(expected=BeanCreationException.class)
|
||||
public void testFailWithEmptyRfsAndAcdTrue() throws Exception{
|
||||
new ClassPathXmlApplicationContext("FtpOutboundChannelAdapterParserTests-fail.xml", this.getClass());
|
||||
@@ -96,40 +125,30 @@ public class FtpOutboundChannelAdapterParserTests {
|
||||
|
||||
@Test
|
||||
public void cachingByDefault() {
|
||||
ApplicationContext ac = new ClassPathXmlApplicationContext(
|
||||
"FtpOutboundChannelAdapterParserTests-context.xml", this.getClass());
|
||||
Object adapter = ac.getBean("simpleAdapter");
|
||||
Object sfProperty = TestUtils.getPropertyValue(adapter, "handler.remoteFileTemplate.sessionFactory");
|
||||
Object sfProperty = TestUtils.getPropertyValue(simpleAdapter, "handler.remoteFileTemplate.sessionFactory");
|
||||
assertEquals(CachingSessionFactory.class, sfProperty.getClass());
|
||||
Object innerSfProperty = TestUtils.getPropertyValue(sfProperty, "sessionFactory");
|
||||
assertEquals(DefaultFtpSessionFactory.class, innerSfProperty.getClass());
|
||||
assertEquals(FileExistsMode.REPLACE, TestUtils.getPropertyValue(simpleAdapter, "handler.mode"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adviceChain() {
|
||||
ApplicationContext ac = new ClassPathXmlApplicationContext(
|
||||
"FtpOutboundChannelAdapterParserTests-context.xml", this.getClass());
|
||||
Object adapter = ac.getBean("advisedAdapter");
|
||||
MessageHandler handler = TestUtils.getPropertyValue(adapter, "handler", MessageHandler.class);
|
||||
MessageHandler handler = TestUtils.getPropertyValue(advisedAdapter, "handler", MessageHandler.class);
|
||||
handler.handleMessage(new GenericMessage<String>("foo"));
|
||||
assertEquals(1, adviceCalled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTemporaryFileSuffix() {
|
||||
ApplicationContext ac =
|
||||
new ClassPathXmlApplicationContext("FtpOutboundChannelAdapterParserTests-context.xml", this.getClass());
|
||||
FileTransferringMessageHandler<?> handler =
|
||||
(FileTransferringMessageHandler<?>)TestUtils.getPropertyValue(ac.getBean("ftpOutbound3"), "handler");
|
||||
assertFalse((Boolean)TestUtils.getPropertyValue(handler,"remoteFileTemplate.useTemporaryFileName"));
|
||||
FileTransferringMessageHandler<?> handler =
|
||||
(FileTransferringMessageHandler<?>)TestUtils.getPropertyValue(ftpOutbound3, "handler");
|
||||
assertFalse((Boolean)TestUtils.getPropertyValue(handler,"remoteFileTemplate.useTemporaryFileName"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBeanExpressions() throws Exception{
|
||||
ApplicationContext ac =
|
||||
new ClassPathXmlApplicationContext("FtpOutboundChannelAdapterParserTests-context.xml", this.getClass());
|
||||
Object consumer = ac.getBean("withBeanExpressions");
|
||||
FileTransferringMessageHandler<?> handler = TestUtils.getPropertyValue(consumer, "handler", FileTransferringMessageHandler.class);
|
||||
FileTransferringMessageHandler<?> handler = TestUtils.getPropertyValue(withBeanExpressions, "handler", FileTransferringMessageHandler.class);
|
||||
ExpressionEvaluatingMessageProcessor<?> dirExpProc = TestUtils.getPropertyValue(handler,
|
||||
"remoteFileTemplate.directoryExpressionProcessor", ExpressionEvaluatingMessageProcessor.class);
|
||||
assertNotNull(dirExpProc);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 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.
|
||||
@@ -46,6 +46,7 @@ import org.springframework.integration.support.MessageBuilder;
|
||||
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.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
@@ -60,6 +61,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@DirtiesContext
|
||||
public class FtpOutboundGatewayParserTests {
|
||||
|
||||
@Autowired
|
||||
|
||||
@@ -19,35 +19,43 @@ package org.springframework.integration.ftp.config;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
|
||||
import org.springframework.integration.ftp.inbound.FtpInboundFileSynchronizer;
|
||||
import org.springframework.integration.ftp.inbound.FtpInboundFileSynchronizingMessageSource;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gunnar Hillert
|
||||
* @author Gary Russell
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@DirtiesContext
|
||||
public class FtpsInboundChannelAdapterParserTests {
|
||||
|
||||
@Autowired
|
||||
private SourcePollingChannelAdapter ftpInbound;
|
||||
|
||||
@Autowired
|
||||
private MessageChannel ftpChannel;
|
||||
|
||||
@Test
|
||||
public void testFtpsInboundChannelAdapterComplete() throws Exception{
|
||||
|
||||
ApplicationContext ac =
|
||||
new ClassPathXmlApplicationContext("FtpsInboundChannelAdapterParserTests-context.xml", this.getClass());
|
||||
SourcePollingChannelAdapter adapter = ac.getBean("ftpInbound", SourcePollingChannelAdapter.class);
|
||||
assertEquals("ftpInbound", adapter.getComponentName());
|
||||
assertEquals("ftp:inbound-channel-adapter", adapter.getComponentType());
|
||||
assertNotNull(TestUtils.getPropertyValue(adapter, "poller"));
|
||||
assertEquals(ac.getBean("ftpChannel"), TestUtils.getPropertyValue(adapter, "outputChannel"));
|
||||
assertEquals("ftpInbound", ftpInbound.getComponentName());
|
||||
assertEquals("ftp:inbound-channel-adapter", ftpInbound.getComponentType());
|
||||
assertNotNull(TestUtils.getPropertyValue(ftpInbound, "poller"));
|
||||
assertEquals(this.ftpChannel, TestUtils.getPropertyValue(ftpInbound, "outputChannel"));
|
||||
FtpInboundFileSynchronizingMessageSource inbound =
|
||||
(FtpInboundFileSynchronizingMessageSource) TestUtils.getPropertyValue(adapter, "source");
|
||||
(FtpInboundFileSynchronizingMessageSource) TestUtils.getPropertyValue(ftpInbound, "source");
|
||||
|
||||
FtpInboundFileSynchronizer fisync =
|
||||
(FtpInboundFileSynchronizer) TestUtils.getPropertyValue(inbound, "synchronizer");
|
||||
@@ -55,19 +63,4 @@ public class FtpsInboundChannelAdapterParserTests {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFtpsInboundChannelAdapterCompleteNoId() throws Exception{
|
||||
|
||||
ApplicationContext ac =
|
||||
new ClassPathXmlApplicationContext("FtpsInboundChannelAdapterParserTests-context.xml", this.getClass());
|
||||
Map<String, SourcePollingChannelAdapter> spcas = ac.getBeansOfType(SourcePollingChannelAdapter.class);
|
||||
SourcePollingChannelAdapter adapter = null;
|
||||
for (String key : spcas.keySet()) {
|
||||
if (!key.equals("ftpInbound")){
|
||||
adapter = spcas.get(key);
|
||||
}
|
||||
}
|
||||
assertNotNull(adapter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 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.
|
||||
@@ -20,13 +20,18 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.file.FileNameGenerator;
|
||||
import org.springframework.integration.file.remote.handler.FileTransferringMessageHandler;
|
||||
import org.springframework.integration.ftp.session.DefaultFtpsSessionFactory;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
@@ -34,17 +39,27 @@ import org.springframework.integration.test.util.TestUtils;
|
||||
* @author Gary Russell
|
||||
* @since 2.0
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@DirtiesContext
|
||||
public class FtpsOutboundChannelAdapterParserTests {
|
||||
|
||||
@Autowired
|
||||
private EventDrivenConsumer ftpOutbound;
|
||||
|
||||
@Autowired
|
||||
private MessageChannel ftpChannel;
|
||||
|
||||
@Autowired
|
||||
private FileNameGenerator fileNameGenerator;
|
||||
|
||||
@Test
|
||||
public void testFtpsOutboundChannelAdapterComplete() throws Exception{
|
||||
ApplicationContext ac = new ClassPathXmlApplicationContext("FtpsOutboundChannelAdapterParserTests-context.xml", this.getClass());
|
||||
Object consumer = ac.getBean("ftpOutbound");
|
||||
assertTrue(consumer instanceof EventDrivenConsumer);
|
||||
assertEquals(ac.getBean("ftpChannel"), TestUtils.getPropertyValue(consumer, "inputChannel"));
|
||||
assertEquals("ftpOutbound", ((EventDrivenConsumer)consumer).getComponentName());
|
||||
FileTransferringMessageHandler<?> handler = TestUtils.getPropertyValue(consumer, "handler", FileTransferringMessageHandler.class);
|
||||
assertEquals(ac.getBean("fileNameGenerator"), TestUtils.getPropertyValue(handler, "remoteFileTemplate.fileNameGenerator"));
|
||||
assertTrue(ftpOutbound instanceof EventDrivenConsumer);
|
||||
assertEquals(this.ftpChannel, TestUtils.getPropertyValue(ftpOutbound, "inputChannel"));
|
||||
assertEquals("ftpOutbound", ftpOutbound.getComponentName());
|
||||
FileTransferringMessageHandler<?> handler = TestUtils.getPropertyValue(ftpOutbound, "handler", FileTransferringMessageHandler.class);
|
||||
assertEquals(this.fileNameGenerator, TestUtils.getPropertyValue(handler, "remoteFileTemplate.fileNameGenerator"));
|
||||
assertEquals("UTF-8", TestUtils.getPropertyValue(handler, "remoteFileTemplate.charset"));
|
||||
DefaultFtpsSessionFactory sf = TestUtils.getPropertyValue(handler, "remoteFileTemplate.sessionFactory", DefaultFtpsSessionFactory.class);
|
||||
assertEquals("localhost", TestUtils.getPropertyValue(sf, "host"));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 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.
|
||||
@@ -48,20 +48,20 @@ import org.mockito.stubbing.Answer;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.expression.common.LiteralExpression;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.integration.file.FileNameGenerator;
|
||||
import org.springframework.integration.file.remote.FileInfo;
|
||||
import org.springframework.integration.file.remote.RemoteFileTemplate;
|
||||
import org.springframework.integration.file.remote.handler.FileTransferringMessageHandler;
|
||||
import org.springframework.integration.ftp.session.AbstractFtpSessionFactory;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
/**
|
||||
@@ -207,17 +207,20 @@ public class FtpOutboundTests {
|
||||
File destFile = new File(targetDir, srcFile.getName());
|
||||
destFile.deleteOnExit();
|
||||
|
||||
ApplicationContext context = new ClassPathXmlApplicationContext("FtpOutboundInsideChainTests-context.xml", getClass());
|
||||
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(
|
||||
"FtpOutboundInsideChainTests-context.xml", getClass());
|
||||
|
||||
MessageChannel channel = context.getBean("outboundChainChannel", MessageChannel.class);
|
||||
|
||||
channel.send(new GenericMessage<File>(srcFile));
|
||||
assertTrue("destination file was not created", destFile.exists());
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test //INT-2275
|
||||
public void testFtpOutboundGatewayInsideChain() throws Exception {
|
||||
ApplicationContext context = new ClassPathXmlApplicationContext("FtpOutboundInsideChainTests-context.xml", getClass());
|
||||
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(
|
||||
"FtpOutboundInsideChainTests-context.xml", getClass());
|
||||
|
||||
MessageChannel channel = context.getBean("ftpOutboundGatewayInsideChain", MessageChannel.class);
|
||||
|
||||
@@ -235,6 +238,7 @@ public class FtpOutboundTests {
|
||||
for (FileInfo<?> remoteFile : remoteFiles) {
|
||||
assertTrue(files.contains(remoteFile.getFilename()));
|
||||
}
|
||||
context.close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<bean id="ftpServer" class="org.springframework.integration.ftp.TesFtpServer">
|
||||
<bean id="ftpServer" class="org.springframework.integration.ftp.TestFtpServer">
|
||||
<constructor-arg value="FtpServerOutboundTests"/>
|
||||
</bean>
|
||||
|
||||
@@ -104,4 +104,35 @@
|
||||
remote-directory="ftpTarget"
|
||||
reply-channel="output"/>
|
||||
|
||||
<int:channel id="appending" />
|
||||
|
||||
<int-ftp:outbound-channel-adapter id="appender"
|
||||
session-factory="ftpSessionFactory"
|
||||
channel="appending"
|
||||
mode="APPEND"
|
||||
use-temporary-file-name="false"
|
||||
remote-directory="ftpTarget"
|
||||
auto-create-directory="true"
|
||||
remote-file-separator="/" />
|
||||
|
||||
<int:channel id="ignoring" />
|
||||
|
||||
<int-ftp:outbound-channel-adapter id="ignore"
|
||||
session-factory="ftpSessionFactory"
|
||||
channel="ignoring"
|
||||
mode="IGNORE"
|
||||
remote-directory="ftpTarget"
|
||||
auto-create-directory="true"
|
||||
remote-file-separator="/" />
|
||||
|
||||
<int:channel id="failing" />
|
||||
|
||||
<int-ftp:outbound-channel-adapter id="fail"
|
||||
session-factory="ftpSessionFactory"
|
||||
channel="failing"
|
||||
mode="FAIL"
|
||||
remote-directory="ftpTarget"
|
||||
auto-create-directory="true"
|
||||
remote-file-separator="/" />
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.integration.ftp.outbound;
|
||||
|
||||
import static org.hamcrest.Matchers.anyOf;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -42,12 +43,17 @@ import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.file.FileHeaders;
|
||||
import org.springframework.integration.file.remote.InputStreamCallback;
|
||||
import org.springframework.integration.file.remote.RemoteFileTemplate;
|
||||
import org.springframework.integration.file.remote.SessionCallback;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
import org.springframework.integration.ftp.TesFtpServer;
|
||||
import org.springframework.integration.ftp.TestFtpServer;
|
||||
import org.springframework.integration.ftp.session.DefaultFtpSessionFactory;
|
||||
import org.springframework.integration.ftp.session.FtpRemoteFileTemplate;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
@@ -65,10 +71,10 @@ import org.springframework.util.FileCopyUtils;
|
||||
public class FtpServerOutboundTests {
|
||||
|
||||
@Autowired
|
||||
private TesFtpServer ftpServer;
|
||||
private TestFtpServer ftpServer;
|
||||
|
||||
@Autowired
|
||||
private SessionFactory<FTPFile> ftpSessionFactory;
|
||||
private DefaultFtpSessionFactory ftpSessionFactory;
|
||||
|
||||
@Autowired
|
||||
private PollableChannel output;
|
||||
@@ -97,6 +103,15 @@ public class FtpServerOutboundTests {
|
||||
@Autowired
|
||||
private DirectChannel inboundMPutRecursiveFiltered;
|
||||
|
||||
@Autowired
|
||||
private DirectChannel appending;
|
||||
|
||||
@Autowired
|
||||
private DirectChannel ignoring;
|
||||
|
||||
@Autowired
|
||||
private DirectChannel failing;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.ftpServer.recursiveDelete(ftpServer.getTargetLocalDirectory());
|
||||
@@ -304,4 +319,39 @@ public class FtpServerOutboundTests {
|
||||
equalTo("ftpTarget/subLocalSource/subLocalSource1.txt")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInt3412FileMode() {
|
||||
Message<String> m = MessageBuilder.withPayload("foo")
|
||||
.setHeader(FileHeaders.FILENAME, "appending.txt")
|
||||
.build();
|
||||
appending.send(m);
|
||||
appending.send(m);
|
||||
|
||||
FtpRemoteFileTemplate template = new FtpRemoteFileTemplate(ftpSessionFactory);
|
||||
assertLength6(template);
|
||||
|
||||
ignoring.send(m);
|
||||
assertLength6(template);
|
||||
try {
|
||||
failing.send(m);
|
||||
fail("Expected exception");
|
||||
}
|
||||
catch (MessagingException e) {
|
||||
assertThat(e.getCause().getCause().getMessage(), containsString("The destination file already exists"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void assertLength6(FtpRemoteFileTemplate template) {
|
||||
FTPFile[] files = template.execute(new SessionCallback<FTPFile, FTPFile[]>() {
|
||||
|
||||
@Override
|
||||
public FTPFile[] doInSession(Session<FTPFile> session) throws IOException {
|
||||
return session.list("ftpTarget/appending.txt");
|
||||
}
|
||||
});
|
||||
assertEquals(1, files.length);
|
||||
assertEquals(6, files[0].getSize());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<?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-ftp="http://www.springframework.org/schema/integration/ftp"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/integration/ftp
|
||||
http://www.springframework.org/schema/integration/ftp/spring-integration-ftp.xsd
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<bean id="ftpServer" class="org.springframework.integration.ftp.TestFtpServer">
|
||||
<constructor-arg value="FtpRemoteFileTemplateTests"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.integration.ftp.session;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.expression.common.LiteralExpression;
|
||||
import org.springframework.integration.file.DefaultFileNameGenerator;
|
||||
import org.springframework.integration.file.remote.ClientCallbackWithoutResult;
|
||||
import org.springframework.integration.file.remote.SessionCallback;
|
||||
import org.springframework.integration.file.remote.SessionCallbackWithoutResult;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.integration.ftp.TestFtpServer;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @since 4.1
|
||||
*
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class FtpRemoteFileTemplateTests {
|
||||
|
||||
@Autowired
|
||||
private TestFtpServer ftpServer;
|
||||
|
||||
@Autowired
|
||||
private DefaultFtpSessionFactory sessionFactory;
|
||||
|
||||
@Before
|
||||
@After
|
||||
public void setup() {
|
||||
this.ftpServer.recursiveDelete(ftpServer.getTargetLocalDirectory());
|
||||
this.ftpServer.recursiveDelete(ftpServer.getTargetFtpDirectory());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testINT3412AppendStatRmdir() {
|
||||
FtpRemoteFileTemplate template = new FtpRemoteFileTemplate(sessionFactory);
|
||||
DefaultFileNameGenerator fileNameGenerator = new DefaultFileNameGenerator();
|
||||
fileNameGenerator.setExpression("'foobar.txt'");
|
||||
template.setFileNameGenerator(fileNameGenerator);
|
||||
template.setRemoteDirectoryExpression(new LiteralExpression("foo/"));
|
||||
template.setUseTemporaryFileName(false);
|
||||
template.execute(new SessionCallback<FTPFile, Boolean>() {
|
||||
|
||||
@Override
|
||||
public Boolean doInSession(Session<FTPFile> session) throws IOException {
|
||||
session.mkdir("foo/");
|
||||
return session.mkdir("foo/bar/");
|
||||
}
|
||||
|
||||
});
|
||||
template.append(new GenericMessage<String>("foo"));
|
||||
template.append(new GenericMessage<String>("bar"));
|
||||
assertTrue(template.exists("foo/foobar.txt"));
|
||||
template.executeWithClient(new ClientCallbackWithoutResult<FTPClient>() {
|
||||
|
||||
@Override
|
||||
public void doWithClientWithoutResult(FTPClient client) {
|
||||
try {
|
||||
FTPFile[] files = client.listFiles("foo/foobar.txt");
|
||||
assertEquals(6, files[0].getSize());
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
template.execute(new SessionCallbackWithoutResult<FTPFile>() {
|
||||
|
||||
@Override
|
||||
public void doInSessionWithoutResult(Session<FTPFile> session) throws IOException {
|
||||
assertTrue(session.remove("foo/foobar.txt"));
|
||||
assertTrue(session.rmdir("foo/bar/"));
|
||||
FTPFile[] files = session.list("foo/");
|
||||
assertEquals(0, files.length);
|
||||
assertTrue(session.rmdir("foo/"));
|
||||
}
|
||||
});
|
||||
assertFalse(template.exists("foo"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.integration.sftp.config;
|
||||
|
||||
import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHandler;
|
||||
import org.springframework.integration.file.config.RemoteFileOutboundChannelAdapterParser;
|
||||
|
||||
/**
|
||||
* Provides namespace support for using SFTP.
|
||||
@@ -30,9 +29,10 @@ import org.springframework.integration.file.config.RemoteFileOutboundChannelAdap
|
||||
*/
|
||||
public class SftpNamespaceHandler extends AbstractIntegrationNamespaceHandler {
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
registerBeanDefinitionParser("inbound-channel-adapter", new SftpInboundChannelAdapterParser());
|
||||
registerBeanDefinitionParser("outbound-channel-adapter", new RemoteFileOutboundChannelAdapterParser());
|
||||
registerBeanDefinitionParser("outbound-channel-adapter", new SftpOutboundChannelAdapterParser());
|
||||
registerBeanDefinitionParser("outbound-gateway", new SftpOutboundGatewayParser());
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.integration.sftp.config;
|
||||
|
||||
import org.springframework.integration.file.config.RemoteFileOutboundChannelAdapterParser;
|
||||
import org.springframework.integration.file.remote.RemoteFileOperations;
|
||||
import org.springframework.integration.sftp.session.SftpRemoteFileTemplate;
|
||||
|
||||
/**
|
||||
* Parser for SFTP Outbound Channel Adapters.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 4.1
|
||||
*
|
||||
*/
|
||||
public class SftpOutboundChannelAdapterParser extends RemoteFileOutboundChannelAdapterParser {
|
||||
|
||||
@Override
|
||||
protected Class<? extends RemoteFileOperations<?>> getTemplateClass() {
|
||||
return SftpRemoteFileTemplate.class;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 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.
|
||||
@@ -16,9 +16,11 @@
|
||||
package org.springframework.integration.sftp.config;
|
||||
|
||||
import org.springframework.integration.file.config.AbstractRemoteFileOutboundGatewayParser;
|
||||
import org.springframework.integration.file.remote.RemoteFileOperations;
|
||||
import org.springframework.integration.sftp.filters.SftpRegexPatternFileListFilter;
|
||||
import org.springframework.integration.sftp.filters.SftpSimplePatternFileListFilter;
|
||||
import org.springframework.integration.sftp.gateway.SftpOutboundGateway;
|
||||
import org.springframework.integration.sftp.session.SftpRemoteFileTemplate;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
@@ -28,6 +30,7 @@ import org.springframework.integration.sftp.gateway.SftpOutboundGateway;
|
||||
*/
|
||||
public class SftpOutboundGatewayParser extends AbstractRemoteFileOutboundGatewayParser {
|
||||
|
||||
@Override
|
||||
public String getGatewayClassName() {
|
||||
return SftpOutboundGateway.class.getName();
|
||||
}
|
||||
@@ -42,4 +45,9 @@ public class SftpOutboundGatewayParser extends AbstractRemoteFileOutboundGateway
|
||||
return SftpRegexPatternFileListFilter.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<? extends RemoteFileOperations<?>> getTemplateClass() {
|
||||
return SftpRemoteFileTemplate.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
import org.springframework.integration.file.remote.session.SharedSessionCapable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -314,7 +313,7 @@ public class DefaultSftpSessionFactory implements SessionFactory<LsEntry>, Share
|
||||
|
||||
|
||||
@Override
|
||||
public Session<LsEntry> getSession() {
|
||||
public SftpSession getSession() {
|
||||
Assert.hasText(this.host, "host must not be empty");
|
||||
Assert.hasText(this.user, "user must not be empty");
|
||||
Assert.isTrue(this.port >= 0, "port must be a positive number");
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.integration.sftp.session;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.integration.file.remote.ClientCallback;
|
||||
import org.springframework.integration.file.remote.RemoteFileTemplate;
|
||||
import org.springframework.integration.file.remote.SessionCallback;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
import com.jcraft.jsch.SftpException;
|
||||
|
||||
/**
|
||||
* SFTP version of {@code RemoteFileTemplate} providing type-safe access to
|
||||
* the underlying ChannelSftp object.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 4.1
|
||||
*
|
||||
*/
|
||||
public class SftpRemoteFileTemplate extends RemoteFileTemplate<LsEntry> {
|
||||
|
||||
public SftpRemoteFileTemplate(SessionFactory<LsEntry> sessionFactory) {
|
||||
super(sessionFactory);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T, C> T executeWithClient(final ClientCallback<C, T> callback) {
|
||||
return doExecuteWithClient((ClientCallback<ChannelSftp, T>) callback);
|
||||
}
|
||||
|
||||
protected <T> T doExecuteWithClient(final ClientCallback<ChannelSftp, T> callback) {
|
||||
return execute(new SessionCallback<LsEntry, T>() {
|
||||
|
||||
@Override
|
||||
public T doInSession(Session<LsEntry> session) throws IOException {
|
||||
return callback.doWithClient((ChannelSftp) session.getClientInstance());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(final String path) {
|
||||
return executeWithClient(new ClientCallback<ChannelSftp, Boolean>() {
|
||||
|
||||
@Override
|
||||
public Boolean doWithClient(ChannelSftp client) {
|
||||
try {
|
||||
return client.stat(path) != null;
|
||||
}
|
||||
catch (SftpException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -47,7 +47,7 @@ import com.jcraft.jsch.SftpException;
|
||||
* @author Gary Russell
|
||||
* @since 2.0
|
||||
*/
|
||||
class SftpSession implements Session<LsEntry> {
|
||||
public class SftpSession implements Session<LsEntry> {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
@@ -159,6 +159,17 @@ class SftpSession implements Session<LsEntry> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void append(InputStream inputStream, String destination) throws IOException {
|
||||
Assert.state(this.channel != null, "session is not connected");
|
||||
try {
|
||||
this.channel.put(inputStream, destination, ChannelSftp.APPEND);
|
||||
}
|
||||
catch (SftpException e) {
|
||||
throw new NestedIOException("failed to write file", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
this.closed = true;
|
||||
@@ -223,6 +234,17 @@ class SftpSession implements Session<LsEntry> {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rmdir(String remoteDirectory) throws IOException {
|
||||
try {
|
||||
this.channel.rmdir(remoteDirectory);
|
||||
}
|
||||
catch (SftpException e) {
|
||||
throw new NestedIOException("failed to remove remote directory '" + remoteDirectory + "'.", e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(String path) {
|
||||
try {
|
||||
@@ -251,4 +273,9 @@ class SftpSession implements Session<LsEntry> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelSftp getClientInstance() {
|
||||
return this.channel;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.integration.sftp;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.sshd.SshServer;
|
||||
import org.apache.sshd.common.NamedFactory;
|
||||
import org.apache.sshd.common.file.FileSystemView;
|
||||
import org.apache.sshd.common.file.nativefs.NativeFileSystemFactory;
|
||||
import org.apache.sshd.common.file.nativefs.NativeFileSystemView;
|
||||
import org.apache.sshd.server.Command;
|
||||
import org.apache.sshd.server.PasswordAuthenticator;
|
||||
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
|
||||
import org.apache.sshd.server.session.ServerSession;
|
||||
import org.apache.sshd.server.sftp.SftpSubsystem;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.integration.sftp.session.DefaultSftpSessionFactory;
|
||||
import org.springframework.util.SocketUtils;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @since 4.1
|
||||
*
|
||||
*/
|
||||
public class TestSftpServer implements InitializingBean, DisposableBean {
|
||||
|
||||
private final SshServer server = SshServer.setUpDefaultServer();
|
||||
|
||||
private final int port = SocketUtils.findAvailableTcpPort();
|
||||
|
||||
private final TemporaryFolder sftpFolder;
|
||||
|
||||
private final TemporaryFolder localFolder;
|
||||
|
||||
private volatile File sftpRootFolder;
|
||||
|
||||
private volatile File sourceSftpDirectory;
|
||||
|
||||
private volatile File targetSftpDirectory;
|
||||
|
||||
private volatile File sourceLocalDirectory;
|
||||
|
||||
private volatile File targetLocalDirectory;
|
||||
|
||||
public TestSftpServer() {
|
||||
this.sftpFolder = new TemporaryFolder() {
|
||||
|
||||
@Override
|
||||
public void create() throws IOException {
|
||||
super.create();
|
||||
sftpRootFolder = this.newFolder("test");
|
||||
sourceSftpDirectory = new File(sftpRootFolder, "sftpSource");
|
||||
sourceSftpDirectory.mkdir();
|
||||
File file = new File(sourceSftpDirectory, "sftpSource1.txt");
|
||||
file.createNewFile();
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
fos.write("source1".getBytes());
|
||||
fos.close();
|
||||
file = new File(sourceSftpDirectory, "sftpSource2.txt");
|
||||
file.createNewFile();
|
||||
fos = new FileOutputStream(file);
|
||||
fos.write("source2".getBytes());
|
||||
fos.close();
|
||||
|
||||
File subSourceFtpDirectory = new File(sourceSftpDirectory, "subSftpSource");
|
||||
subSourceFtpDirectory.mkdir();
|
||||
file = new File(subSourceFtpDirectory, "subSftpSource1.txt");
|
||||
file.createNewFile();
|
||||
fos = new FileOutputStream(file);
|
||||
fos.write("subSource1".getBytes());
|
||||
fos.close();
|
||||
|
||||
targetSftpDirectory = new File(sftpRootFolder, "sftpTarget");
|
||||
targetSftpDirectory.mkdir();
|
||||
}
|
||||
};
|
||||
this.localFolder = new TemporaryFolder() {
|
||||
|
||||
@Override
|
||||
public void create() throws IOException {
|
||||
super.create();
|
||||
File rootFolder = this.newFolder("test");
|
||||
sourceLocalDirectory = new File(rootFolder, "localSource");
|
||||
sourceLocalDirectory.mkdirs();
|
||||
File file = new File(sourceLocalDirectory, "localSource1.txt");
|
||||
file.createNewFile();
|
||||
file = new File(sourceLocalDirectory, "localSource2.txt");
|
||||
file.createNewFile();
|
||||
|
||||
File subSourceLocalDirectory = new File(sourceLocalDirectory, "subLocalSource");
|
||||
subSourceLocalDirectory.mkdir();
|
||||
file = new File(subSourceLocalDirectory, "subLocalSource1.txt");
|
||||
file.createNewFile();
|
||||
|
||||
targetLocalDirectory = new File(rootFolder, "slocalTarget");
|
||||
targetLocalDirectory.mkdir();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
server.setPasswordAuthenticator(new PasswordAuthenticator() {
|
||||
|
||||
@Override
|
||||
public boolean authenticate(String arg0, String arg1, ServerSession arg2) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
server.setPort(port);
|
||||
server.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("hostkey.ser"));
|
||||
SftpSubsystem.Factory sftp = new SftpSubsystem.Factory();
|
||||
server.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(sftp));
|
||||
server.setFileSystemFactory(new NativeFileSystemFactory() {
|
||||
|
||||
@Override
|
||||
public FileSystemView createFileSystemView(org.apache.sshd.common.Session session) {
|
||||
return new NativeFileSystemView(session.getUsername(), false) {
|
||||
|
||||
@Override
|
||||
public String getVirtualUserDir() {
|
||||
return sftpRootFolder.getAbsolutePath();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
});
|
||||
this.sftpFolder.create();
|
||||
this.localFolder.create();
|
||||
server.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
this.server.stop();
|
||||
this.sftpFolder.delete();
|
||||
this.localFolder.delete();
|
||||
}
|
||||
|
||||
public File getSourceLocalDirectory() {
|
||||
return this.sourceLocalDirectory;
|
||||
}
|
||||
|
||||
public File getTargetLocalDirectory() {
|
||||
return this.targetLocalDirectory;
|
||||
}
|
||||
|
||||
public String getTargetLocalDirectoryName() {
|
||||
return this.targetLocalDirectory.getAbsolutePath() + File.separator;
|
||||
}
|
||||
|
||||
public File getTargetSftpDirectory() {
|
||||
return this.targetSftpDirectory;
|
||||
}
|
||||
|
||||
public void recursiveDelete(File file) {
|
||||
File[] files = file.listFiles();
|
||||
if (files != null) {
|
||||
for (File each : files) {
|
||||
recursiveDelete(each);
|
||||
}
|
||||
}
|
||||
if (!(file.equals(this.targetSftpDirectory) || file.equals(this.targetLocalDirectory))) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
public DefaultSftpSessionFactory getSessionFactory() {
|
||||
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
|
||||
factory.setHost("localhost");
|
||||
factory.setPort(this.port);
|
||||
factory.setUser("foo");
|
||||
factory.setPassword("foo");
|
||||
return factory;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.integration.sftp;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.integration.sftp.session.DefaultSftpSessionFactory;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @since 4.1
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
public class TestSftpServerConfig {
|
||||
|
||||
@Bean
|
||||
public TestSftpServer sftpServer() {
|
||||
return new TestSftpServer();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DefaultSftpSessionFactory sftpSessionFactory(TestSftpServer server) {
|
||||
return sftpServer().getSessionFactory();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -45,11 +45,11 @@ import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.integration.expression.ExpressionUtils;
|
||||
import org.springframework.integration.file.filters.CompositeFileListFilter;
|
||||
import org.springframework.integration.file.filters.FileListFilter;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.integration.metadata.PropertiesPersistingMetadataStore;
|
||||
import org.springframework.integration.sftp.filters.SftpPersistentAcceptOnceFileListFilter;
|
||||
import org.springframework.integration.sftp.filters.SftpRegexPatternFileListFilter;
|
||||
import org.springframework.integration.sftp.session.DefaultSftpSessionFactory;
|
||||
import org.springframework.integration.sftp.session.SftpSession;
|
||||
import org.springframework.integration.sftp.session.SftpTestSessionFactory;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
@@ -93,7 +93,6 @@ public class SftpInboundRemoteFileSystemSynchronizerTests {
|
||||
ftpSessionFactory.setPassword("frog");
|
||||
ftpSessionFactory.setHost("foo.com");
|
||||
|
||||
|
||||
SftpInboundFileSynchronizer synchronizer = spy(new SftpInboundFileSynchronizer(ftpSessionFactory));
|
||||
synchronizer.setDeleteRemoteFiles(true);
|
||||
synchronizer.setPreserveTimestamp(true);
|
||||
@@ -168,7 +167,7 @@ public class SftpInboundRemoteFileSystemSynchronizerTests {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Session<LsEntry> getSession() {
|
||||
public SftpSession getSession() {
|
||||
if (this.sftpEntries.size() == 0) {
|
||||
this.init();
|
||||
}
|
||||
@@ -184,7 +183,8 @@ public class SftpInboundRemoteFileSystemSynchronizerTests {
|
||||
|
||||
when(jschSession.openChannel("sftp")).thenReturn(channel);
|
||||
return SftpTestSessionFactory.createSftpSession(jschSession);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException("Failed to create mock sftp session", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ import org.springframework.integration.file.remote.session.CachingSessionFactory
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
import org.springframework.integration.sftp.session.DefaultSftpSessionFactory;
|
||||
import org.springframework.integration.sftp.session.SftpSession;
|
||||
import org.springframework.integration.sftp.session.SftpTestSessionFactory;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
@@ -375,7 +376,7 @@ public class SftpOutboundTests {
|
||||
public static class TestSftpSessionFactory extends DefaultSftpSessionFactory {
|
||||
|
||||
@Override
|
||||
public Session<LsEntry> getSession() {
|
||||
public SftpSession getSession() {
|
||||
try {
|
||||
ChannelSftp channel = mock(ChannelSftp.class);
|
||||
|
||||
|
||||
@@ -14,17 +14,17 @@
|
||||
|
||||
<int:channel id="inboundGet"/>
|
||||
|
||||
<int-sftp:outbound-gateway session-factory="ftpSessionFactory"
|
||||
<int-sftp:outbound-gateway session-factory="sftpSessionFactory"
|
||||
request-channel="inboundGet"
|
||||
command="get"
|
||||
expression="payload"
|
||||
local-directory-expression="'/tmp/sftpOutboundTests/' + #remoteDirectory.toUpperCase()"
|
||||
local-filename-generator-expression="#remoteFileName.replaceFirst('ftpSource', 'localTarget')"
|
||||
local-directory-expression="@sftpServer.targetLocalDirectoryName + #remoteDirectory.toUpperCase()"
|
||||
local-filename-generator-expression="#remoteFileName.replaceFirst('sftpSource', 'localTarget')"
|
||||
reply-channel="output"/>
|
||||
|
||||
<int:channel id="invalidDirExpression"/>
|
||||
|
||||
<int-sftp:outbound-gateway session-factory="ftpSessionFactory"
|
||||
<int-sftp:outbound-gateway session-factory="sftpSessionFactory"
|
||||
request-channel="invalidDirExpression"
|
||||
command="get"
|
||||
expression="payload"
|
||||
@@ -33,40 +33,40 @@
|
||||
|
||||
<int:channel id="inboundMGet"/>
|
||||
|
||||
<int-sftp:outbound-gateway session-factory="ftpSessionFactory"
|
||||
<int-sftp:outbound-gateway session-factory="sftpSessionFactory"
|
||||
request-channel="inboundMGet"
|
||||
command="mget"
|
||||
expression="payload"
|
||||
local-directory-expression="'/tmp/sftpOutboundTests/' + #remoteDirectory"
|
||||
local-filename-generator-expression="#remoteFileName.replaceFirst('ftpSource', 'localTarget')"
|
||||
local-directory-expression="@sftpServer.targetLocalDirectoryName + #remoteDirectory"
|
||||
local-filename-generator-expression="#remoteFileName.replaceFirst('sftpSource', 'localTarget')"
|
||||
reply-channel="output"/>
|
||||
|
||||
<int:channel id="inboundMGetRecursive"/>
|
||||
|
||||
<int-sftp:outbound-gateway session-factory="ftpSessionFactory"
|
||||
<int-sftp:outbound-gateway session-factory="sftpSessionFactory"
|
||||
request-channel="inboundMGetRecursive"
|
||||
command="mget"
|
||||
expression="payload"
|
||||
command-options="-R"
|
||||
local-directory-expression="'/tmp/sftpOutboundTests/' + #remoteDirectory"
|
||||
local-filename-generator-expression="#remoteFileName.replaceFirst('ftpSource', 'localTarget')"
|
||||
local-directory-expression="@sftpServer.targetLocalDirectoryName + #remoteDirectory"
|
||||
local-filename-generator-expression="#remoteFileName.replaceFirst('sftpSource', 'localTarget')"
|
||||
reply-channel="output"/>
|
||||
|
||||
<int:channel id="inboundMGetRecursiveFiltered"/>
|
||||
|
||||
<int-sftp:outbound-gateway session-factory="ftpSessionFactory"
|
||||
<int-sftp:outbound-gateway session-factory="sftpSessionFactory"
|
||||
request-channel="inboundMGetRecursiveFiltered"
|
||||
command="mget"
|
||||
expression="payload"
|
||||
command-options="-R"
|
||||
filename-regex="(subSftpSource|.*1.txt)"
|
||||
local-directory-expression="'/tmp/sftpOutboundTests/' + #remoteDirectory"
|
||||
local-filename-generator-expression="#remoteFileName.replaceFirst('ftpSource', 'localTarget')"
|
||||
local-directory-expression="@sftpServer.targetLocalDirectoryName + #remoteDirectory"
|
||||
local-filename-generator-expression="#remoteFileName.replaceFirst('sftpSource', 'localTarget')"
|
||||
reply-channel="output"/>
|
||||
|
||||
<int:channel id="inboundMPut"/>
|
||||
|
||||
<int-sftp:outbound-gateway session-factory="ftpSessionFactory"
|
||||
<int-sftp:outbound-gateway session-factory="sftpSessionFactory"
|
||||
request-channel="inboundMPut"
|
||||
command="mput"
|
||||
auto-create-directory="true"
|
||||
@@ -77,7 +77,7 @@
|
||||
|
||||
<int:channel id="inboundMPutRecursive"/>
|
||||
|
||||
<int-sftp:outbound-gateway session-factory="ftpSessionFactory"
|
||||
<int-sftp:outbound-gateway session-factory="sftpSessionFactory"
|
||||
request-channel="inboundMPutRecursive"
|
||||
command="mput"
|
||||
command-options="-R"
|
||||
@@ -89,7 +89,7 @@
|
||||
|
||||
<int:channel id="inboundMPutRecursiveFiltered"/>
|
||||
|
||||
<int-sftp:outbound-gateway session-factory="ftpSessionFactory"
|
||||
<int-sftp:outbound-gateway session-factory="sftpSessionFactory"
|
||||
request-channel="inboundMPutRecursiveFiltered"
|
||||
command="mput"
|
||||
command-options="-R"
|
||||
@@ -100,32 +100,37 @@
|
||||
remote-directory="sftpTarget"
|
||||
reply-channel="output"/>
|
||||
|
||||
<bean id="ftpSessionFactory" class="org.mockito.Mockito" factory-method="mock">
|
||||
<constructor-arg value="org.springframework.integration.file.remote.session.SessionFactory" />
|
||||
</bean>
|
||||
<int:channel id="appending" />
|
||||
|
||||
<beans profile="realSSH">
|
||||
<bean id="ftpSessionFactory"
|
||||
class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
|
||||
<property name="host" value="localhost"/>
|
||||
<property name="user" value="ftptest"/>
|
||||
<property name="password" value="ftptest"/>
|
||||
</bean>
|
||||
</beans>
|
||||
<bean id="sftpServerConfig" class="org.springframework.integration.sftp.TestSftpServerConfig" />
|
||||
|
||||
<beans profile="realSSHSharedSession">
|
||||
<bean id="ftpSessionFactory"
|
||||
class="org.springframework.integration.file.remote.session.CachingSessionFactory">
|
||||
<constructor-arg>
|
||||
<bean
|
||||
class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
|
||||
<constructor-arg value="true"/>
|
||||
<property name="host" value="localhost"/>
|
||||
<property name="user" value="ftptest"/>
|
||||
<property name="password" value="ftptest"/>
|
||||
</bean>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
</beans>
|
||||
<int-sftp:outbound-channel-adapter id="appender"
|
||||
session-factory="sftpSessionFactory"
|
||||
channel="appending"
|
||||
mode="APPEND"
|
||||
use-temporary-file-name="false"
|
||||
remote-directory="sftpTarget"
|
||||
auto-create-directory="true"
|
||||
remote-file-separator="/" />
|
||||
|
||||
<int:channel id="ignoring" />
|
||||
|
||||
<int-sftp:outbound-channel-adapter id="ignore"
|
||||
session-factory="sftpSessionFactory"
|
||||
channel="ignoring"
|
||||
mode="IGNORE"
|
||||
remote-directory="sftpTarget"
|
||||
auto-create-directory="true"
|
||||
remote-file-separator="/" />
|
||||
|
||||
<int:channel id="failing" />
|
||||
|
||||
<int-sftp:outbound-channel-adapter id="fail"
|
||||
session-factory="sftpSessionFactory"
|
||||
channel="failing"
|
||||
mode="FAIL"
|
||||
remote-directory="sftpTarget"
|
||||
auto-create-directory="true"
|
||||
remote-file-separator="/" />
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2014 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.
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.integration.sftp.outbound;
|
||||
|
||||
import static org.hamcrest.Matchers.anyOf;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -25,8 +26,6 @@ import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
@@ -44,29 +43,28 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.file.FileHeaders;
|
||||
import org.springframework.integration.file.remote.SessionCallback;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
import org.springframework.integration.sftp.session.SftpFileInfo;
|
||||
import org.springframework.integration.sftp.TestSftpServer;
|
||||
import org.springframework.integration.sftp.session.DefaultSftpSessionFactory;
|
||||
import org.springframework.integration.sftp.session.SftpRemoteFileTemplate;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.test.annotation.IfProfileValue;
|
||||
import org.springframework.test.annotation.ProfileValueUtils;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
import com.jcraft.jsch.SftpATTRS;
|
||||
|
||||
/**
|
||||
* Run with -Dspring-profiles-active=realSSH to run with a real SSH server.
|
||||
*
|
||||
* Assumes ftptest account on localhost with the following directory tree in the user's root...
|
||||
* Runs against an embedded SFTP Server with the following directory tree:
|
||||
*
|
||||
* <pre class="code">
|
||||
* $ tree sftpSource/
|
||||
@@ -113,81 +111,30 @@ public class SftpServerOutboundTests {
|
||||
private DirectChannel inboundMPutRecursiveFiltered;
|
||||
|
||||
@Autowired
|
||||
private SessionFactory<SftpFileInfo> sessionFactory;
|
||||
private DefaultSftpSessionFactory sessionFactory;
|
||||
|
||||
@Autowired
|
||||
private DirectChannel appending;
|
||||
|
||||
@Autowired
|
||||
private DirectChannel ignoring;
|
||||
|
||||
@Autowired
|
||||
private DirectChannel failing;
|
||||
|
||||
@Autowired
|
||||
private TestSftpServer sftpServer;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
purge();
|
||||
setUpMocksIfNeeded();
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private void setUpMocksIfNeeded() throws IOException {
|
||||
String profile = ProfileValueUtils.retrieveProfileValueSource(this.getClass()).get("spring.profiles.active");
|
||||
boolean usingMocks = profile == null || ! profile.startsWith("realSSH");
|
||||
if (usingMocks) {
|
||||
Session session = mock(Session.class);
|
||||
when(sessionFactory.getSession()).thenReturn(session);
|
||||
LsEntry entry1 = mock(LsEntry.class);
|
||||
SftpATTRS attrs1 = mock(SftpATTRS.class);
|
||||
when(entry1.getAttrs()).thenReturn(attrs1);
|
||||
when(entry1.getFilename()).thenReturn("sftpSource1.txt");
|
||||
LsEntry entry2 = mock(LsEntry.class);
|
||||
SftpATTRS attrs2 = mock(SftpATTRS.class);
|
||||
when(entry2.getAttrs()).thenReturn(attrs2);
|
||||
when(entry2.getFilename()).thenReturn("sftpSource2.txt");
|
||||
LsEntry entry3 = mock(LsEntry.class);
|
||||
when(entry3.getFilename()).thenReturn("subSftpSource");
|
||||
SftpATTRS attrs3 = mock(SftpATTRS.class);
|
||||
when(entry3.getAttrs()).thenReturn(attrs3);
|
||||
when(attrs3.isDir()).thenReturn(true);
|
||||
LsEntry entry4 = mock(LsEntry.class);
|
||||
SftpATTRS attrs4 = mock(SftpATTRS.class);
|
||||
when(entry4.getAttrs()).thenReturn(attrs4);
|
||||
// recursion uses a DFA to update the filename to include the subdirectory
|
||||
new DirectFieldAccessor(entry4).setPropertyValue("filename", "subSftpSource1.txt");
|
||||
when(entry4.getFilename()).thenCallRealMethod();
|
||||
when(session.list("sftpSource/sftpSource1.txt")).thenReturn(new LsEntry[] {
|
||||
entry1
|
||||
});
|
||||
when(session.list("sftpSource/")).thenReturn(new LsEntry[] {
|
||||
entry1, entry2, entry3
|
||||
});
|
||||
when(session.list("sftpSource/subSftpSource/")).thenReturn(new LsEntry[] {
|
||||
entry4
|
||||
});
|
||||
when(session.list("sftpSource/subSftpSource/subSftpSource1.txt")).thenReturn(new LsEntry[] {
|
||||
entry4
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void purge() {
|
||||
File local = new File("/tmp/sftpOutboundTests/");
|
||||
purge(local);
|
||||
local.delete();
|
||||
}
|
||||
|
||||
private void purge(File local) {
|
||||
File[] files = local.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
this.purge(file);
|
||||
}
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
public void setup() {
|
||||
this.sftpServer.recursiveDelete(sftpServer.getTargetLocalDirectory());
|
||||
this.sftpServer.recursiveDelete(sftpServer.getTargetSftpDirectory());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInt2866LocalDirectoryExpressionGET() {
|
||||
Session<?> session = null;
|
||||
boolean sharedSession = "realSSHSharedSession".equals(System.getProperty("spring.profiles.active"));
|
||||
if (sharedSession) {
|
||||
session = this.sessionFactory.getSession();
|
||||
}
|
||||
Session<?> session = this.sessionFactory.getSession();
|
||||
String dir = "sftpSource/";
|
||||
this.inboundGet.send(new GenericMessage<Object>(dir + "sftpSource1.txt"));
|
||||
Message<?> result = this.output.receive(1000);
|
||||
@@ -203,11 +150,9 @@ public class SftpServerOutboundTests {
|
||||
localFile = (File) result.getPayload();
|
||||
assertThat(localFile.getPath().replaceAll(java.util.regex.Matcher.quoteReplacement(File.separator), "/"),
|
||||
Matchers.containsString(dir.toUpperCase()));
|
||||
if (sharedSession) {
|
||||
Session<?> session2 = this.sessionFactory.getSession();
|
||||
assertSame(TestUtils.getPropertyValue(session, "targetSession.jschSession"),
|
||||
TestUtils.getPropertyValue(session2, "targetSession.jschSession"));
|
||||
}
|
||||
Session<?> session2 = this.sessionFactory.getSession();
|
||||
assertSame(TestUtils.getPropertyValue(session, "jschSession"),
|
||||
TestUtils.getPropertyValue(session2, "jschSession"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -294,7 +239,6 @@ public class SftpServerOutboundTests {
|
||||
* Only runs with a real server (see class javadocs).
|
||||
*/
|
||||
@Test
|
||||
@IfProfileValue(name="spring.profiles.active", value="realSSH")
|
||||
public void testInt3100RawGET() throws Exception {
|
||||
Session<?> session = this.sessionFactory.getSession();
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
@@ -311,7 +255,6 @@ public class SftpServerOutboundTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@IfProfileValue(name="spring.profiles.active", value="realSSHSharedSession")
|
||||
public void testInt3047ConcurrentSharedSession() throws Exception {
|
||||
final Session<?> session1 = this.sessionFactory.getSession();
|
||||
final Session<?> session2 = this.sessionFactory.getSession();
|
||||
@@ -371,12 +314,11 @@ public class SftpServerOutboundTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@IfProfileValue(name="spring.profiles.active", value="realSSH")
|
||||
public void testInt3088MPutNotRecursive() {
|
||||
String dir = "sftpSource/";
|
||||
this.inboundMGetRecursive.send(new GenericMessage<Object>(dir + "*"));
|
||||
while (output.receive(0) != null) { }
|
||||
this.inboundMPut.send(new GenericMessage<File>(new File("/tmp/sftpOutboundTests/sftpSource")));
|
||||
this.inboundMPut.send(new GenericMessage<File>(this.sftpServer.getSourceLocalDirectory()));
|
||||
@SuppressWarnings("unchecked")
|
||||
Message<List<String>> out = (Message<List<String>>) this.output.receive(1000);
|
||||
assertNotNull(out);
|
||||
@@ -385,19 +327,18 @@ public class SftpServerOutboundTests {
|
||||
not(equalTo(out.getPayload().get(1))));
|
||||
assertThat(
|
||||
out.getPayload().get(0),
|
||||
anyOf(equalTo("sftpTarget/slocalTarget1.txt"), equalTo("sftpTarget/slocalTarget2.txt")));
|
||||
anyOf(equalTo("sftpTarget/localSource1.txt"), equalTo("sftpTarget/localSource2.txt")));
|
||||
assertThat(
|
||||
out.getPayload().get(1),
|
||||
anyOf(equalTo("sftpTarget/slocalTarget1.txt"), equalTo("sftpTarget/slocalTarget2.txt")));
|
||||
anyOf(equalTo("sftpTarget/localSource1.txt"), equalTo("sftpTarget/localSource2.txt")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@IfProfileValue(name="spring.profiles.active", value="realSSH")
|
||||
public void testInt3088MPutRecursive() {
|
||||
String dir = "sftpSource/";
|
||||
this.inboundMGetRecursive.send(new GenericMessage<Object>(dir + "*"));
|
||||
while (output.receive(0) != null) { }
|
||||
this.inboundMPutRecursive.send(new GenericMessage<File>(new File("/tmp/sftpOutboundTests/sftpSource")));
|
||||
this.inboundMPutRecursive.send(new GenericMessage<File>(this.sftpServer.getSourceLocalDirectory()));
|
||||
@SuppressWarnings("unchecked")
|
||||
Message<List<String>> out = (Message<List<String>>) this.output.receive(1000);
|
||||
assertNotNull(out);
|
||||
@@ -406,25 +347,24 @@ public class SftpServerOutboundTests {
|
||||
not(equalTo(out.getPayload().get(1))));
|
||||
assertThat(
|
||||
out.getPayload().get(0),
|
||||
anyOf(equalTo("sftpTarget/slocalTarget1.txt"), equalTo("sftpTarget/slocalTarget2.txt"),
|
||||
equalTo("sftpTarget/subSftpSource/subSlocalTarget1.txt")));
|
||||
anyOf(equalTo("sftpTarget/localSource1.txt"), equalTo("sftpTarget/localSource2.txt"),
|
||||
equalTo("sftpTarget/subLocalSource/subLocalSource1.txt")));
|
||||
assertThat(
|
||||
out.getPayload().get(1),
|
||||
anyOf(equalTo("sftpTarget/slocalTarget1.txt"), equalTo("sftpTarget/slocalTarget2.txt"),
|
||||
equalTo("sftpTarget/subSftpSource/subSlocalTarget1.txt")));
|
||||
anyOf(equalTo("sftpTarget/localSource1.txt"), equalTo("sftpTarget/localSource2.txt"),
|
||||
equalTo("sftpTarget/subLocalSource/subLocalSource1.txt")));
|
||||
assertThat(
|
||||
out.getPayload().get(2),
|
||||
anyOf(equalTo("sftpTarget/slocalTarget1.txt"), equalTo("sftpTarget/slocalTarget2.txt"),
|
||||
equalTo("sftpTarget/subSftpSource/subSlocalTarget1.txt")));
|
||||
anyOf(equalTo("sftpTarget/localSource1.txt"), equalTo("sftpTarget/localSource2.txt"),
|
||||
equalTo("sftpTarget/subLocalSource/subLocalSource1.txt")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@IfProfileValue(name="spring.profiles.active", value="realSSH")
|
||||
public void testInt3088MPutRecursiveFiltered() {
|
||||
String dir = "sftpSource/";
|
||||
this.inboundMGetRecursive.send(new GenericMessage<Object>(dir + "*"));
|
||||
while (output.receive(0) != null) { }
|
||||
this.inboundMPutRecursiveFiltered.send(new GenericMessage<File>(new File("/tmp/sftpOutboundTests/sftpSource")));
|
||||
this.inboundMPutRecursiveFiltered.send(new GenericMessage<File>(this.sftpServer.getSourceLocalDirectory()));
|
||||
@SuppressWarnings("unchecked")
|
||||
Message<List<String>> out = (Message<List<String>>) this.output.receive(1000);
|
||||
assertNotNull(out);
|
||||
@@ -433,12 +373,47 @@ public class SftpServerOutboundTests {
|
||||
not(equalTo(out.getPayload().get(1))));
|
||||
assertThat(
|
||||
out.getPayload().get(0),
|
||||
anyOf(equalTo("sftpTarget/slocalTarget1.txt"), equalTo("sftpTarget/slocalTarget2.txt"),
|
||||
equalTo("sftpTarget/subSftpSource/subSlocalTarget1.txt")));
|
||||
anyOf(equalTo("sftpTarget/localSource1.txt"), equalTo("sftpTarget/localSource2.txt"),
|
||||
equalTo("sftpTarget/subLocalSource/subLocalSource1.txt")));
|
||||
assertThat(
|
||||
out.getPayload().get(1),
|
||||
anyOf(equalTo("sftpTarget/slocalTarget1.txt"), equalTo("sftpTarget/slocalTarget2.txt"),
|
||||
equalTo("sftpTarget/subSftpSource/subSlocalTarget1.txt")));
|
||||
anyOf(equalTo("sftpTarget/localSource1.txt"), equalTo("sftpTarget/localSource2.txt"),
|
||||
equalTo("sftpTarget/subLocalSource/subLocalSource1.txt")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInt3412FileMode() {
|
||||
Message<String> m = MessageBuilder.withPayload("foo")
|
||||
.setHeader(FileHeaders.FILENAME, "appending.txt")
|
||||
.build();
|
||||
appending.send(m);
|
||||
appending.send(m);
|
||||
|
||||
SftpRemoteFileTemplate template = new SftpRemoteFileTemplate(sessionFactory);
|
||||
assertLength6(template);
|
||||
|
||||
ignoring.send(m);
|
||||
assertLength6(template);
|
||||
try {
|
||||
failing.send(m);
|
||||
fail("Expected exception");
|
||||
}
|
||||
catch (MessagingException e) {
|
||||
assertThat(e.getCause().getCause().getMessage(), containsString("The destination file already exists"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void assertLength6(SftpRemoteFileTemplate template) {
|
||||
LsEntry[] files = template.execute(new SessionCallback<LsEntry, LsEntry[]>() {
|
||||
|
||||
@Override
|
||||
public LsEntry[] doInSession(Session<LsEntry> session) throws IOException {
|
||||
return session.list("sftpTarget/appending.txt");
|
||||
}
|
||||
});
|
||||
assertEquals(1, files.length);
|
||||
assertEquals(6, files[0].getAttrs().getSize());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.integration.sftp.session;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.expression.common.LiteralExpression;
|
||||
import org.springframework.integration.file.DefaultFileNameGenerator;
|
||||
import org.springframework.integration.file.remote.ClientCallbackWithoutResult;
|
||||
import org.springframework.integration.file.remote.SessionCallback;
|
||||
import org.springframework.integration.file.remote.SessionCallbackWithoutResult;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.integration.sftp.TestSftpServer;
|
||||
import org.springframework.integration.sftp.TestSftpServerConfig;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
import com.jcraft.jsch.SftpATTRS;
|
||||
import com.jcraft.jsch.SftpException;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @since 4.1
|
||||
*
|
||||
*/
|
||||
@ContextConfiguration(classes=TestSftpServerConfig.class)
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@DirtiesContext
|
||||
public class SftpRemoteFileTemplateTests {
|
||||
|
||||
@Autowired
|
||||
private TestSftpServer sftpServer;
|
||||
|
||||
@Autowired
|
||||
private DefaultSftpSessionFactory sessionFactory;
|
||||
|
||||
@Before
|
||||
@After
|
||||
public void setup() {
|
||||
this.sftpServer.recursiveDelete(sftpServer.getTargetLocalDirectory());
|
||||
this.sftpServer.recursiveDelete(sftpServer.getTargetSftpDirectory());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testINT3412AppendStatRmdir() {
|
||||
SftpRemoteFileTemplate template = new SftpRemoteFileTemplate(sessionFactory);
|
||||
DefaultFileNameGenerator fileNameGenerator = new DefaultFileNameGenerator();
|
||||
fileNameGenerator.setExpression("'foobar.txt'");
|
||||
template.setFileNameGenerator(fileNameGenerator);
|
||||
template.setRemoteDirectoryExpression(new LiteralExpression("foo/"));
|
||||
template.setUseTemporaryFileName(false);
|
||||
template.execute(new SessionCallback<LsEntry, Boolean>() {
|
||||
|
||||
@Override
|
||||
public Boolean doInSession(Session<LsEntry> session) throws IOException {
|
||||
session.mkdir("foo/");
|
||||
return session.mkdir("foo/bar/");
|
||||
}
|
||||
|
||||
});
|
||||
template.append(new GenericMessage<String>("foo"));
|
||||
template.append(new GenericMessage<String>("bar"));
|
||||
assertTrue(template.exists("foo/foobar.txt"));
|
||||
template.executeWithClient(new ClientCallbackWithoutResult<ChannelSftp>() {
|
||||
|
||||
@Override
|
||||
public void doWithClientWithoutResult(ChannelSftp client) {
|
||||
try {
|
||||
SftpATTRS file = client.lstat("foo/foobar.txt");
|
||||
assertEquals(6, file.getSize());
|
||||
}
|
||||
catch (SftpException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
template.execute(new SessionCallbackWithoutResult<LsEntry>() {
|
||||
|
||||
@Override
|
||||
public void doInSessionWithoutResult(Session<LsEntry> session) throws IOException {
|
||||
assertTrue(session.remove("foo/foobar.txt"));
|
||||
assertTrue(session.rmdir("foo/bar/"));
|
||||
LsEntry[] files = session.list("foo/");
|
||||
assertEquals(0, files.length);
|
||||
assertTrue(session.rmdir("foo/"));
|
||||
}
|
||||
});
|
||||
assertFalse(template.exists("foo"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2014 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.
|
||||
@@ -15,17 +15,15 @@
|
||||
*/
|
||||
package org.springframework.integration.sftp.session;
|
||||
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
*
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
public class SftpTestSessionFactory {
|
||||
|
||||
public static Session<LsEntry> createSftpSession(com.jcraft.jsch.Session jschSession) {
|
||||
public static SftpSession createSftpSession(com.jcraft.jsch.Session jschSession) {
|
||||
SftpSession sftpSession = new SftpSession(jschSession);
|
||||
sftpSession.connect();
|
||||
return sftpSession;
|
||||
|
||||
@@ -329,7 +329,9 @@ protected void postProcessClientBeforeConnect(T client) throws IOException {
|
||||
auto-create-directory="true"
|
||||
remote-directory-expression="headers.['remote_dir']"
|
||||
temporary-remote-directory-expression="headers.['temp_remote_dir']"
|
||||
filename-generator="fileNameGenerator"/>]]></programlisting>
|
||||
filename-generator="fileNameGenerator"
|
||||
use-temporary-filename="true"
|
||||
mode="REPLACE"/>]]></programlisting>
|
||||
|
||||
As you can see from the configuration above you can configure an <emphasis>FTP Outbound Channel Adapter</emphasis> via the
|
||||
<code>outbound-channel-adapter</code> element while also providing values for various attributes such as <code>filename-generator</code>
|
||||
@@ -356,7 +358,14 @@ protected void postProcessClientBeforeConnect(T client) throws IOException {
|
||||
instead of remote-directory="/foo/bar")
|
||||
</important>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Starting with <emphasis>version 4.1</emphasis>, you can specify the <code>mode</code> when transferring the file. By default,
|
||||
an existing file will be overwritten; the modes are defined on <code>enum</code>
|
||||
<classname>FileExistsMode</classname>, having values <code>REPLACE</code> (default), <code>APPEND</code>,
|
||||
<code>IGNORE</code>, and <code>FAIL</code>. With <code>IGNORE</code> and <code>FAIL</code>, the file is not
|
||||
transferred; <code>FAIL</code> causes an exception to be thrown whereas <code>IGNORE</code> silently
|
||||
ignores the transfer (although a <code>DEBUG</code> log entry is produced).
|
||||
</para>
|
||||
<para>
|
||||
<emphasis>Avoiding Partially Written Files</emphasis>
|
||||
</para>
|
||||
@@ -635,7 +644,15 @@ protected void postProcessClientBeforeConnect(T client) throws IOException {
|
||||
<classname>InputStream</classname>), remove, and rename files. In addition an <code>execute</code>
|
||||
method is provided allowing the caller to execute multiple operations on the session. In all cases,
|
||||
the template takes care of reliably closing the session.
|
||||
For more information, refer to the javadocs for <classname>RemoteFileTemplate</classname>.
|
||||
For more information, refer to the <ulink
|
||||
url="http://docs.spring.io/spring-integration/api/org/springframework/integration/file/remote/RemoteFileTemplate.html">javadocs
|
||||
for <classname>RemoteFileTemplate</classname></ulink> There is a subclass for FTP:
|
||||
<classname>FtpRemoteFileTemplate</classname>. <!-- TODO: fix the link when 4.1 is released -->
|
||||
</para>
|
||||
<para>
|
||||
Additional methods were added in <emphasis>version 4.1</emphasis> including <code>getClientInstance()</code>
|
||||
which provides access to the underlying <classname>FTPClient</classname> enabling access to low-level
|
||||
APIs.
|
||||
</para>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
@@ -255,12 +255,20 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/sftp
|
||||
<section id="sftp-rft">
|
||||
<title>RemoteFileTemplate</title>
|
||||
<para>
|
||||
Starting with <emphasis>Spring Integration version 3.0</emphasis> a new abstraction is provided over the
|
||||
Starting with <emphasis>Spring Integration version 3.0</emphasis>, a new abstraction is provided over the
|
||||
<classname>SftpSession</classname> object. The template provides methods to send, retrieve (as an
|
||||
<classname>InputStream</classname>), remove, and rename files. In addition an <code>execute</code>
|
||||
method is provided allowing the caller to execute multiple operations on the session. In all cases,
|
||||
the template takes care of reliably closing the session.
|
||||
For more information, refer to the javadocs for <classname>RemoteFileTemplate</classname>.
|
||||
For more information, refer to the <ulink
|
||||
url="http://docs.spring.io/spring-integration/api/org/springframework/integration/file/remote/RemoteFileTemplate.html">javadocs
|
||||
for <classname>RemoteFileTemplate</classname></ulink> There is a subclass for SFTP:
|
||||
<classname>SftpRemoteFileTemplate</classname>. <!-- TODO: fix the link when 4.1 is released -->
|
||||
</para>
|
||||
<para>
|
||||
Additional methods were added in <emphasis>version 4.1</emphasis> including <code>getClientInstance()</code>
|
||||
which provides access to the underlying <classname>ChannelSftp</classname> enabling access to low-level
|
||||
APIs.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
@@ -411,11 +419,15 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/sftp
|
||||
the file contents; 3) <classname>java.lang.String</classname> - text that represents the file contents.
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-sftp:outbound-channel-adapter id="sftpOutboundAdapter"
|
||||
session-factory="sftpSessionFactory"
|
||||
channel="inputChannel"
|
||||
charset="UTF-8"
|
||||
remote-directory="foo/bar"
|
||||
remote-filename-generator-expression="payload.getName() + '-foo'"/>]]></programlisting>
|
||||
session-factory="sftpSessionFactory"
|
||||
channel="inputChannel"
|
||||
charset="UTF-8"
|
||||
remote-file-separator="/"
|
||||
remote-directory="foo/bar"
|
||||
remote-filename-generator-expression="payload.getName() + '-foo'"
|
||||
filename-generator="fileNameGenerator"
|
||||
use-temporary-filename="true"
|
||||
mode="REPLACE"/>]]></programlisting>
|
||||
|
||||
As you can see from the configuration above you can configure the <emphasis>SFTP Outbound Channel Adapter</emphasis> via
|
||||
the <code>outbound-channel-adapter</code> element.
|
||||
@@ -434,6 +446,15 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/sftp
|
||||
value that computes the <emphasis>file name</emphasis> based on its original name while also appending a suffix: '-foo'.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Starting with <emphasis>version 4.1</emphasis>, you can specify the <code>mode</code> when transferring the file. By default,
|
||||
an existing file will be overwritten; the modes are defined on <code>enum</code>
|
||||
<classname>FileExistsMode</classname>, having values <code>REPLACE</code> (default), <code>APPEND</code>,
|
||||
<code>IGNORE</code>, and <code>FAIL</code>. With <code>IGNORE</code> and <code>FAIL</code>, the file is not
|
||||
transferred; <code>FAIL</code> causes an exception to be thrown whereas <code>IGNORE</code> silently
|
||||
ignores the transfer (although a <code>DEBUG</code> log entry is produced).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<emphasis>Avoiding Partially Written Files</emphasis>
|
||||
</para>
|
||||
|
||||
@@ -74,6 +74,19 @@
|
||||
See <xref linkend="mqtt-inbound"/> for more information.
|
||||
</para>
|
||||
</section>
|
||||
<section id="4.1-sftp">
|
||||
<title>FTP/SFTP Adapter Changes</title>
|
||||
<para>
|
||||
The FTP and SFTP outbound channel adapters now support appending to remote files, as
|
||||
well as taking specific actions when a remote file already exists. The remote file
|
||||
templates now also support this as well as <code>rmdir()</code> and <code>exists()</code>.
|
||||
In addition, the remote file templates provide access to the underlying client object
|
||||
enabling access to low-level APIs.
|
||||
</para>
|
||||
<para>
|
||||
See <xref linkend="ftp"/> and <xref linkend="sftp"/> for more information.
|
||||
</para>
|
||||
</section>
|
||||
<section id="4.1-splitter-iterator">
|
||||
<title>Splitter and Iterator</title>
|
||||
<para>
|
||||
|
||||
Reference in New Issue
Block a user