Search code examples
javamongodbmicronaut

Registering codecs for MongoDB using Micronaut


I'm in the process of creating a new application using Micronaut and MongoDB. I have to a bit of stalemate regarding some of my db objects.

I have an object that contains an enum field which I need to convert over to a value that can be saved (I'm planning to persist it as a string).

From what I understand and from the error message I'm getting, I need to create a new codec to handle this (similar to JPA converters). While I have found examples showing this, I'm a bit confused as to how I'm supposed to register the converter for the MongoClient.

I'm using the latest Micronaut version of both the framework and the respective MongoClient.

Below is the exception I'm getting for reference:

org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class fts.marketing.models.CampaignEmailStatus.
    at org.bson.codecs.configuration.CodecCache.getOrThrow(CodecCache.java:46)
    at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:63)
    at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:37)
    at com.mongodb.client.model.BuildersHelper.encodeValue(BuildersHelper.java:37)
    at com.mongodb.client.model.Filters$SimpleEncodingFilter.toBsonDocument(Filters.java:1109)
    at com.mongodb.client.model.Filters$AndFilter.toBsonDocument(Filters.java:946)
    at com.mongodb.internal.operation.Operations.createFindOperation(Operations.java:142)
    at com.mongodb.internal.operation.Operations.find(Operations.java:130)
    at com.mongodb.internal.operation.AsyncOperations.find(AsyncOperations.java:85)
    at com.mongodb.async.client.FindIterableImpl.createFindOperation(FindIterableImpl.java:229)
    at com.mongodb.async.client.FindIterableImpl.asAsyncReadOperation(FindIterableImpl.java:225)
    at com.mongodb.async.client.MongoIterableImpl.batchCursor(MongoIterableImpl.java:161)
    at com.mongodb.async.client.MongoIterableSubscription.requestInitialData(MongoIterableSubscription.java:46)
    at com.mongodb.async.client.AbstractSubscription.tryRequestInitialData(AbstractSubscription.java:151)
    at com.mongodb.async.client.AbstractSubscription.request(AbstractSubscription.java:84)
    at com.mongodb.reactivestreams.client.internal.ObservableToPublisher$1$1.request(ObservableToPublisher.java:50)
    at io.reactivex.internal.operators.flowable.FlowableToListSingle$ToListSubscriber.onSubscribe(FlowableToListSingle.java:84)
    at com.mongodb.reactivestreams.client.internal.ObservableToPublisher$1.onSubscribe(ObservableToPublisher.java:39)
    at com.mongodb.async.client.MongoIterableSubscription.<init>(MongoIterableSubscription.java:40)
    at com.mongodb.async.client.Observables$1.subscribe(Observables.java:47)
    at com.mongodb.reactivestreams.client.internal.ObservableToPublisher.subscribe(ObservableToPublisher.java:36)
    at com.mongodb.reactivestreams.client.internal.FindPublisherImpl.subscribe(FindPublisherImpl.java:189)

Solution

  • I think it should be enough to create a class with the codec and have it injectable:

    @Singleton
    public class EnumCodec implements Codec<CampaignEmailStatus> {
    
      @Override
      public CampaignEmailStatus decode(BsonReader reader, DecoderContext decoderContext) {
        String enumString = reader.readString();
        return CampaignEmailStatus.valueOf(enumString);
      }
    
      @Override
      public void encode(BsonWriter writer, CampaignEmailStatus value, EncoderContext encoderContext) {
        String enumString = value.name();
        writer.writeString(enumString);
      }
    
      @Override
      public Class<CampaignEmailStatus> getEncoderClass() {
        return CampaignEmailStatus.class;
      }
    }
    

    The Mongo driver docs explaining Codecs are here: https://mongodb.github.io/mongo-java-driver/3.11/bson/codecs/

    The injection of the codecs happens here

    io.micronaut.configuration.mongo.reactive.DefaultReactiveMongoConfiguration#codecs
    

    You should be able to debug it from there if it's not working

    PS: I did not test the code above but it should give you an idea