준호씨의 블로그
Jackson - kafka listener StringJsonMessageConverter에서 enum에 없는 값이 들어 올 때 기본값 지정 방법. @JsonEnumDefaultValue 본문
Jackson - kafka listener StringJsonMessageConverter에서 enum에 없는 값이 들어 올 때 기본값 지정 방법. @JsonEnumDefaultValue
준호씨 2021. 1. 4. 00:05
org.springframework.kafka.listener.ListenerExecutionFailedException: Listener failed; nested exception is org.springframework.kafka.support.converter.ConversionException: Failed to convert from JSON; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `kr.pe.junho85.demo.model.TestType` from String "TEST": not one of the values accepted for Enum class: [BETA, UNKNOWN, ALPHA]
at [Source: (String)"{"testType": "TEST"}"; line: 1, column: 14] (through reference chain: kr.pe.junho85.demo.model.TestMessage["testType"])
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.decorateException(KafkaMessageListenerContainer.java:2105) ~[spring-kafka-2.6.4.jar:2.6.4]
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeErrorHandler(KafkaMessageListenerContainer.java:2090) ~[spring-kafka-2.6.4.jar:2.6.4]
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeRecordListener(KafkaMessageListenerContainer.java:1989) ~[spring-kafka-2.6.4.jar:2.6.4]
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeWithRecords(KafkaMessageListenerContainer.java:1916) ~[spring-kafka-2.6.4.jar:2.6.4]
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeRecordListener(KafkaMessageListenerContainer.java:1810) ~[spring-kafka-2.6.4.jar:2.6.4]
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeListener(KafkaMessageListenerContainer.java:1529) ~[spring-kafka-2.6.4.jar:2.6.4]
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.pollAndInvoke(KafkaMessageListenerContainer.java:1176) ~[spring-kafka-2.6.4.jar:2.6.4]
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.run(KafkaMessageListenerContainer.java:1073) ~[spring-kafka-2.6.4.jar:2.6.4]
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) ~[na:na]
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]
Caused by: org.springframework.kafka.support.converter.ConversionException: Failed to convert from JSON; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `kr.pe.junho85.demo.model.TestType` from String "TEST": not one of the values accepted for Enum class: [BETA, UNKNOWN, ALPHA]
at [Source: (String)"{"testType": "TEST"}"; line: 1, column: 14] (through reference chain: kr.pe.junho85.demo.model.TestMessage["testType"])
at org.springframework.kafka.support.converter.JsonMessageConverter.extractAndConvertValue(JsonMessageConverter.java:117) ~[spring-kafka-2.6.4.jar:2.6.4]
at org.springframework.kafka.support.converter.MessagingMessageConverter.toMessage(MessagingMessageConverter.java:123) ~[spring-kafka-2.6.4.jar:2.6.4]
at org.springframework.kafka.listener.adapter.MessagingMessageListenerAdapter.toMessagingMessage(MessagingMessageListenerAdapter.java:304) ~[spring-kafka-2.6.4.jar:2.6.4]
at org.springframework.kafka.listener.adapter.RecordMessagingMessageListenerAdapter.onMessage(RecordMessagingMessageListenerAdapter.java:77) ~[spring-kafka-2.6.4.jar:2.6.4]
at org.springframework.kafka.listener.adapter.RecordMessagingMessageListenerAdapter.onMessage(RecordMessagingMessageListenerAdapter.java:51) ~[spring-kafka-2.6.4.jar:2.6.4]
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeOnMessage(KafkaMessageListenerContainer.java:2057) ~[spring-kafka-2.6.4.jar:2.6.4]
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeOnMessage(KafkaMessageListenerContainer.java:2039) ~[spring-kafka-2.6.4.jar:2.6.4]
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeRecordListener(KafkaMessageListenerContainer.java:1976) ~[spring-kafka-2.6.4.jar:2.6.4]
... 8 common frames omitted
Caused by: com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `kr.pe.junho85.demo.model.TestType` from String "TEST": not one of the values accepted for Enum class: [BETA, UNKNOWN, ALPHA]
at [Source: (String)"{"testType": "TEST"}"; line: 1, column: 14] (through reference chain: kr.pe.junho85.demo.model.TestMessage["testType"])
at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:67) ~[jackson-databind-2.11.3.jar:2.11.3]
at com.fasterxml.jackson.databind.DeserializationContext.weirdStringException(DeserializationContext.java:1702) ~[jackson-databind-2.11.3.jar:2.11.3]
at com.fasterxml.jackson.databind.DeserializationContext.handleWeirdStringValue(DeserializationContext.java:947) ~[jackson-databind-2.11.3.jar:2.11.3]
at com.fasterxml.jackson.databind.deser.std.EnumDeserializer._deserializeAltString(EnumDeserializer.java:257) ~[jackson-databind-2.11.3.jar:2.11.3]
at com.fasterxml.jackson.databind.deser.std.EnumDeserializer.deserialize(EnumDeserializer.java:181) ~[jackson-databind-2.11.3.jar:2.11.3]
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129) ~[jackson-databind-2.11.3.jar:2.11.3]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:371) ~[jackson-databind-2.11.3.jar:2.11.3]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:164) ~[jackson-databind-2.11.3.jar:2.11.3]
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4526) ~[jackson-databind-2.11.3.jar:2.11.3]
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3468) ~[jackson-databind-2.11.3.jar:2.11.3]
at org.springframework.kafka.support.converter.JsonMessageConverter.extractAndConvertValue(JsonMessageConverter.java:114) ~[spring-kafka-2.6.4.jar:2.6.4]
... 15 common frames omitted
KafkaListener로 메시지를 받아와서 처리할 때 enum에 맞는 값이 없어서 파싱 오류가 나는 경우가 있습니다.
예를 들어 아래와 같은 KafkaListener가 있습니다.
@KafkaListener(id = "test", topics = "test")
public void testListener(TestMessage testMessage) {
System.out.println(testMessage);
}
KafkaConfig에서 kafkaListenerContainerFactory() 설정을 아래처럼 만들어서 String이 들어오면 json을 파싱 해서 객체에 맵핑되도록 합니다.
@Bean
KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<String, Object>> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, Object> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setMessageConverter(new StringJsonMessageConverter());
factory.setConsumerFactory(consumerFactory());
return factory;
}
TestMessage는 아래처럼 TestType을 받습니다.
@Builder
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TestMessage {
private TestType testType;
}
TestType에는 예제와 같이 ALPHA, BETA가 있고요.
public enum TestType {
ALPHA,
BETA;
}
kafka메시지가
{"testType": "ALPHA"}
처럼 TestType에 있는 값이 들어오면 정상적으로 처리됩니다.
하지만 아래처럼 TestType에 없는 값이 들어오면
{"testType": "TEST"}
아래와 같이 enum에 없는 타입이라고 deserialize오류가 발생합니다.
Caused by: com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `kr.pe.junho85.demo.model.TestType` from String "TEST": not one of the values accepted for Enum class: [BETA, ALPHA]
at [Source: (String)"{"testType": "TEST"}"; line: 1, column: 14] (through reference chain: kr.pe.junho85.demo.model.TestMessage["testType"])
없는 타입이 들어올 경우 특정 타입을 지정해 주는 방법입니다.
일단 enum에 없는 값이 들어올 경우 기본적으로 지정하는 타입을 만들고 @JsonEnumDefaultValue를 지정해 줍니다.
public enum TestType {
ALPHA,
BETA,
@JsonEnumDefaultValue UNKNOWN;
}
jackson설정에 READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE를 활성화시켜줍니다.
spring:
jackson:
deserialization:
READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE: true
그리고 kafkaListenerContainerFactory()의 StringJsonMessageConverter에 objectMapper를 인자로 넘겨줍니다.
@RequiredArgsConstructor
@Configuration
public class KafkaConfig {
...
private final ObjectMapper objectMapper;
...
@Bean
KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<String, Object>> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, Object> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setMessageConverter(new StringJsonMessageConverter(objectMapper));
factory.setConsumerFactory(consumerFactory());
return factory;
}
이제 다음과 같이 enum에 정의되지 않은 타입의 메시지가 들어오면
{"testType": "TEST"}
@JsonEnumDefaultValue로 지정된 UNKNOWN이 기본 지정되게 됩니다.
TestMessage(testType=UNKNOWN)
코드: github.com/junho85/spring-kafka-enum
#ListenerExcutionFailedException #ConversionExceptoin #InvalidFormatException
'개발이야기' 카테고리의 다른 글
정원사들 시즌6 시작. 출석부 만들기 (0) | 2021.01.21 |
---|---|
perl - millisecond로 sleep주는 방법 (0) | 2021.01.19 |
FreeMarker - null 데이터 오류 안나도록 하기. 포멧팅한 date는? (0) | 2021.01.03 |
Java - HashMap 초깃값을 갖도록 초기화 하는 방법 (0) | 2020.12.30 |
내가 만든 WebFlux가 느렸던 이유 발표 영상 필기 (0) | 2020.12.23 |