Skip to content

Commit 24b9e96

Browse files
garyrussellartembilan
authored andcommitted
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 fa983fd commit 24b9e96

File tree

2 files changed

+34
-9
lines changed

2 files changed

+34
-9
lines changed

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

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
import org.springframework.transaction.support.TransactionSynchronizationManager;
109109
import org.springframework.transaction.support.TransactionTemplate;
110110
import org.springframework.util.Assert;
111+
import org.springframework.util.ClassUtils;
111112
import org.springframework.util.StringUtils;
112113
import org.springframework.util.concurrent.ListenableFuture;
113114
import org.springframework.util.concurrent.ListenableFutureCallback;
@@ -658,8 +659,8 @@ else if (listener instanceof MessageListener) {
658659
this.logger.info(this.toString());
659660
}
660661
Map<String, Object> props = KafkaMessageListenerContainer.this.consumerFactory.getConfigurationProperties();
661-
this.checkNullKeyForExceptions = checkDeserializer(findDeserializerClass(props, false));
662-
this.checkNullValueForExceptions = checkDeserializer(findDeserializerClass(props, true));
662+
this.checkNullKeyForExceptions = checkDeserializer(findDeserializerClass(props, consumerProperties, false));
663+
this.checkNullValueForExceptions = checkDeserializer(findDeserializerClass(props, consumerProperties, true));
663664
this.syncCommitTimeout = determineSyncCommitTimeout();
664665
if (this.containerProperties.getSyncCommitTimeout() == null) {
665666
// update the property so we can use it directly from code elsewhere
@@ -877,14 +878,21 @@ else if (timeout instanceof String) {
877878
}
878879
}
879880

880-
private Object findDeserializerClass(Map<String, Object> props, boolean isValue) {
881+
@Nullable
882+
private Object findDeserializerClass(Map<String, Object> props, Properties consumerOverrides, boolean isValue) {
881883
Object configuredDeserializer = isValue
882884
? KafkaMessageListenerContainer.this.consumerFactory.getValueDeserializer()
883885
: KafkaMessageListenerContainer.this.consumerFactory.getKeyDeserializer();
884886
if (configuredDeserializer == null) {
885-
return props.get(isValue
887+
Object deser = consumerOverrides.get(isValue
886888
? ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG
887889
: ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG);
890+
if (deser == null) {
891+
deser = props.get(isValue
892+
? ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG
893+
: ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG);
894+
}
895+
return deser;
888896
}
889897
else {
890898
return configuredDeserializer.getClass();
@@ -916,10 +924,23 @@ private void subscribeOrAssignTopics(final Consumer<? super K, ? super V> subscr
916924
}
917925
}
918926

919-
private boolean checkDeserializer(Object deser) {
920-
return deser instanceof Class
921-
? ErrorHandlingDeserializer.class.isAssignableFrom((Class<?>) deser)
922-
: deser instanceof String && deser.equals(ErrorHandlingDeserializer.class.getName());
927+
private boolean checkDeserializer(@Nullable Object deser) {
928+
Class<?> deserializer = null;
929+
if (deser instanceof Class) {
930+
deserializer = (Class<?>) deser;
931+
}
932+
else if (deser instanceof String) {
933+
try {
934+
deserializer = ClassUtils.forName((String) deser, getApplicationContext().getClassLoader());
935+
}
936+
catch (ClassNotFoundException | LinkageError e) {
937+
throw new IllegalStateException(e);
938+
}
939+
}
940+
else if (deser != null) {
941+
throw new IllegalStateException("Deserializer must be a class or class name, not a " + deser.getClass());
942+
}
943+
return deserializer == null ? false : ErrorHandlingDeserializer.class.isAssignableFrom(deserializer);
923944
}
924945

925946
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
@@ -170,7 +170,7 @@ else if (r.key() == null && t.getCause() instanceof DeserializationException) {
170170
@Bean
171171
public ConsumerFactory<String, String> cf() {
172172
Map<String, Object> props = KafkaTestUtils.consumerProps(TOPIC + ".g1", "false", embeddedKafka());
173-
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ErrorHandlingDeserializer.class.getName());
173+
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ExtendedEHD.class.getName());
174174
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, ErrorHandlingDeserializer.class);
175175
props.put(ErrorHandlingDeserializer.KEY_DESERIALIZER_CLASS, FailSometimesDeserializer.class);
176176
props.put(ErrorHandlingDeserializer.VALUE_DESERIALIZER_CLASS, FailSometimesDeserializer.class.getName());
@@ -231,4 +231,8 @@ public String deserialize(String topic, Headers headers, byte[] data) {
231231

232232
}
233233

234+
public static class ExtendedEHD<T> extends ErrorHandlingDeserializer<T> {
235+
236+
}
237+
234238
}

0 commit comments

Comments
 (0)