+++ /dev/null
-package de.juplo.kafka;
-
-import lombok.extern.slf4j.Slf4j;
-import org.apache.kafka.clients.consumer.Consumer;
-import org.apache.kafka.clients.consumer.ConsumerRecord;
-import org.apache.kafka.clients.consumer.ConsumerRecords;
-import org.apache.kafka.common.TopicPartition;
-import org.springframework.kafka.listener.CommonErrorHandler;
-import org.springframework.kafka.listener.MessageListenerContainer;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-
-
-@Slf4j
-public class ApplicationErrorHandler implements CommonErrorHandler
-{
- private Exception exception;
- private boolean ack = true;
-
- @Override
- public boolean remainingRecords()
- {
- return true;
- }
-
- @Override
- public void handleOtherException(
- Exception thrownException,
- Consumer<?, ?> consumer,
- MessageListenerContainer container,
- boolean batchListener)
- {
- rememberExceptionAndStopContainer(thrownException, container);
- }
-
- @Override
- public void handleRemaining(
- Exception thrownException,
- List<ConsumerRecord<?, ?>> records,
- Consumer<?, ?> consumer,
- MessageListenerContainer container)
- {
- Map<TopicPartition, Long> offsets = new HashMap<>();
- records.forEach(record ->
- offsets.computeIfAbsent(
- new TopicPartition(record.topic(), record.partition()),
- offset -> record.offset()));
- offsets.forEach((tp, offset) -> consumer.seek(tp, offset));
- rememberExceptionAndStopContainer(thrownException, container);
- }
-
- @Override
- public void handleBatch(
- Exception thrownException,
- ConsumerRecords<?, ?> data,
- Consumer<?, ?> consumer,
- MessageListenerContainer container,
- Runnable invokeListener)
- {
- // Do not commit the polled offsets on a logic-error
- ack = false;
- rememberExceptionAndStopContainer(thrownException, container);
- }
-
- private void rememberExceptionAndStopContainer(
- Exception exception,
- MessageListenerContainer container)
- {
- log.error("{}, stopping container {} abnormally", exception, container);
- this.exception = exception;
- container.stopAbnormally(() -> log.info("{} is stopped", container));
- }
-
- @Override
- public boolean isAckAfterHandle()
- {
- return ack;
- }
-
-
- public Optional<Exception> getException()
- {
- return Optional.ofNullable(exception);
- }
-
- public void clearState()
- {
- this.exception = null;
- this.ack = true;
- }
-}
{
private final String id;
private final KafkaListenerEndpointRegistry registry;
- private final ApplicationErrorHandler errorHandler;
private final RecordHandler recordHandler;
private long consumed = 0;
throw new IllegalStateException("Consumer instance " + id + " is already running!");
log.info("{} - Starting - consumed {} messages before", id, consumed);
- errorHandler.clearState();
registry.getListenerContainer(id).start();
}
{
return registry.getListenerContainer(id).isRunning();
}
-
- public Optional<Exception> exitStatus()
- {
- if (running())
- throw new IllegalStateException("No exit-status available: Consumer instance " + id + " is running!");
-
- return errorHandler.getException();
- }
}
assertSeenOffsetsEqualCommittedOffsets(recordHandler.seenOffsets);
});
- assertThatExceptionOfType(IllegalStateException.class)
- .isThrownBy(() -> endlessConsumer.exitStatus())
- .describedAs("Consumer should still be running");
+ assertThat(endlessConsumer.running())
+ .describedAs("Consumer should still be running")
+ .isTrue();
endlessConsumer.stop();
recordGenerator.assertBusinessLogic();
.describedAs("Received not all sent events")
.isLessThan(numberOfGeneratedMessages);
- assertThatNoException()
- .describedAs("Consumer should not be running")
- .isThrownBy(() -> endlessConsumer.exitStatus());
- assertThat(endlessConsumer.exitStatus())
- .describedAs("Consumer should have exited abnormally")
- .containsInstanceOf(RecordDeserializationException.class);
+ assertThat(endlessConsumer.running())
+ .describedAs("Consumer should have exited")
+ .isFalse();
recordGenerator.assertBusinessLogic();
}
assertSeenOffsetsEqualCommittedOffsets(recordHandler.seenOffsets);
- assertThatNoException()
+ assertThat(endlessConsumer.running())
.describedAs("Consumer should not be running")
- .isThrownBy(() -> endlessConsumer.exitStatus());
- assertThat(endlessConsumer.exitStatus())
- .describedAs("Consumer should have exited abnormally")
- .containsInstanceOf(RuntimeException.class);
+ .isFalse();
recordGenerator.assertBusinessLogic();
}
return new TestRecordHandler(applicationRecordHandler);
}
- @Bean(destroyMethod = "close")
- public org.apache.kafka.clients.consumer.Consumer<String, Message> kafkaConsumer(ConsumerFactory<String, Message> factory)
- {
- return factory.createConsumer();
- }
+ @Bean(destroyMethod = "close")
+ public org.apache.kafka.clients.consumer.Consumer<String, Message> kafkaConsumer(ConsumerFactory<String, Message> factory)
+ {
+ return factory.createConsumer();
+ }
}
}