Skip to content

Commit fd41e50

Browse files
committed
GH-1565: ErrorHandlingDeserializer Extensions
Resolves #1565 Previously extended `ErrorHandlingDeserializer` only worked if configured as a class rather than a class name. Spring Boot automatically converts the class names to classes but config outside of such an environment would not work. **cherry-pick to 2.5.x, 2.4.x, 2.3.x**
1 parent 7303413 commit fd41e50

File tree

2 files changed

+33
-9
lines changed

2 files changed

+33
-9
lines changed

spring-kafka/src/main/java/org/springframework/kafka/listener/KafkaMessageListenerContainer.java

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -680,8 +680,8 @@ else if (listener instanceof MessageListener) {
680680
this.logger.info(this.toString());
681681
}
682682
Map<String, Object> props = KafkaMessageListenerContainer.this.consumerFactory.getConfigurationProperties();
683-
this.checkNullKeyForExceptions = checkDeserializer(findDeserializerClass(props, false));
684-
this.checkNullValueForExceptions = checkDeserializer(findDeserializerClass(props, true));
683+
this.checkNullKeyForExceptions = checkDeserializer(findDeserializerClass(props, consumerProperties, false));
684+
this.checkNullValueForExceptions = checkDeserializer(findDeserializerClass(props, consumerProperties, true));
685685
this.syncCommitTimeout = determineSyncCommitTimeout();
686686
if (this.containerProperties.getSyncCommitTimeout() == null) {
687687
// update the property so we can use it directly from code elsewhere
@@ -846,14 +846,21 @@ else if (timeout instanceof String) {
846846
}
847847
}
848848

849-
private Object findDeserializerClass(Map<String, Object> props, boolean isValue) {
849+
@Nullable
850+
private Object findDeserializerClass(Map<String, Object> props, Properties consumerOverrides, boolean isValue) {
850851
Object configuredDeserializer = isValue
851852
? KafkaMessageListenerContainer.this.consumerFactory.getValueDeserializer()
852853
: KafkaMessageListenerContainer.this.consumerFactory.getKeyDeserializer();
853854
if (configuredDeserializer == null) {
854-
return props.get(isValue
855+
Object deser = consumerOverrides.get(isValue
855856
? ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG
856857
: ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG);
858+
if (deser == null) {
859+
deser = props.get(isValue
860+
? ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG
861+
: ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG);
862+
}
863+
return deser;
857864
}
858865
else {
859866
return configuredDeserializer.getClass();
@@ -885,10 +892,23 @@ private void subscribeOrAssignTopics(final Consumer<? super K, ? super V> subscr
885892
}
886893
}
887894

888-
private boolean checkDeserializer(Object deser) {
889-
return deser instanceof Class
890-
? ErrorHandlingDeserializer2.class.isAssignableFrom((Class<?>) deser)
891-
: deser instanceof String && deser.equals(ErrorHandlingDeserializer2.class.getName());
895+
private boolean checkDeserializer(@Nullable Object deser) {
896+
Class<?> deserializer = null;
897+
if (deser instanceof Class) {
898+
deserializer = (Class<?>) deser;
899+
}
900+
else if (deser instanceof String) {
901+
try {
902+
deserializer = ClassUtils.forName((String) deser, getApplicationContext().getClassLoader());
903+
}
904+
catch (ClassNotFoundException | LinkageError e) {
905+
throw new IllegalStateException(e);
906+
}
907+
}
908+
else if (deser != null) {
909+
throw new IllegalStateException("Deserializer must be a class or class name, not a " + deser.getClass());
910+
}
911+
return deserializer == null ? false : ErrorHandlingDeserializer2.class.isAssignableFrom(deserializer);
892912
}
893913

894914
protected void checkConsumer() {

spring-kafka/src/test/java/org/springframework/kafka/listener/ErrorHandlingDeserializerTests.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ else if (r.key() == null && t.getCause() instanceof DeserializationException) {
171171
public ConsumerFactory<String, String> cf() {
172172
Map<String, Object> props = KafkaTestUtils.consumerProps(TOPIC + ".g1", "false", embeddedKafka());
173173
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
174-
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ErrorHandlingDeserializer2.class.getName());
174+
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ExtendedEHD.class.getName());
175175
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, ErrorHandlingDeserializer2.class);
176176
props.put(ErrorHandlingDeserializer2.KEY_DESERIALIZER_CLASS, FailSometimesDeserializer.class);
177177
props.put(ErrorHandlingDeserializer2.VALUE_DESERIALIZER_CLASS, FailSometimesDeserializer.class.getName());
@@ -233,4 +233,8 @@ public String deserialize(String topic, Headers headers, byte[] data) {
233233

234234
}
235235

236+
public static class ExtendedEHD<T> extends ErrorHandlingDeserializer2<T> {
237+
238+
}
239+
236240
}

0 commit comments

Comments
 (0)