|
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.rule.KafkaEmbedded;
|
54 | 56 | import org.springframework.kafka.test.utils.KafkaTestUtils;
|
55 | 57 | import org.springframework.kafka.transaction.KafkaTransactionManager;
|
@@ -180,6 +182,58 @@ public void testNoTx() {
|
180 | 182 | .hasMessageContaining("No transaction is in process;");
|
181 | 183 | }
|
182 | 184 |
|
| 185 | + @Test |
| 186 | + public void testTransactionSynchronization() { |
| 187 | + MockProducer<String, String> producer = new MockProducer<>(); |
| 188 | + producer.initTransactions(); |
| 189 | + |
| 190 | + @SuppressWarnings("unchecked") |
| 191 | + ProducerFactory<String, String> pf = mock(ProducerFactory.class); |
| 192 | + given(pf.transactionCapable()).willReturn(true); |
| 193 | + given(pf.createProducer()).willReturn(producer); |
| 194 | + |
| 195 | + KafkaTemplate<String, String> template = new KafkaTemplate<>(pf); |
| 196 | + template.setDefaultTopic(STRING_KEY_TOPIC); |
| 197 | + |
| 198 | + ResourcelessTransactionManager tm = new ResourcelessTransactionManager(); |
| 199 | + |
| 200 | + new TransactionTemplate(tm).execute(s -> { |
| 201 | + template.sendDefault("foo", "bar"); |
| 202 | + return null; |
| 203 | + }); |
| 204 | + |
| 205 | + assertThat(producer.history()).containsExactly(new ProducerRecord<>(STRING_KEY_TOPIC, "foo", "bar")); |
| 206 | + assertThat(producer.transactionCommitted()).isTrue(); |
| 207 | + assertThat(producer.closed()).isTrue(); |
| 208 | + } |
| 209 | + |
| 210 | + @Test |
| 211 | + public void testTransactionSynchronizationExceptionOnCommit() { |
| 212 | + MockProducer<String, String> producer = new MockProducer<>(); |
| 213 | + producer.initTransactions(); |
| 214 | + |
| 215 | + @SuppressWarnings("unchecked") |
| 216 | + ProducerFactory<String, String> pf = mock(ProducerFactory.class); |
| 217 | + given(pf.transactionCapable()).willReturn(true); |
| 218 | + given(pf.createProducer()).willReturn(producer); |
| 219 | + |
| 220 | + KafkaTemplate<String, String> template = new KafkaTemplate<>(pf); |
| 221 | + template.setDefaultTopic(STRING_KEY_TOPIC); |
| 222 | + |
| 223 | + ResourcelessTransactionManager tm = new ResourcelessTransactionManager(); |
| 224 | + |
| 225 | + new TransactionTemplate(tm).execute(s -> { |
| 226 | + template.sendDefault("foo", "bar"); |
| 227 | + |
| 228 | + // Mark the mock producer as fenced so it throws when committing the transaction |
| 229 | + producer.fenceProducer(); |
| 230 | + return null; |
| 231 | + }); |
| 232 | + |
| 233 | + assertThat(producer.transactionCommitted()).isFalse(); |
| 234 | + assertThat(producer.closed()).isTrue(); |
| 235 | + } |
| 236 | + |
183 | 237 | @Configuration
|
184 | 238 | @EnableTransactionManagement
|
185 | 239 | public static class DeclarativeConfig {
|
|
0 commit comments