11/*
2- * Copyright 2017-2018 the original author or authors.
2+ * Copyright 2017-2020 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.
6767import org .springframework .kafka .core .KafkaTemplate ;
6868import org .springframework .kafka .core .ProducerFactory ;
6969import org .springframework .kafka .core .ProducerFactoryUtils ;
70+ import org .springframework .kafka .listener .AbstractMessageListenerContainer .AckMode ;
7071import org .springframework .kafka .listener .config .ContainerProperties ;
72+ import org .springframework .kafka .support .Acknowledgment ;
7173import org .springframework .kafka .support .TopicPartitionInitialOffset ;
7274import org .springframework .kafka .support .TransactionSupport ;
7375import org .springframework .kafka .test .rule .KafkaEmbedded ;
@@ -98,21 +100,28 @@ public class TransactionalContainerTests {
98100
99101 @ Test
100102 public void testConsumeAndProduceTransactionKTM () throws Exception {
101- testConsumeAndProduceTransactionGuts (false , false );
103+ testConsumeAndProduceTransactionGuts (false , false , AckMode . RECORD );
102104 }
103105
104106 @ Test
105107 public void testConsumeAndProduceTransactionKCTM () throws Exception {
106- testConsumeAndProduceTransactionGuts (true , false );
108+ testConsumeAndProduceTransactionGuts (true , false , AckMode . RECORD );
107109 }
108110
109111 @ Test
110112 public void testConsumeAndProduceTransactionHandleError () throws Exception {
111- testConsumeAndProduceTransactionGuts (false , true );
113+ testConsumeAndProduceTransactionGuts (false , true , AckMode .RECORD );
114+ }
115+
116+ @ Test
117+ public void testConsumeAndProduceTransactionKTMManual () throws Exception {
118+ testConsumeAndProduceTransactionGuts (false , false , AckMode .MANUAL_IMMEDIATE );
112119 }
113120
114121 @ SuppressWarnings ({ "rawtypes" , "unchecked" })
115- private void testConsumeAndProduceTransactionGuts (boolean chained , boolean handleError ) throws Exception {
122+ private void testConsumeAndProduceTransactionGuts (boolean chained , boolean handleError , AckMode ackMode )
123+ throws Exception {
124+
116125 Consumer consumer = mock (Consumer .class );
117126 final TopicPartition topicPartition = new TopicPartition ("foo" , 0 );
118127 willAnswer (i -> {
@@ -122,14 +131,15 @@ private void testConsumeAndProduceTransactionGuts(boolean chained, boolean handl
122131 }).given (consumer ).subscribe (any (Collection .class ), any (ConsumerRebalanceListener .class ));
123132 ConsumerRecords records = new ConsumerRecords (Collections .singletonMap (topicPartition ,
124133 Collections .singletonList (new ConsumerRecord <>("foo" , 0 , 0 , "key" , "value" ))));
134+ ConsumerRecords empty = new ConsumerRecords (Collections .emptyMap ());
125135 final AtomicBoolean done = new AtomicBoolean ();
126136 willAnswer (i -> {
127137 if (done .compareAndSet (false , true )) {
128138 return records ;
129139 }
130140 else {
131141 Thread .sleep (500 );
132- return null ;
142+ return empty ;
133143 }
134144 }).given (consumer ).poll (anyLong ());
135145 ConsumerFactory cf = mock (ConsumerFactory .class );
@@ -149,15 +159,34 @@ private void testConsumeAndProduceTransactionGuts(boolean chained, boolean handl
149159 }).given (pf ).createProducer ();
150160 KafkaTransactionManager tm = new KafkaTransactionManager (pf );
151161 ContainerProperties props = new ContainerProperties ("foo" );
162+ props .setAckMode (ackMode );
152163 props .setGroupId ("group" );
153164 props .setTransactionManager (tm );
154165 final KafkaTemplate template = new KafkaTemplate (pf );
155- props .setMessageListener ((MessageListener ) m -> {
156- template .send ("bar" , "baz" );
157- if (handleError ) {
158- throw new RuntimeException ("fail" );
166+ if (AckMode .MANUAL_IMMEDIATE .equals (ackMode )) {
167+ class AckListener implements AcknowledgingMessageListener {
168+ // not a lambda https://bugs.openjdk.java.net/browse/JDK-8074381
169+
170+ @ Override
171+ public void onMessage (Object data , Acknowledgment acknowledgment ) {
172+ template .send ("bar" , "baz" );
173+ if (handleError ) {
174+ throw new RuntimeException ("fail" );
175+ }
176+ acknowledgment .acknowledge ();
177+ }
178+
159179 }
160- });
180+ props .setMessageListener (new AckListener ());
181+ }
182+ else {
183+ props .setMessageListener ((MessageListener ) m -> {
184+ template .send ("bar" , "baz" );
185+ if (handleError ) {
186+ throw new RuntimeException ("fail" );
187+ }
188+ });
189+ }
161190 if (handleError ) {
162191 props .setErrorHandler ((e , data ) -> {
163192 });
0 commit comments