YeonCheol Jang
YeonCheol Jang

Reputation: 115

Kafka consumer ClassNotFoundException

(Before start question, my English might not be enough to describe all clearly. Kindly let me know if you don't understand.)

I am trying to send an data object from A spring project(producer) to B spring project(consumer) by Kafka.

Problem is that data objects in A and B are having different classpath. So B project data class is not able to map A project's field.

But two objects have same fields. So i want to get object from A project as an argument on B project.

Error message

Listener failed; nested exception is 

org.springframework.kafka.support.serializer.DeserializationException: failed to deserialize; nested exception is 

org.springframework.messaging.converter.MessageConversionException: failed to resolve class name. Class not found [com.example.springboot.DTO.kafka.PostViewCountDTO]; nested exception is 

java.lang.ClassNotFoundException: com.example.springboot.DTO.kafka.PostViewCountDTO

build.gradle

    implementation 'org.apache.kafka:kafka-clients:2.8.0'
    implementation 'org.apache.kafka:kafka_2.13:2.8.0'
    implementation 'org.springframework.boot:spring-boot-starter-web:2.5.3'

Data class (using A and B projects)

public class PostViewCountDTO implements Serializable {
    private static final long serialVersionUID = 1L;

    @NotNull
    private long postNo;
}

producer config

@Configuration
public class PostViewProducerConfig {

    @Value("${spring.kafka.producer.bootstrap-servers}")
    private String bootstrapServer;

    @Bean
    public Map<String,Object> postViewProducerConfigs() {
        return JsonSerializer.getStringObjectMap(bootstrapServer);
    }

    @Bean
    public ProducerFactory<String, PostViewCountDTO> postViewCountDTOProducerFactory() {
        return new DefaultKafkaProducerFactory<>(postViewProducerConfigs());
    }

    @Bean
    public KafkaTemplate<String, PostViewCountDTO> postViewDTOKafkaTemplate() {
        return new KafkaTemplate<>(postViewCountDTOProducerFactory());
    }
}

Common JsonSerializer class

public class JsonSerializer {

    static Map<String, Object> getStringObjectMap(String bootstrapServer) {
        Map<String, Object> props = new HashMap<>();

        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServer);
        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, org.springframework.kafka.support.serializer.JsonSerializer.class);

        return props;
    }
}

consumer config

@Configuration
@RequiredArgsConstructor
public class PostViewConsumerConfig {

    @Value("${spring.kafka.consumer.bootstrap-servers}")
    private String bootstrapServer;

    @Bean
    public Map<String,Object> postViewConsumerConfigs() {
        return JsonDeserializer.getStringObjectMap(bootstrapServer);
    }

    @Bean
    public ConsumerFactory<String, PostViewCountDTO> postViewCountDTO_ConsumerFactory() {
        return new DefaultKafkaConsumerFactory<>(postViewConsumerConfigs());
    }

    @Bean
    public ConcurrentKafkaListenerContainerFactory<String, PostViewCountDTO> postViewCountListener() {
        ConcurrentKafkaListenerContainerFactory<String, PostViewCountDTO> factory = new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(postViewCountDTO_ConsumerFactory());
        return factory;
    }

    @Bean
    public StringJsonMessageConverter jsonConverter() {
        return new StringJsonMessageConverter();
    }
}

produce

    @Async
    public void sendPostNo(PostViewCountDTO postViewCountDTO) {
        postViewKafkaTemplate.send(topic_viewCount, null, postViewCountDTO);
    }

consume

    @KafkaListener(topics = topic_viewCount, groupId = groupId, containerFactory = "postViewCountListener")
    public void consume(@Payload PostViewCountDTO postViewCountDTO) {
        ...
    }

Upvotes: 3

Views: 8763

Answers (1)

Gary Russell
Gary Russell

Reputation: 174554

You need to add type mappings to the serializer and deserializer

https://docs.spring.io/spring-kafka/reference/kafka/serdes.html#serdes-mapping-types

On the producer side, map com.a.PostViewCountDTO to PostViewCountDTO. On the consumer side, map com.b.PostViewCountDTO to PostViewCountDTO.

Upvotes: 8

Related Questions