|
23 | 23 | import java.util.List;
|
24 | 24 | import java.util.Set;
|
25 | 25 | import java.util.function.Consumer;
|
| 26 | +import java.util.regex.Pattern; |
26 | 27 |
|
27 | 28 | import org.apache.commons.logging.LogFactory;
|
28 | 29 |
|
29 | 30 | import org.springframework.beans.factory.annotation.Qualifier;
|
30 | 31 | import org.springframework.core.log.LogAccessor;
|
31 | 32 | import org.springframework.kafka.KafkaException;
|
32 | 33 | import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
|
| 34 | +import org.springframework.kafka.config.KafkaListenerContainerFactory; |
| 35 | +import org.springframework.kafka.config.KafkaListenerEndpoint; |
33 | 36 | import org.springframework.kafka.listener.AcknowledgingConsumerAwareMessageListener;
|
34 | 37 | import org.springframework.kafka.listener.CommonErrorHandler;
|
35 | 38 | import org.springframework.kafka.listener.ConcurrentMessageListenerContainer;
|
|
38 | 41 | import org.springframework.kafka.listener.DefaultErrorHandler;
|
39 | 42 | import org.springframework.kafka.listener.KafkaConsumerBackoffManager;
|
40 | 43 | import org.springframework.kafka.listener.adapter.KafkaBackoffAwareMessageListenerAdapter;
|
| 44 | +import org.springframework.kafka.support.TopicPartitionOffset; |
41 | 45 | import org.springframework.util.Assert;
|
42 | 46 | import org.springframework.util.backoff.FixedBackOff;
|
43 | 47 |
|
44 | 48 | /**
|
45 | 49 | *
|
46 |
| - * Configures the provided {@link ConcurrentKafkaListenerContainerFactory} with a |
47 |
| - * {@link DefaultErrorHandler}, the {@link DeadLetterPublishingRecoverer} created by |
48 |
| - * the {@link DeadLetterPublishingRecovererFactory}. |
| 50 | + * Decorates the provided {@link ConcurrentKafkaListenerContainerFactory} to add a |
| 51 | + * {@link DefaultErrorHandler} and the {@link DeadLetterPublishingRecoverer} |
| 52 | + * created by the {@link DeadLetterPublishingRecovererFactory}. |
49 | 53 | *
|
50 |
| - * Mind that the same factory can be used by many different |
51 |
| - * {@link org.springframework.kafka.annotation.RetryableTopic}s but should not be shared |
52 |
| - * with non retryable topics as some of their configurations will be overriden. |
| 54 | + * Also sets {@link ContainerProperties#setIdlePartitionEventInterval(Long)} |
| 55 | + * and {@link ContainerProperties#setPollTimeout(long)} if its defaults haven't |
| 56 | + * been overridden by the user. |
| 57 | + * |
| 58 | + * Since 2.8.3 these configurations don't interfere with the provided factory |
| 59 | + * instance itself, so the same factory instance can be shared among retryable and |
| 60 | + * non-retryable endpoints. |
53 | 61 | *
|
54 | 62 | * @author Tomaz Fernandes
|
55 | 63 | * @since 2.7
|
@@ -95,20 +103,62 @@ public class ListenerContainerFactoryConfigurer {
|
95 | 103 | this.clock = clock;
|
96 | 104 | }
|
97 | 105 |
|
| 106 | + /** |
| 107 | + * Configures the provided {@link ConcurrentKafkaListenerContainerFactory}. |
| 108 | + * @param containerFactory the factory instance to be configured. |
| 109 | + * @param configuration the configuration provided by the {@link RetryTopicConfiguration}. |
| 110 | + * @return the configured factory instance. |
| 111 | + * @deprecated in favor of |
| 112 | + * {@link #decorateFactory(ConcurrentKafkaListenerContainerFactory, Configuration)}. |
| 113 | + */ |
| 114 | + @Deprecated |
98 | 115 | public ConcurrentKafkaListenerContainerFactory<?, ?> configure(
|
99 | 116 | ConcurrentKafkaListenerContainerFactory<?, ?> containerFactory, Configuration configuration) {
|
100 | 117 | return isCached(containerFactory)
|
101 | 118 | ? containerFactory
|
102 | 119 | : addToCache(doConfigure(containerFactory, configuration.backOffValues));
|
103 | 120 | }
|
104 | 121 |
|
| 122 | + /** |
| 123 | + * Configures the provided {@link ConcurrentKafkaListenerContainerFactory}. |
| 124 | + * Meant to be used for the main endpoint, this method ignores the provided backOff values. |
| 125 | + * @param containerFactory the factory instance to be configured. |
| 126 | + * @param configuration the configuration provided by the {@link RetryTopicConfiguration}. |
| 127 | + * @return the configured factory instance. |
| 128 | + * @deprecated in favor of |
| 129 | + * {@link #decorateFactoryWithoutBackOffValues(ConcurrentKafkaListenerContainerFactory, Configuration)}. |
| 130 | + */ |
| 131 | + @Deprecated |
105 | 132 | public ConcurrentKafkaListenerContainerFactory<?, ?> configureWithoutBackOffValues(
|
106 | 133 | ConcurrentKafkaListenerContainerFactory<?, ?> containerFactory, Configuration configuration) {
|
107 | 134 | return isCached(containerFactory)
|
108 | 135 | ? containerFactory
|
109 | 136 | : doConfigure(containerFactory, Collections.emptyList());
|
110 | 137 | }
|
111 | 138 |
|
| 139 | + /** |
| 140 | + * Decorates the provided {@link ConcurrentKafkaListenerContainerFactory}. |
| 141 | + * @param factory the factory instance to be decorated. |
| 142 | + * @param configuration the configuration provided by the {@link RetryTopicConfiguration}. |
| 143 | + * @return the decorated factory instance. |
| 144 | + */ |
| 145 | + public KafkaListenerContainerFactory<?> decorateFactory(ConcurrentKafkaListenerContainerFactory<?, ?> factory, |
| 146 | + Configuration configuration) { |
| 147 | + return new RetryTopicListenerContainerFactoryDecorator(factory, configuration.backOffValues); |
| 148 | + } |
| 149 | + |
| 150 | + /** |
| 151 | + * Decorates the provided {@link ConcurrentKafkaListenerContainerFactory}. |
| 152 | + * Meant to be used for the main endpoint, this method ignores the provided backOff values. |
| 153 | + * @param factory the factory instance to be decorated. |
| 154 | + * @param configuration the configuration provided by the {@link RetryTopicConfiguration}. |
| 155 | + * @return the decorated factory instance. |
| 156 | + */ |
| 157 | + public KafkaListenerContainerFactory<?> decorateFactoryWithoutBackOffValues( |
| 158 | + ConcurrentKafkaListenerContainerFactory<?, ?> factory, Configuration configuration) { |
| 159 | + return new RetryTopicListenerContainerFactoryDecorator(factory, Collections.emptyList()); |
| 160 | + } |
| 161 | + |
112 | 162 | private ConcurrentKafkaListenerContainerFactory<?, ?> doConfigure(
|
113 | 163 | ConcurrentKafkaListenerContainerFactory<?, ?> containerFactory, List<Long> backOffValues) {
|
114 | 164 |
|
@@ -217,6 +267,45 @@ private <T> T checkAndCast(Object obj, Class<T> clazz) {
|
217 | 267 | return (T) obj;
|
218 | 268 | }
|
219 | 269 |
|
| 270 | + private class RetryTopicListenerContainerFactoryDecorator implements KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<?, ?>> { |
| 271 | + |
| 272 | + private final ConcurrentKafkaListenerContainerFactory<?, ?> delegate; |
| 273 | + private final List<Long> backOffValues; |
| 274 | + |
| 275 | + RetryTopicListenerContainerFactoryDecorator(ConcurrentKafkaListenerContainerFactory<?, ?> delegate, List<Long> backOffValues) { |
| 276 | + this.delegate = delegate; |
| 277 | + this.backOffValues = backOffValues; |
| 278 | + } |
| 279 | + |
| 280 | + @Override |
| 281 | + public ConcurrentMessageListenerContainer<?, ?> createListenerContainer(KafkaListenerEndpoint endpoint) { |
| 282 | + return decorate(this.delegate.createListenerContainer(endpoint)); |
| 283 | + } |
| 284 | + |
| 285 | + private ConcurrentMessageListenerContainer<?, ?> decorate(ConcurrentMessageListenerContainer<?, ?> listenerContainer) { |
| 286 | + setupBackoffAwareMessageListenerAdapter(listenerContainer, this.backOffValues); |
| 287 | + listenerContainer |
| 288 | + .setCommonErrorHandler(createErrorHandler( |
| 289 | + ListenerContainerFactoryConfigurer.this.deadLetterPublishingRecovererFactory.create())); |
| 290 | + return listenerContainer; |
| 291 | + } |
| 292 | + |
| 293 | + @Override |
| 294 | + public ConcurrentMessageListenerContainer<?, ?> createContainer(TopicPartitionOffset... topicPartitions) { |
| 295 | + return decorate(this.delegate.createContainer(topicPartitions)); |
| 296 | + } |
| 297 | + |
| 298 | + @Override |
| 299 | + public ConcurrentMessageListenerContainer<?, ?> createContainer(String... topics) { |
| 300 | + return decorate(this.delegate.createContainer(topics)); |
| 301 | + } |
| 302 | + |
| 303 | + @Override |
| 304 | + public ConcurrentMessageListenerContainer<?, ?> createContainer(Pattern topicPattern) { |
| 305 | + return decorate(this.delegate.createContainer(topicPattern)); |
| 306 | + } |
| 307 | + } |
| 308 | + |
220 | 309 | static class Configuration {
|
221 | 310 |
|
222 | 311 | private final List<Long> backOffValues;
|
|
0 commit comments