|
37 | 37 | import org.apache.kafka.clients.consumer.ConsumerRecord;
|
38 | 38 | import org.apache.kafka.clients.consumer.ConsumerRecords;
|
39 | 39 | import org.apache.kafka.clients.producer.Callback;
|
| 40 | +import org.apache.kafka.clients.producer.MockProducer; |
40 | 41 | import org.apache.kafka.clients.producer.Producer;
|
41 | 42 | import org.apache.kafka.clients.producer.ProducerConfig;
|
42 | 43 | import org.apache.kafka.clients.producer.ProducerRecord;
|
|
50 | 51 | import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
51 | 52 | import org.springframework.context.annotation.Bean;
|
52 | 53 | import org.springframework.context.annotation.Configuration;
|
| 54 | +import org.springframework.kafka.support.transaction.ResourcelessTransactionManager; |
53 | 55 | import org.springframework.kafka.test.EmbeddedKafkaBroker;
|
54 | 56 | import org.springframework.kafka.test.rule.EmbeddedKafkaRule;
|
55 | 57 | import org.springframework.kafka.test.utils.KafkaTestUtils;
|
@@ -212,6 +214,58 @@ public void testNoTx() {
|
212 | 214 | .hasMessageContaining("No transaction is in process;");
|
213 | 215 | }
|
214 | 216 |
|
| 217 | + @Test |
| 218 | + public void testTransactionSynchronization() { |
| 219 | + MockProducer<String, String> producer = new MockProducer<>(); |
| 220 | + producer.initTransactions(); |
| 221 | + |
| 222 | + @SuppressWarnings("unchecked") |
| 223 | + ProducerFactory<String, String> pf = mock(ProducerFactory.class); |
| 224 | + given(pf.transactionCapable()).willReturn(true); |
| 225 | + given(pf.createProducer()).willReturn(producer); |
| 226 | + |
| 227 | + KafkaTemplate<String, String> template = new KafkaTemplate<>(pf); |
| 228 | + template.setDefaultTopic(STRING_KEY_TOPIC); |
| 229 | + |
| 230 | + ResourcelessTransactionManager tm = new ResourcelessTransactionManager(); |
| 231 | + |
| 232 | + new TransactionTemplate(tm).execute(s -> { |
| 233 | + template.sendDefault("foo", "bar"); |
| 234 | + return null; |
| 235 | + }); |
| 236 | + |
| 237 | + assertThat(producer.history()).containsExactly(new ProducerRecord<>(STRING_KEY_TOPIC, "foo", "bar")); |
| 238 | + assertThat(producer.transactionCommitted()).isTrue(); |
| 239 | + assertThat(producer.closed()).isTrue(); |
| 240 | + } |
| 241 | + |
| 242 | + @Test |
| 243 | + public void testTransactionSynchronizationExceptionOnCommit() { |
| 244 | + MockProducer<String, String> producer = new MockProducer<>(); |
| 245 | + producer.initTransactions(); |
| 246 | + |
| 247 | + @SuppressWarnings("unchecked") |
| 248 | + ProducerFactory<String, String> pf = mock(ProducerFactory.class); |
| 249 | + given(pf.transactionCapable()).willReturn(true); |
| 250 | + given(pf.createProducer()).willReturn(producer); |
| 251 | + |
| 252 | + KafkaTemplate<String, String> template = new KafkaTemplate<>(pf); |
| 253 | + template.setDefaultTopic(STRING_KEY_TOPIC); |
| 254 | + |
| 255 | + ResourcelessTransactionManager tm = new ResourcelessTransactionManager(); |
| 256 | + |
| 257 | + new TransactionTemplate(tm).execute(s -> { |
| 258 | + template.sendDefault("foo", "bar"); |
| 259 | + |
| 260 | + // Mark the mock producer as fenced so it throws when committing the transaction |
| 261 | + producer.fenceProducer(); |
| 262 | + return null; |
| 263 | + }); |
| 264 | + |
| 265 | + assertThat(producer.transactionCommitted()).isFalse(); |
| 266 | + assertThat(producer.closed()).isTrue(); |
| 267 | + } |
| 268 | + |
215 | 269 | @Configuration
|
216 | 270 | @EnableTransactionManagement
|
217 | 271 | public static class DeclarativeConfig {
|
|
0 commit comments