INT-1849/INT-2147 Add Disposition to File Adapter
* Add *disposition-expression* to the inbound file adapter. This allows for operations such as *payload.delete()*, *payload.renameTo()* after the file is processed. * The result of executing the expression (if any) is sent to the disposition-result-channel.
This commit is contained in:
committed by
Gunnar Hillert
parent
703122a6fb
commit
066eb91100
@@ -38,27 +38,40 @@ import org.springframework.transaction.support.TransactionSynchronization;
|
||||
* @since 2.2
|
||||
*
|
||||
*/
|
||||
public interface PseudoTransactionalMessageSource<T> extends MessageSource<T> {
|
||||
public interface PseudoTransactionalMessageSource<T, V> extends MessageSource<T> {
|
||||
|
||||
/**
|
||||
* Obtain the resource on which appropriate action needs
|
||||
* to be taken.
|
||||
* @return The resource.
|
||||
*/
|
||||
Object getResource();
|
||||
V getResource();
|
||||
|
||||
/**
|
||||
* Invoked via {@link TransactionSynchronization} when the
|
||||
* transaction commits.
|
||||
* @param resource The resource to be "committed"
|
||||
*/
|
||||
void afterCommit(Object resource);
|
||||
void afterCommit(V resource);
|
||||
|
||||
/**
|
||||
* Invoked via {@link TransactionSynchronization} when the
|
||||
* transaction rolls back.
|
||||
* @param resource
|
||||
*/
|
||||
void afterRollback(Object resource);
|
||||
void afterRollback(V resource);
|
||||
|
||||
/**
|
||||
* Called when there is no transaction and the receive() call completed.
|
||||
* @param resource
|
||||
*/
|
||||
void afterReceiveNoTx(V resource);
|
||||
|
||||
/**
|
||||
* Called when there is no transaction and after the message was
|
||||
* sent to the channel.
|
||||
* @param resource
|
||||
*/
|
||||
void afterSendNoTx(V resource);
|
||||
|
||||
}
|
||||
|
||||
@@ -99,13 +99,14 @@ public class SourcePollingChannelAdapter extends AbstractPollingEndpoint impleme
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected boolean doPoll() {
|
||||
boolean isInTx = false;
|
||||
PseudoTransactionalMessageSource<?> messageSource = null;
|
||||
PseudoTransactionalMessageSource<?,Object> messageSource = null;
|
||||
Object resource = null;
|
||||
if (this.isPseudoTxMessageSource) {
|
||||
messageSource = (PseudoTransactionalMessageSource<?>) this.source;
|
||||
messageSource = (PseudoTransactionalMessageSource<?,Object>) this.source;
|
||||
resource = messageSource.getResource();
|
||||
Assert.state(resource != null, "Pseudo Transactional Message Source returned null resource");
|
||||
if (this.synchronizedTx && TransactionSynchronizationManager.isActualTransactionActive()) {
|
||||
@@ -122,17 +123,7 @@ public class SourcePollingChannelAdapter extends AbstractPollingEndpoint impleme
|
||||
}
|
||||
finally {
|
||||
if (this.isPseudoTxMessageSource && !isInTx) {
|
||||
/*
|
||||
* If the message source implements PseudoTransactionalMessageSource and
|
||||
* we're running from a transactional poller, the message source's afterCommit
|
||||
* method will be called by the transaction interceptor, using the transaction
|
||||
* synchronization callback, after the transaction is committed.
|
||||
*
|
||||
* If we are not running in a transaction, we invoke it manually, so the message
|
||||
* source can take the appropriate action, immediately after the receive;
|
||||
* this was the behavior before pseudo transaction support was added.
|
||||
*/
|
||||
messageSource.afterCommit(resource);
|
||||
messageSource.afterReceiveNoTx(resource);
|
||||
}
|
||||
}
|
||||
if (this.logger.isDebugEnabled()){
|
||||
@@ -143,6 +134,9 @@ public class SourcePollingChannelAdapter extends AbstractPollingEndpoint impleme
|
||||
message = MessageHistory.write(message, this);
|
||||
}
|
||||
this.messagingTemplate.send(this.outputChannel, message);
|
||||
if (this.isPseudoTxMessageSource && !isInTx) {
|
||||
messageSource.afterSendNoTx(resource);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (this.logger.isDebugEnabled()){
|
||||
@@ -191,21 +185,23 @@ public class SourcePollingChannelAdapter extends AbstractPollingEndpoint impleme
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected void processResourceAfterCommit(PseudoTransactionalResourceHolder resourceHolder) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("'Committing' pseudo-transactional resource");
|
||||
}
|
||||
((PseudoTransactionalMessageSource<?>) source).afterCommit(resourceHolder.getResource());
|
||||
((PseudoTransactionalMessageSource<?,Object>) source).afterCommit(resourceHolder.getResource());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void afterCompletion(int status) {
|
||||
if (status != TransactionSynchronization.STATUS_COMMITTED) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("'Rolling back' pseudo-transactional resource");
|
||||
}
|
||||
((PseudoTransactionalMessageSource<?>) source).afterRollback(this.resourceHolder.getResource());
|
||||
((PseudoTransactionalMessageSource<?,Object>) source).afterRollback(this.resourceHolder.getResource());
|
||||
}
|
||||
super.afterCompletion(status);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2002-2012 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.util;
|
||||
|
||||
import org.springframework.context.expression.MapAccessor;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.expression.BeanResolver;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.expression.spel.support.StandardTypeConverter;
|
||||
|
||||
/**
|
||||
* Utility class with static methods for helping with establishing environments for
|
||||
* SpEL expressions.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 2.2
|
||||
*
|
||||
*/
|
||||
public class ExpressionUtils {
|
||||
|
||||
/**
|
||||
* Create a {@link StandardEvaluationContext} with a {@link MapAccessor} in its
|
||||
* property accessor property.
|
||||
* @return the evaluation context.
|
||||
*/
|
||||
public static StandardEvaluationContext createStandardEvaluationContext() {
|
||||
return createStandardEvaluationContext(null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link StandardEvaluationContext} with a {@link MapAccessor} in its
|
||||
* property accessor property and the supplied {@link BeanResolver} in its
|
||||
* beanResolver property.
|
||||
* @param beanResolver the bean factory.
|
||||
* @return the evaluation context.
|
||||
*/
|
||||
public static StandardEvaluationContext createStandardEvaluationContext(BeanResolver beanResolver) {
|
||||
return createStandardEvaluationContext(beanResolver, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link StandardEvaluationContext} with a {@link MapAccessor} in its
|
||||
* property accessor property and the supplied {@link ConversionService} in its
|
||||
* conversionService property.
|
||||
* @param conversionService the conversion service.
|
||||
* @return the evaluation context.
|
||||
*/
|
||||
public static StandardEvaluationContext createStandardEvaluationContext(ConversionService conversionService) {
|
||||
return createStandardEvaluationContext(null, conversionService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link StandardEvaluationContext} with a {@link MapAccessor} in its
|
||||
* property accessor property, the supplied {@link BeanResolver} in its
|
||||
* beanResolver property, and the supplied {@link ConversionService} in its
|
||||
* conversionService property.
|
||||
* @param beanResolver the bean factory.
|
||||
* @param conversionService the conversion service.
|
||||
* @return the evaluation context.
|
||||
*/
|
||||
public static StandardEvaluationContext createStandardEvaluationContext(BeanResolver beanResolver,
|
||||
ConversionService conversionService) {
|
||||
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
|
||||
evaluationContext.addPropertyAccessor(new MapAccessor());
|
||||
if (beanResolver != null) {
|
||||
evaluationContext.setBeanResolver(beanResolver);
|
||||
}
|
||||
if (conversionService != null) {
|
||||
evaluationContext.setTypeConverter(new StandardTypeConverter(conversionService));
|
||||
}
|
||||
return evaluationContext;
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ public class PseudoTransactionalMessageSourceTests {
|
||||
final Object object = new Object();
|
||||
final AtomicReference<Object> committed = new AtomicReference<Object>();
|
||||
final AtomicReference<Object> rolledBack = new AtomicReference<Object>();
|
||||
adapter.setSource(new PseudoTransactionalMessageSource<String>() {
|
||||
adapter.setSource(new PseudoTransactionalMessageSource<String, Object>() {
|
||||
|
||||
public Message<String> receive() {
|
||||
return new GenericMessage<String>("foo");
|
||||
@@ -61,6 +61,12 @@ public class PseudoTransactionalMessageSourceTests {
|
||||
public void afterRollback(Object resource) {
|
||||
rolledBack.set(resource);
|
||||
}
|
||||
|
||||
public void afterReceiveNoTx(Object resource) {
|
||||
}
|
||||
|
||||
public void afterSendNoTx(Object resource) {
|
||||
}
|
||||
});
|
||||
|
||||
TransactionSynchronizationManager.initSynchronization();
|
||||
@@ -81,7 +87,7 @@ public class PseudoTransactionalMessageSourceTests {
|
||||
final Object object = new Object();
|
||||
final AtomicReference<Object> committed = new AtomicReference<Object>();
|
||||
final AtomicReference<Object> rolledBack = new AtomicReference<Object>();
|
||||
adapter.setSource(new PseudoTransactionalMessageSource<String>() {
|
||||
adapter.setSource(new PseudoTransactionalMessageSource<String, Object>() {
|
||||
|
||||
public Message<String> receive() {
|
||||
return new GenericMessage<String>("foo");
|
||||
@@ -98,6 +104,12 @@ public class PseudoTransactionalMessageSourceTests {
|
||||
public void afterRollback(Object resource) {
|
||||
rolledBack.set(resource);
|
||||
}
|
||||
|
||||
public void afterReceiveNoTx(Object resource) {
|
||||
}
|
||||
|
||||
public void afterSendNoTx(Object resource) {
|
||||
}
|
||||
});
|
||||
|
||||
TransactionSynchronizationManager.initSynchronization();
|
||||
|
||||
Reference in New Issue
Block a user