From 23b05832e108bf40e91d8b06b5c588c63017465d Mon Sep 17 00:00:00 2001 From: Daniel Merchant Date: Wed, 22 May 2024 21:45:54 -0400 Subject: [PATCH] Prefer to load custom property codec providers before default codec providers. (#3038) * Prefer to load custom property codec providers before default codec providers. --------- Co-authored-by: Justin Lee --- .../mapping/codec/MorphiaCodecProvider.java | 7 +-- .../codec/MorphiaCodecProviderTest.java | 29 +++++++++++++ .../NoOpMorphiaPropertyCodecProvider.java | 43 +++++++++++++++++++ ...mapping.codec.MorphiaPropertyCodecProvider | 1 + 4 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 core/src/test/java/dev/morphia/mapping/codec/MorphiaCodecProviderTest.java create mode 100644 core/src/test/java/dev/morphia/mapping/codec/NoOpMorphiaPropertyCodecProvider.java create mode 100644 core/src/test/resources/META-INF/services/dev.morphia.mapping.codec.MorphiaPropertyCodecProvider diff --git a/core/src/main/java/dev/morphia/mapping/codec/MorphiaCodecProvider.java b/core/src/main/java/dev/morphia/mapping/codec/MorphiaCodecProvider.java index 5c2d0cce3cb..4c02af5f766 100644 --- a/core/src/main/java/dev/morphia/mapping/codec/MorphiaCodecProvider.java +++ b/core/src/main/java/dev/morphia/mapping/codec/MorphiaCodecProvider.java @@ -48,13 +48,14 @@ public MorphiaCodecProvider(Datastore datastore) { this.datastore = datastore; this.mapper = datastore.getMapper(); - propertyCodecProviders.addAll(List.of(new MorphiaMapPropertyCodecProvider(), - new MorphiaCollectionPropertyCodecProvider())); - + // Load user-provided custom codecs first, to prevent the defaults from overriding them. ServiceLoader providers = ServiceLoader.load(MorphiaPropertyCodecProvider.class); providers.forEach(provider -> { propertyCodecProviders.add(provider); }); + + propertyCodecProviders.addAll(List.of(new MorphiaMapPropertyCodecProvider(), + new MorphiaCollectionPropertyCodecProvider())); } protected Map, Codec> getCodecs() { diff --git a/core/src/test/java/dev/morphia/mapping/codec/MorphiaCodecProviderTest.java b/core/src/test/java/dev/morphia/mapping/codec/MorphiaCodecProviderTest.java new file mode 100644 index 00000000000..af07a437fa0 --- /dev/null +++ b/core/src/test/java/dev/morphia/mapping/codec/MorphiaCodecProviderTest.java @@ -0,0 +1,29 @@ +package dev.morphia.mapping.codec; + +import java.util.List; + +import dev.morphia.Datastore; +import dev.morphia.test.TestBase; + +import org.bson.codecs.pojo.PropertyCodecProvider; +import org.testng.annotations.Test; + +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertTrue; + +public class MorphiaCodecProviderTest extends TestBase { + + @Test + public void ensureCustomCodedProvidersComeFirst() { + // given a custom 'no-op' codec is provided in the service configuration + // (see META-INF/services/dev.morphia.mapping.codec.MorphiaPropertyCodecProvider) + // when we instantiate a morphia codec provider + Datastore datastore = getDs(); + MorphiaCodecProvider provider = new MorphiaCodecProvider(datastore); + + // then we expect that the custom provider we provided is the first codec in the list + List providers = provider.getPropertyCodecProviders(); + assertEquals(providers.size(), 3); + assertTrue(providers.get(0) instanceof NoOpMorphiaPropertyCodecProvider); + } +} diff --git a/core/src/test/java/dev/morphia/mapping/codec/NoOpMorphiaPropertyCodecProvider.java b/core/src/test/java/dev/morphia/mapping/codec/NoOpMorphiaPropertyCodecProvider.java new file mode 100644 index 00000000000..aad50354224 --- /dev/null +++ b/core/src/test/java/dev/morphia/mapping/codec/NoOpMorphiaPropertyCodecProvider.java @@ -0,0 +1,43 @@ +package dev.morphia.mapping.codec; + +import org.bson.BsonReader; +import org.bson.BsonWriter; +import org.bson.codecs.Codec; +import org.bson.codecs.DecoderContext; +import org.bson.codecs.EncoderContext; +import org.bson.codecs.pojo.PropertyCodecRegistry; +import org.bson.codecs.pojo.TypeWithTypeParameters; + +public class NoOpMorphiaPropertyCodecProvider extends MorphiaPropertyCodecProvider { + + @Override + public Codec get(TypeWithTypeParameters typeWithTypeParameters, + PropertyCodecRegistry propertyCodecRegistry) { + if (NoOpClass.class.isAssignableFrom(typeWithTypeParameters.getType())) { + return (Codec) new NoOpCodec(); + } + + return null; + } + + static class NoOpCodec implements Codec { + + @Override + public NoOpClass decode(BsonReader bsonReader, DecoderContext decoderContext) { + return null; + } + + @Override + public void encode(BsonWriter bsonWriter, NoOpClass unused, EncoderContext encoderContext) { + + } + + @Override + public Class getEncoderClass() { + return NoOpClass.class; + } + } + + static class NoOpClass { + } +} diff --git a/core/src/test/resources/META-INF/services/dev.morphia.mapping.codec.MorphiaPropertyCodecProvider b/core/src/test/resources/META-INF/services/dev.morphia.mapping.codec.MorphiaPropertyCodecProvider new file mode 100644 index 00000000000..318831118cf --- /dev/null +++ b/core/src/test/resources/META-INF/services/dev.morphia.mapping.codec.MorphiaPropertyCodecProvider @@ -0,0 +1 @@ +dev.morphia.mapping.codec.NoOpMorphiaPropertyCodecProvider \ No newline at end of file