11/*
2- * Copyright 2017 the original author or authors.
2+ * Copyright 2017-2018 the original author or authors.
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
2020
2121import java .util .Collections ;
2222import java .util .Map ;
23+ import java .util .concurrent .BlockingQueue ;
24+ import java .util .concurrent .CountDownLatch ;
25+ import java .util .concurrent .TimeUnit ;
2326
2427import org .apache .kafka .clients .consumer .ConsumerConfig ;
2528import org .apache .kafka .clients .consumer .KafkaConsumer ;
29+ import org .apache .kafka .clients .producer .ProducerConfig ;
2630import org .junit .Test ;
31+ import org .junit .runner .RunWith ;
32+
33+ import org .springframework .beans .factory .annotation .Autowired ;
34+ import org .springframework .context .annotation .Configuration ;
35+ import org .springframework .kafka .listener .KafkaMessageListenerContainer ;
36+ import org .springframework .kafka .listener .MessageListener ;
37+ import org .springframework .kafka .listener .config .ContainerProperties ;
38+ import org .springframework .kafka .support .SendResult ;
39+ import org .springframework .kafka .test .context .EmbeddedKafka ;
40+ import org .springframework .kafka .test .rule .KafkaEmbedded ;
41+ import org .springframework .kafka .test .utils .KafkaTestUtils ;
42+ import org .springframework .kafka .transaction .KafkaTransactionManager ;
43+ import org .springframework .test .context .junit4 .SpringRunner ;
44+ import org .springframework .util .concurrent .ListenableFuture ;
2745
2846/**
2947 * @author Gary Russell
3048 * @since 1.0.6
3149 *
3250 */
51+ @ EmbeddedKafka (topics = { "txCache1" , "txCache2" , "txCacheSendFromListener" },
52+ brokerProperties = {
53+ "transaction.state.log.replication.factor=1" ,
54+ "transaction.state.log.min.isr=1" }
55+ )
56+ @ RunWith (SpringRunner .class )
3357public class DefaultKafkaConsumerFactoryTests {
3458
59+ @ Autowired
60+ private KafkaEmbedded embeddedKafka ;
61+
3562 @ Test
3663 public void testClientId () {
3764 Map <String , Object > configs = Collections .singletonMap (ConsumerConfig .CLIENT_ID_CONFIG , "foo" );
38- DefaultKafkaConsumerFactory <String , String > factory = new DefaultKafkaConsumerFactory <String , String >(configs ) {
65+ DefaultKafkaConsumerFactory <String , String > factory =
66+ new DefaultKafkaConsumerFactory <String , String >(configs ) {
3967
4068 @ Override
4169 protected KafkaConsumer <String , String > createKafkaConsumer (Map <String , Object > configs ) {
@@ -47,4 +75,87 @@ protected KafkaConsumer<String, String> createKafkaConsumer(Map<String, Object>
4775 factory .createConsumer ("-1" );
4876 }
4977
78+ @ SuppressWarnings ("unchecked" )
79+ @ Test
80+ public void testNestedTxProducerIsCached () throws Exception {
81+ Map <String , Object > producerProps = KafkaTestUtils .producerProps (this .embeddedKafka );
82+ producerProps .put (ProducerConfig .RETRIES_CONFIG , 1 );
83+ DefaultKafkaProducerFactory <Integer , String > pf = new DefaultKafkaProducerFactory <>(producerProps );
84+ KafkaTemplate <Integer , String > template = new KafkaTemplate <>(pf );
85+ DefaultKafkaProducerFactory <Integer , String > pfTx = new DefaultKafkaProducerFactory <>(producerProps );
86+ pfTx .setTransactionIdPrefix ("fooTx." );
87+ KafkaTemplate <Integer , String > templateTx = new KafkaTemplate <>(pfTx );
88+ Map <String , Object > consumerProps = KafkaTestUtils .consumerProps ("txCache1Group" , "false" , this .embeddedKafka );
89+ consumerProps .put (ConsumerConfig .AUTO_OFFSET_RESET_CONFIG , "earliest" );
90+ DefaultKafkaConsumerFactory <Integer , String > cf = new DefaultKafkaConsumerFactory <>(consumerProps );
91+ ContainerProperties containerProps = new ContainerProperties ("txCache1" );
92+ CountDownLatch latch = new CountDownLatch (1 );
93+ containerProps .setMessageListener ((MessageListener <Integer , String >) r -> {
94+ templateTx .executeInTransaction (t -> t .send ("txCacheSendFromListener" , "bar" ));
95+ templateTx .executeInTransaction (t -> t .send ("txCacheSendFromListener" , "baz" ));
96+ latch .countDown ();
97+ });
98+ KafkaTransactionManager <Integer , String > tm = new KafkaTransactionManager <>(pfTx );
99+ containerProps .setTransactionManager (tm );
100+ KafkaMessageListenerContainer <Integer , String > container = new KafkaMessageListenerContainer <>(cf ,
101+ containerProps );
102+ container .start ();
103+ try {
104+ ListenableFuture <SendResult <Integer , String >> future = template .send ("txCache1" , "foo" );
105+ future .get ();
106+ assertThat (KafkaTestUtils .getPropertyValue (pf , "cache" , BlockingQueue .class )).hasSize (0 );
107+ assertThat (latch .await (30 , TimeUnit .SECONDS )).isTrue ();
108+ assertThat (KafkaTestUtils .getPropertyValue (pfTx , "cache" , BlockingQueue .class )).hasSize (1 );
109+ }
110+ finally {
111+ container .stop ();
112+ pf .destroy ();
113+ pfTx .destroy ();
114+ }
115+ }
116+
117+ @ SuppressWarnings ("unchecked" )
118+ @ Test
119+ public void testContainerTxProducerIsNotCached () throws Exception {
120+ Map <String , Object > producerProps = KafkaTestUtils .producerProps (this .embeddedKafka );
121+ producerProps .put (ProducerConfig .RETRIES_CONFIG , 1 );
122+ DefaultKafkaProducerFactory <Integer , String > pf = new DefaultKafkaProducerFactory <>(producerProps );
123+ KafkaTemplate <Integer , String > template = new KafkaTemplate <>(pf );
124+ DefaultKafkaProducerFactory <Integer , String > pfTx = new DefaultKafkaProducerFactory <>(producerProps );
125+ pfTx .setTransactionIdPrefix ("fooTx." );
126+ KafkaTemplate <Integer , String > templateTx = new KafkaTemplate <>(pfTx );
127+ Map <String , Object > consumerProps = KafkaTestUtils .consumerProps ("txCache2Group" , "false" , this .embeddedKafka );
128+ consumerProps .put (ConsumerConfig .AUTO_OFFSET_RESET_CONFIG , "earliest" );
129+ DefaultKafkaConsumerFactory <Integer , String > cf = new DefaultKafkaConsumerFactory <>(consumerProps );
130+ ContainerProperties containerProps = new ContainerProperties ("txCache2" );
131+ CountDownLatch latch = new CountDownLatch (1 );
132+ containerProps .setMessageListener ((MessageListener <Integer , String >) r -> {
133+ templateTx .send ("txCacheSendFromListener" , "bar" );
134+ templateTx .send ("txCacheSendFromListener" , "baz" );
135+ latch .countDown ();
136+ });
137+ KafkaTransactionManager <Integer , String > tm = new KafkaTransactionManager <>(pfTx );
138+ containerProps .setTransactionManager (tm );
139+ KafkaMessageListenerContainer <Integer , String > container = new KafkaMessageListenerContainer <>(cf ,
140+ containerProps );
141+ container .start ();
142+ try {
143+ ListenableFuture <SendResult <Integer , String >> future = template .send ("txCache2" , "foo" );
144+ future .get ();
145+ assertThat (KafkaTestUtils .getPropertyValue (pf , "cache" , BlockingQueue .class )).hasSize (0 );
146+ assertThat (latch .await (30 , TimeUnit .SECONDS )).isTrue ();
147+ assertThat (KafkaTestUtils .getPropertyValue (pfTx , "cache" , BlockingQueue .class )).hasSize (0 );
148+ }
149+ finally {
150+ container .stop ();
151+ pf .destroy ();
152+ pfTx .destroy ();
153+ }
154+ }
155+
156+ @ Configuration
157+ public static class Config {
158+
159+ }
160+
50161}
0 commit comments