INT-3622: (S)FTP: Add MessageSessionCallback

JIRA: https://jira.spring.io/browse/INT-3622

INT-3622: Polishing
This commit is contained in:
Artem Bilan
2015-08-07 23:18:52 -04:00
committed by Gary Russell
parent d89dabe72f
commit e408331e67
14 changed files with 396 additions and 59 deletions

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.file.config;
import org.w3c.dom.Element;
@@ -53,8 +54,13 @@ public abstract class AbstractRemoteFileOutboundGatewayParser extends AbstractCo
builder.addConstructorArgValue(templateDefinition);
builder.addConstructorArgValue(element.getAttribute("command"));
builder.addConstructorArgValue(element.getAttribute(EXPRESSION_ATTRIBUTE));
if (element.hasAttribute("session-callback")) {
builder.addConstructorArgReference(element.getAttribute("session-callback"));
}
else {
builder.addConstructorArgValue(element.getAttribute("command"));
builder.addConstructorArgValue(element.getAttribute(EXPRESSION_ATTRIBUTE));
}
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "command-options", "options");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "reply-timeout", "sendTimeout");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "reply-channel", "outputChannel");

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2015 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 java.io.IOException;
import org.springframework.integration.file.remote.session.Session;
import org.springframework.messaging.Message;
/**
* Callback invoked by {@code RemoteFileOperations.executeForMessage()}
* - allows multiple operations on a session.
*
* @author Artem Bilan
* @since 4.2
*/
public interface MessageSessionCallback<F, T> {
/**
* Called within the context of a session and requestMessage.
* Perform some operation(s) on the session.
* The caller will take care of closing the session after this method exits.
*
* @param session The session.
* @param requestMessage The message to take in account with session operation(s).
* @return The result of type T.
* @throws IOException Any IOException.
*/
T doInSession(Session<F> session, Message<?> requestMessage) throws IOException;
}

View File

