INT-2727 PseudoTX Refactoring
Remove the need for pseudo-transactional element INT-2727 PseudoTX Add PseudoTransactionalTransactionManager INT-2727 addressed PR comments cherry picked previous code for mail module to eliminate breaking change INT-2727 initial refactoring pseudo-tx support to use common configuration INT-2727 finalizing pseudo-tx synchronization support INT-2727 polishing INT-2727 polishing based on PR comments INT-2727 addressed PR comments INT-2727 polishing INT-2727 Remove PseudoTransactionalMessageSource Instead of getResource, bind the resource holder before receive() and then add attributes to the holder. INT-2727 polishing INT-2727 Polishing Remove bind of #resource; add beforeCommit() test; add TransactionTemplate tests.
This commit is contained in:
committed by
Gary Russell
parent
5828f70321
commit
94cc5a73e2
@@ -29,9 +29,11 @@ import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.context.IntegrationObjectSupport;
|
||||
import org.springframework.integration.core.PseudoTransactionalMessageSource;
|
||||
import org.springframework.integration.core.MessageSource;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.transaction.MessageSourceResourceHolder;
|
||||
import org.springframework.integration.util.ExpressionUtils;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.util.Assert;
|
||||
/**
|
||||
* Inbound channel adapter which returns a Message representing a view into
|
||||
@@ -43,7 +45,7 @@ import org.springframework.util.Assert;
|
||||
* @since 2.2
|
||||
*/
|
||||
public class RedisStoreMessageSource extends IntegrationObjectSupport
|
||||
implements PseudoTransactionalMessageSource<RedisStore, RedisStore> {
|
||||
implements MessageSource<RedisStore> {
|
||||
|
||||
private final ThreadLocal<RedisStore> resourceHolder = new ThreadLocal<RedisStore>();
|
||||
|
||||
@@ -117,7 +119,12 @@ public class RedisStoreMessageSource extends IntegrationObjectSupport
|
||||
Assert.hasText(key, "Failed to determine the key for the collection");
|
||||
|
||||
RedisStore store = this.createStoreView(key);
|
||||
this.resourceHolder.set(store);
|
||||
|
||||
Object holder = TransactionSynchronizationManager.getResource(this);
|
||||
if (holder != null) {
|
||||
Assert.isInstanceOf(MessageSourceResourceHolder.class, holder);
|
||||
((MessageSourceResourceHolder) holder).addAttribute("store", store);
|
||||
}
|
||||
|
||||
if (store instanceof Collection<?> && ((Collection<Object>)store).size() < 1){
|
||||
return null;
|
||||
@@ -158,12 +165,4 @@ public class RedisStoreMessageSource extends IntegrationObjectSupport
|
||||
public void afterRollback(Object object) {
|
||||
this.resourceHolder.remove();
|
||||
}
|
||||
|
||||
public void afterReceiveNoTx(RedisStore resource) {
|
||||
this.resourceHolder.remove();
|
||||
}
|
||||
|
||||
public void afterSendNoTx(RedisStore resource) {
|
||||
this.resourceHolder.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,18 +168,6 @@
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="error-channel" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<xsd:documentation>
|
||||
Channel to which Error Messages will be sent.
|
||||
</xsd:documentation>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.integration.MessageChannel"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="message-converter" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
@@ -193,6 +181,16 @@
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="error-channel" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Identifies channel that error messages will be sent to if a failure occurs in this
|
||||
gateway's invocation. If no "error-channel" reference is provided, this gateway will
|
||||
propagate Exceptions to the caller. To completely suppress Exceptions, provide a
|
||||
reference to the "nullChannel" here.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
|
||||
@@ -18,16 +18,24 @@ package org.springframework.integration.redis.config;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.BoundListOperations;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.data.redis.support.collections.RedisList;
|
||||
import org.springframework.data.redis.support.collections.RedisZSet;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessagingException;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.core.MessageHandler;
|
||||
import org.springframework.integration.core.SubscribableChannel;
|
||||
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
|
||||
import org.springframework.integration.redis.rules.RedisAvailable;
|
||||
import org.springframework.integration.redis.rules.RedisAvailableTests;
|
||||
@@ -85,6 +93,42 @@ public class RedisCollectionsInboundChannelAdapterParserTests extends RedisAvail
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@RedisAvailable
|
||||
public void testListInboundConfigurationWithSynchronizationAndRollback() throws Exception{
|
||||
JedisConnectionFactory jcf = this.getConnectionFactoryForTest();
|
||||
this.prepareList(jcf);
|
||||
|
||||
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<Object, Object>();
|
||||
redisTemplate.setConnectionFactory(jcf);
|
||||
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
|
||||
|
||||
BoundListOperations<Object, Object> ops = redisTemplate.boundListOps("baz");
|
||||
|
||||
assertTrue(ops.size() == 0);
|
||||
|
||||
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("list-inbound-adapter.xml", this.getClass());
|
||||
SourcePollingChannelAdapter spca = context.getBean("listAdapterWithSynchronizationAndRollback", SourcePollingChannelAdapter.class);
|
||||
SubscribableChannel redisFailChannel = context.getBean("redisFailChannel", SubscribableChannel.class);
|
||||
redisFailChannel.subscribe(new MessageHandler() {
|
||||
|
||||
public void handleMessage(Message<?> message) throws MessagingException {
|
||||
throw new MessagingException("intentional");
|
||||
}
|
||||
});
|
||||
spca.start();
|
||||
|
||||
Thread.sleep(1000);
|
||||
ops = redisTemplate.boundListOps("baz");
|
||||
assertTrue(ops.size() == 13);
|
||||
ops = redisTemplate.boundListOps("bar");
|
||||
assertTrue(ops.size() == 0);
|
||||
|
||||
spca.stop();
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@RedisAvailable
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -109,6 +153,27 @@ public class RedisCollectionsInboundChannelAdapterParserTests extends RedisAvail
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@RedisAvailable
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testListInboundConfigurationWithBeforeCommit() throws Exception{
|
||||
JedisConnectionFactory jcf = this.getConnectionFactoryForTest();
|
||||
this.prepareList(jcf);
|
||||
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("list-inbound-adapter.xml", this.getClass());
|
||||
SourcePollingChannelAdapter spca = context.getBean("listAdapterWithSynchronizationBeforeCommit", SourcePollingChannelAdapter.class);
|
||||
spca.start();
|
||||
QueueChannel redisChannel = context.getBean("redisChannel", QueueChannel.class);
|
||||
|
||||
QueueChannel adapterErrors = context.getBean("adapterErrors", QueueChannel.class);
|
||||
|
||||
Message<RedisList<Object>> message = (Message<RedisList<Object>>) redisChannel.receive(1000);
|
||||
assertNotNull(message);
|
||||
assertNotNull(adapterErrors.receive(2000));
|
||||
spca.stop();
|
||||
context.close();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@RedisAvailable
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@@ -54,7 +54,7 @@ public class RedisInboundChannelAdapterParserTests extends RedisAvailableTests{
|
||||
@Autowired @Qualifier("autoChannel.adapter")
|
||||
private RedisInboundChannelAdapter autoChannelAdapter;
|
||||
|
||||
@Test
|
||||
@Test
|
||||
@RedisAvailable
|
||||
public void validateConfiguration() {
|
||||
RedisInboundChannelAdapter adapter = context.getBean("adapter", RedisInboundChannelAdapter.class);
|
||||
@@ -67,7 +67,7 @@ public class RedisInboundChannelAdapterParserTests extends RedisAvailableTests{
|
||||
assertEquals(converterBean, accessor.getPropertyValue("messageConverter"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test
|
||||
@RedisAvailable
|
||||
public void testInboundChannelAdapterMessaging() throws Exception{
|
||||
JedisConnectionFactory connectionFactory = new JedisConnectionFactory();
|
||||
|
||||
@@ -22,9 +22,21 @@
|
||||
channel="redisChannel"
|
||||
auto-startup="false">
|
||||
<int:poller fixed-rate="2000" max-messages-per-poll="10">
|
||||
<int:pseudo-transactional on-success-expression="#resource.rename('bar')"/>
|
||||
<int:transactional synchronization-factory="syncFactory"/>
|
||||
</int:poller>
|
||||
</int-redis:store-inbound-channel-adapter>
|
||||
|
||||
<int-redis:store-inbound-channel-adapter id="listAdapterWithSynchronizationAndRollback"
|
||||
connection-factory="redisConnectionFactory"
|
||||
key-expression="'presidents'"
|
||||
channel="redisFailChannel"
|
||||
auto-startup="false">
|
||||
<int:poller fixed-rate="2000" max-messages-per-poll="10">
|
||||
<int:transactional synchronization-factory="syncFactory"/>
|
||||
</int:poller>
|
||||
</int-redis:store-inbound-channel-adapter>
|
||||
|
||||
<int:channel id="redisFailChannel"/>
|
||||
|
||||
<int-redis:store-inbound-channel-adapter id="listAdapterWithSynchronizationAndRedisTemplate"
|
||||
redis-template="redisTemplate"
|
||||
@@ -32,9 +44,32 @@
|
||||
channel="redisChannel"
|
||||
auto-startup="false">
|
||||
<int:poller fixed-rate="2000" max-messages-per-poll="10">
|
||||
<int:pseudo-transactional on-success-expression="#resource.rename('bar')"/>
|
||||
<int:transactional synchronization-factory="syncFactory"/>
|
||||
</int:poller>
|
||||
</int-redis:store-inbound-channel-adapter>
|
||||
|
||||
<int:transaction-synchronization-factory id="syncFactory">
|
||||
<int:after-commit expression="#resource.attributes['store'].rename('bar')"/>
|
||||
<int:after-rollback expression="#store.rename('baz')"/>
|
||||
</int:transaction-synchronization-factory>
|
||||
|
||||
<int-redis:store-inbound-channel-adapter id="listAdapterWithSynchronizationBeforeCommit"
|
||||
redis-template="redisTemplate"
|
||||
key-expression="'presidents'"
|
||||
channel="redisChannel"
|
||||
auto-startup="false">
|
||||
<int:poller fixed-rate="2000" max-messages-per-poll="10" error-channel="adapterErrors">
|
||||
<int:transactional synchronization-factory="syncFactory2"/>
|
||||
</int:poller>
|
||||
</int-redis:store-inbound-channel-adapter>
|
||||
|
||||
<int:channel id="adapterErrors">
|
||||
<int:queue/>
|
||||
</int:channel>
|
||||
|
||||
<int:transaction-synchronization-factory id="syncFactory2">
|
||||
<int:before-commit expression="5/0"/>
|
||||
</int:transaction-synchronization-factory>
|
||||
|
||||
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
|
||||
<property name="keySerializer">
|
||||
@@ -53,5 +88,7 @@
|
||||
<bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
|
||||
<property name="port" value="7379"/>
|
||||
</bean>
|
||||
|
||||
<bean id="transactionManager" class="org.springframework.integration.transaction.PseudoTransactionManager"/>
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -41,9 +41,13 @@
|
||||
auto-startup="false"
|
||||
collection-type="ZSET">
|
||||
<int:poller fixed-rate="1000" max-messages-per-poll="2">
|
||||
<int:pseudo-transactional on-success-expression="#resource.removeByScore(18, 18)" on-success-result-channel="nullChannel"/>
|
||||
<int:transactional synchronization-factory="syncFactory"/>
|
||||
</int:poller>
|
||||
</int-redis:store-inbound-channel-adapter>
|
||||
|
||||
<int:transaction-synchronization-factory id="syncFactory">
|
||||
<int:after-commit expression="#resource.attributes['store'].removeByScore(18, 18)"/>
|
||||
</int:transaction-synchronization-factory>
|
||||
|
||||
<int:channel id="redisChannel">
|
||||
<int:queue/>
|
||||
@@ -56,5 +60,7 @@
|
||||
<bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
|
||||
<property name="port" value="7379"/>
|
||||
</bean>
|
||||
|
||||
<bean id="transactionManager" class="org.springframework.integration.transaction.PseudoTransactionManager"/>
|
||||
|
||||
</beans>
|
||||
|
||||
Reference in New Issue
Block a user