From 12c1ad22f9788ae6fe135189e336a532a2dc9479 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Mon, 5 Mar 2018 16:29:11 -0500 Subject: [PATCH 1/2] Support JUnit5 in tests --- build.gradle | 21 ++++++++++++++++-- gradle/wrapper/gradle-wrapper.jar | Bin 54727 -> 54329 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- .../core/StreamsBuilderFactoryBeanTests.java | 14 ++++++------ 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index b086640f72..e3e1a37492 100644 --- a/build.gradle +++ b/build.gradle @@ -74,7 +74,10 @@ subprojects { subproject -> hamcrestVersion = '1.3' jacksonVersion = '2.9.4' jaywayJsonPathVersion = '2.4.0' - junitVersion = '4.12' + junit4Version = '4.12' + junitJupiterVersion = '5.1.0' + junitPlatformVersion = '1.1.0' + junitVintageVersion = '5.1.0' kafkaVersion = '1.0.0' mockitoVersion = '2.15.0' scalaVersion = '2.11' @@ -93,6 +96,19 @@ subprojects { subproject -> toolVersion = '0.7.9' } + // dependencies that are common across all java projects + dependencies { + testCompile "org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion" + testRuntime "org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion" + testRuntime "org.junit.platform:junit-platform-launcher:$junitPlatformVersion" + + // To support JUnit 4 tests + testRuntime "org.junit.vintage:junit-vintage-engine:$junitVintageVersion" + + // To avoid compiler warnings about @API annotations in JUnit code + testCompileOnly 'org.apiguardian:apiguardian-api:1.0.0' + } + // enable all compiler warnings; individual projects may customize further [compileJava, compileTestJava]*.options*.compilerArgs = ['-Xlint:all,-options'] @@ -104,6 +120,7 @@ subprojects { subproject -> append = false destinationFile = file("$buildDir/jacoco.exec") } + useJUnitPlatform() } checkstyle { @@ -202,7 +219,7 @@ project ('spring-kafka-test') { exclude group: 'org.slf4j', module: 'slf4j-log4j12' } - compile ("junit:junit:$junitVersion") { + compile ("junit:junit:$junit4Version") { exclude group: 'org.hamcrest', module: 'hamcrest-core' } compile ("org.mockito:mockito-core:$mockitoVersion") { diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 27768f1bbac3ce2d055b20d521f12da78d331e8e..f6b961fd5a86aa5fbfe90f707c3138408be7c718 100644 GIT binary patch delta 15755 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 Date: Mon, 5 Mar 2018 19:50:44 -0500 Subject: [PATCH 2/2] GH-599: Fix initial seek Fixes https://github.com/spring-projects/spring-kafka/issues/599 Previously, initial seeks using `TopicPartitionInitialOffset`s only worked with a provided `offset`. The `SeekPosition` field was ignored, and only used for subsequent seek operations. `initPartitionsIfNeeded()` now processes both styles of initial offset. --- .../KafkaMessageListenerContainer.java | 38 +++++++++++++--- .../KafkaMessageListenerContainerTests.java | 43 +++++++++++++++++++ 2 files changed, 76 insertions(+), 5 deletions(-) diff --git a/spring-kafka/src/main/java/org/springframework/kafka/listener/KafkaMessageListenerContainer.java b/spring-kafka/src/main/java/org/springframework/kafka/listener/KafkaMessageListenerContainer.java index cfe87249c2..0100865f65 100644 --- a/spring-kafka/src/main/java/org/springframework/kafka/listener/KafkaMessageListenerContainer.java +++ b/spring-kafka/src/main/java/org/springframework/kafka/listener/KafkaMessageListenerContainer.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -420,7 +421,8 @@ private final class ListenerConsumer implements SchedulingAwareRunnable, Consume this.definedPartitions = new HashMap<>(topicPartitions.size()); for (TopicPartitionInitialOffset topicPartition : topicPartitions) { this.definedPartitions.put(topicPartition.topicPartition(), - new OffsetMetadata(topicPartition.initialOffset(), topicPartition.isRelativeToCurrent())); + new OffsetMetadata(topicPartition.initialOffset(), topicPartition.isRelativeToCurrent(), + topicPartition.getPosition())); } consumer.assign(new ArrayList<>(this.definedPartitions.keySet())); } @@ -647,7 +649,12 @@ public void run() { this.count = 0; this.last = System.currentTimeMillis(); if (isRunning() && this.definedPartitions != null) { - initPartitionsIfNeeded(); + try { + initPartitionsIfNeeded(); + } + catch (Exception e) { + this.logger.error("Failed to set initial offsets", e); + } } long lastReceive = System.currentTimeMillis(); long lastAlertAt = lastReceive; @@ -1186,9 +1193,27 @@ private void initPartitionsIfNeeded() { /* * Note: initial position setting is only supported with explicit topic assignment. * When using auto assignment (subscribe), the ConsumerRebalanceListener is not - * called until we poll() the consumer. + * called until we poll() the consumer. Users can use a ConsumerAwareRebalanceListener + * or a ConsumerSeekAware listener in that case. */ - for (Entry entry : this.definedPartitions.entrySet()) { + Map partitions = new HashMap<>(this.definedPartitions); + Set beginnings = partitions.entrySet().stream() + .filter(e -> SeekPosition.BEGINNING.equals(e.getValue().seekPosition)) + .map(e -> e.getKey()) + .collect(Collectors.toSet()); + beginnings.forEach(k -> partitions.remove(k)); + Set ends = partitions.entrySet().stream() + .filter(e -> SeekPosition.END.equals(e.getValue().seekPosition)) + .map(e -> e.getKey()) + .collect(Collectors.toSet()); + ends.forEach(k -> partitions.remove(k)); + if (beginnings.size() > 0) { + this.consumer.seekToBeginning(beginnings); + } + if (ends.size() > 0) { + this.consumer.seekToEnd(ends); + } + for (Entry entry : partitions.entrySet()) { TopicPartition topicPartition = entry.getKey(); OffsetMetadata metadata = entry.getValue(); Long offset = metadata.offset; @@ -1378,9 +1403,12 @@ private static final class OffsetMetadata { private final boolean relativeToCurrent; - OffsetMetadata(Long offset, boolean relativeToCurrent) { + private final SeekPosition seekPosition; + + OffsetMetadata(Long offset, boolean relativeToCurrent, SeekPosition seekPosition) { this.offset = offset; this.relativeToCurrent = relativeToCurrent; + this.seekPosition = seekPosition; } } diff --git a/spring-kafka/src/test/java/org/springframework/kafka/listener/KafkaMessageListenerContainerTests.java b/spring-kafka/src/test/java/org/springframework/kafka/listener/KafkaMessageListenerContainerTests.java index d5bb484d3c..2a905191dc 100644 --- a/spring-kafka/src/test/java/org/springframework/kafka/listener/KafkaMessageListenerContainerTests.java +++ b/spring-kafka/src/test/java/org/springframework/kafka/listener/KafkaMessageListenerContainerTests.java @@ -35,6 +35,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -78,6 +79,7 @@ import org.springframework.kafka.listener.config.ContainerProperties; import org.springframework.kafka.support.Acknowledgment; import org.springframework.kafka.support.TopicPartitionInitialOffset; +import org.springframework.kafka.support.TopicPartitionInitialOffset.SeekPosition; import org.springframework.kafka.support.serializer.JsonDeserializer; import org.springframework.kafka.support.serializer.JsonSerializer; import org.springframework.kafka.test.rule.KafkaEmbedded; @@ -1675,6 +1677,47 @@ public void testPauseResume() throws Exception { container.stop(); } + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Test + public void testInitialSeek() throws Exception { + ConsumerFactory cf = mock(ConsumerFactory.class); + Consumer consumer = mock(Consumer.class); + given(cf.createConsumer(isNull(), eq("clientId"), isNull())).willReturn(consumer); + ConsumerRecords emptyRecords = new ConsumerRecords<>(Collections.emptyMap()); + final CountDownLatch latch = new CountDownLatch(1); + given(consumer.poll(anyLong())).willAnswer(i -> { + latch.countDown(); + Thread.sleep(50); + return emptyRecords; + }); + TopicPartitionInitialOffset[] topicPartition = new TopicPartitionInitialOffset[] { + new TopicPartitionInitialOffset("foo", 0, SeekPosition.BEGINNING), + new TopicPartitionInitialOffset("foo", 1, SeekPosition.END), + new TopicPartitionInitialOffset("foo", 2, 0L), + new TopicPartitionInitialOffset("foo", 3, Long.MAX_VALUE), + new TopicPartitionInitialOffset("foo", 4, SeekPosition.BEGINNING), + new TopicPartitionInitialOffset("foo", 5, SeekPosition.END), + }; + ContainerProperties containerProps = new ContainerProperties(topicPartition); + containerProps.setAckMode(AckMode.RECORD); + containerProps.setClientId("clientId"); + containerProps.setMessageListener((MessageListener) r -> { }); + KafkaMessageListenerContainer container = + new KafkaMessageListenerContainer<>(cf, containerProps); + container.start(); + assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue(); + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(consumer).seekToBeginning(captor.capture()); + assertThat(captor.getValue() + .equals(new HashSet<>(Arrays.asList(new TopicPartition("foo", 0), new TopicPartition("foo", 4))))); + verify(consumer).seekToEnd(captor.capture()); + assertThat(captor.getValue() + .equals(new HashSet<>(Arrays.asList(new TopicPartition("foo", 1), new TopicPartition("foo", 5))))); + verify(consumer).seek(new TopicPartition("foo", 2), 0L); + verify(consumer).seek(new TopicPartition("foo", 3), Long.MAX_VALUE); + container.stop(); + } + private Consumer spyOnConsumer(KafkaMessageListenerContainer container) { Consumer consumer = spy( KafkaTestUtils.getPropertyValue(container, "listenerConsumer.consumer", Consumer.class));