@@ -39,6 +39,7 @@ import org.springframework.integration.expression.ExpressionUtils;
import org.springframework.integration.file.FileHeaders;
import org.springframework.integration.file.filters.FileListFilter;
import org.springframework.integration.file.remote.AbstractFileInfo;
import org.springframework.integration.file.remote.MessageSessionCallback;
import org.springframework.integration.file.remote.RemoteFileTemplate;
import org.springframework.integration.file.remote.SessionCallback;
import org.springframework.integration.file.remote.session.Session;
@@ -71,7 +72,7 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
/**
* Enumeration of commands supported by the gateways.
*/
public static enum Command {
public enum Command {
/**
* List remote files.
@@ -110,7 +111,7 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
private String command;
private Command(String command) {
Command(String command) {
this.command = command;
}
@@ -126,13 +127,14 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
}
throw new IllegalArgumentException("No Command with value '" + cmd + "'");
}
}
/**
* Enumeration of options supported by various commands.
*
*/
public static enum Option {
public enum Option {
/**
* Don't return full file information; just the name (ls).
@@ -181,7 +183,7 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
private String option;
private Option(String option) {
Option(String option) {
this.option = option;
}
@@ -197,10 +199,13 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
}
throw new IllegalArgumentException("No option with value '" + opt + "'");
}
}
private final ExpressionEvaluatingMessageProcessor<String> fileNameProcessor;
private final MessageSessionCallback<F, ?> messageSessionCallback;
private volatile ExpressionEvaluatingMessageProcessor<String> renameProcessor =
new ExpressionEvaluatingMessageProcessor<String>(
new SpelExpressionParser().parseExpression("headers." + FileHeaders.RENAME_TO));
@@ -226,33 +231,75 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
private volatile FileExistsMode fileExistsMode;
/**
* Construct an instance using the provided session factory and callback for
* performing operations on the session.
* @param sessionFactory the session factory.
* @param messageSessionCallback the callback.
*/
public AbstractRemoteFileOutboundGateway(SessionFactory<F> sessionFactory,
MessageSessionCallback<F, ?> messageSessionCallback) {
this(new RemoteFileTemplate<F>(sessionFactory), messageSessionCallback);
}
/**
* Construct an instance with the supplied remote file template and callback
* for performing operations on the session.
* @param remoteFileTemplate the remote file template.
* @param messageSessionCallback the callback.
*/
public AbstractRemoteFileOutboundGateway(RemoteFileTemplate<F> remoteFileTemplate,
MessageSessionCallback<F, ?> messageSessionCallback) {
Assert.notNull(remoteFileTemplate, "'remoteFileTemplate' cannot be null");
Assert.notNull(messageSessionCallback, "'messageSessionCallback' cannot be null");
this.remoteFileTemplate = remoteFileTemplate;
this.messageSessionCallback = messageSessionCallback;
this.fileNameProcessor = null;
this.command = null;
}
/**
* Construct an instance with the supplied session factory, a command ('ls', 'get'
* etc), and an expression to determine the filename.
* @param sessionFactory the session factory.
* @param command the command.
* @param expression the filename expression.
*/
public AbstractRemoteFileOutboundGateway(SessionFactory<F> sessionFactory, String command,
String expression) {
Assert.notNull(sessionFactory, "'sessionFactory' cannot be null");
this.remoteFileTemplate = new RemoteFileTemplate<F>(sessionFactory);
this.command = Command.toCommand(command);
this.fileNameProcessor = new ExpressionEvaluatingMessageProcessor<String>(
new SpelExpressionParser().parseExpression(expression));
this(sessionFactory, Command.toCommand(command), expression);
}
public AbstractRemoteFileOutboundGateway(SessionFactory<F> sessionFactory, Command command,
String expression) {
Assert.notNull(sessionFactory, "'sessionFactory' cannot be null");
this.remoteFileTemplate = new RemoteFileTemplate<F>(sessionFactory);
this.command = command;
this.fileNameProcessor = new ExpressionEvaluatingMessageProcessor<String>(
new SpelExpressionParser().parseExpression(expression));
/**
* Construct an instance with the supplied session factory, a command ('ls', 'get'
* etc), and an expression to determine the filename.
* @param sessionFactory the session factory.
* @param command the command.
* @param expression the filename expression.
*/
public AbstractRemoteFileOutboundGateway(SessionFactory<F> sessionFactory, Command command, String expression) {
this(new RemoteFileTemplate<F>(sessionFactory), command, expression);
}
/**
* Construct an instance with the supplied remote file template, a command ('ls',
* 'get' etc), and an expression to determine the filename.
* @param remoteFileTemplate the remote file template.
* @param command the command.
* @param expression the filename expression.
*/
public AbstractRemoteFileOutboundGateway(RemoteFileTemplate<F> remoteFileTemplate, String command,
String expression) {
Assert.notNull(remoteFileTemplate, "'remoteFileTemplate' cannot be null");
this.remoteFileTemplate = remoteFileTemplate;
this.command = Command.toCommand(command);
this.fileNameProcessor = new ExpressionEvaluatingMessageProcessor<String>(
new SpelExpressionParser().parseExpression(expression));
this(remoteFileTemplate, Command.toCommand(command), expression);
}
/**
* Construct an instance with the supplied remote file template, a command ('ls',
* 'get' etc), and an expression to determine the filename.
* @param remoteFileTemplate the remote file template.
* @param command the command.
* @param expression the filename expression.
*/
public AbstractRemoteFileOutboundGateway(RemoteFileTemplate<F> remoteFileTemplate, Command command,
String expression) {
Assert.notNull(remoteFileTemplate, "'remoteFileTemplate' cannot be null");
@@ -260,6 +307,7 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
this.command = command;
this.fileNameProcessor = new ExpressionEvaluatingMessageProcessor<String>(
new SpelExpressionParser().parseExpression(expression));
this.messageSessionCallback = null;
}
/**
@@ -366,7 +414,8 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
@Override
protected void doInit() {
Assert.notNull(this.command, "command must not be null");
Assert.state(this.command != null || this.messageSessionCallback != null,
"'command' or 'messageSessionCallback' must be specified.");
if (Command.RM.equals(this.command) ||
Command.GET.equals(this.command)) {
Assert.isNull(this.filter, "Filters are not supported with the rm and get commands");
@@ -402,10 +451,10 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
}
if (Command.MGET.equals(this.command)) {
Assert.isTrue(!(this.options.contains(Option.SUBDIRS)),
"Cannot use " + Option.SUBDIRS.toString() + " when using 'mget' use " + Option.RECURSIVE.toString() +
" to obtain files in subdirectories");
"Cannot use " + Option.SUBDIRS.toString() + " when using 'mget' use "
+ Option.RECURSIVE.toString() + " to obtain files in subdirectories");
}
if (this.getBeanFactory() != null) {
if (this.fileNameProcessor != null && getBeanFactory() != null) {
this.fileNameProcessor.setBeanFactory(this.getBeanFactory());
this.renameProcessor.setBeanFactory(this.getBeanFactory());
this.remoteFileTemplate.setBeanFactory(this.getBeanFactory());
@@ -413,25 +462,33 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
}
@Override
protected Object handleRequestMessage(Message<?> requestMessage) {
switch (this.command) {
case LS:
return doLs(requestMessage);
case GET:
return doGet(requestMessage);
case MGET:
return doMget(requestMessage);
case RM:
return doRm(requestMessage);
case MV:
return doMv(requestMessage);
case PUT:
return doPut(requestMessage);
case MPUT:
return doMput(requestMessage);
default:
return null;
protected Object handleRequestMessage(final Message<?> requestMessage) {
if (this.command != null) {
switch (this.command) {
case LS:
return doLs(requestMessage);
case GET:
return doGet(requestMessage);
case MGET:
return doMget(requestMessage);
case RM:
return doRm(requestMessage);
case MV:
return doMv(requestMessage);
case PUT:
return doPut(requestMessage);
case MPUT:
return doMput(requestMessage);
}
}
return this.remoteFileTemplate.execute(new SessionCallback<F, Object>() {
@Override
public Object doInSession(Session<F> session) throws IOException {
return messageSessionCallback.doInSession(session, requestMessage);
}
});
}
private Object doLs(Message<?> requestMessage) {