[JAVA-3040] CodecConfigurationException with subtype specialyzed getter Created: 08/Oct/18  Updated: 28/Oct/23  Resolved: 22/Nov/18

Status: Closed
Project: Java Driver
Component/s: BSON, Codecs
Affects Version/s: 3.8.2
Fix Version/s: 3.9.1, 3.10.0

Type: Bug Priority: Major - P3
Reporter: Clécius J. Martinkoski Assignee: Ross Lawley
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Related
is related to JAVA-3097 Create Convention that customises whi... Backlog

 Description   

When i'm using automatic PojoCodec with Enterprise class that extends from Person class and specilyzes it getDocuments to return a subtype i got that error:

org.bson.codecs.pojo - Cannot use 'Enterprise' with the PojoCodec.org.bson.codecs.pojo - Cannot use 'Enterprise' with the PojoCodec.org.bson.codecs.configuration.CodecConfigurationException: Property 'documents' in Enterprise, has differing data types: TypeData{type=LegalEntityDocuments} and TypeData{type=Documents} at org.bson.codecs.pojo.PojoBuilderHelper.getOrCreateMethodPropertyMetadata(PojoBuilderHelper.java:161) at org.bson.codecs.pojo.PojoBuilderHelper.configureClassModelBuilder(PojoBuilderHelper.java:73) at org.bson.codecs.pojo.ClassModelBuilder.<init>(ClassModelBuilder.java:59) at org.bson.codecs.pojo.ClassModel.builder(ClassModel.java:63) at org.bson.codecs.pojo.PojoCodecProvider.createClassModel(PojoCodecProvider.java:215) at org.bson.codecs.pojo.PojoCodecProvider.getPojoCodec(PojoCodecProvider.java:82) at org.bson.codecs.pojo.PojoCodecProvider.get(PojoCodecProvider.java:72) at eprecise.libs.arch.persistence.async.kmongo.db.cache.CustomPojoCodecProvider.get(CustomPojoCodecProvider.kt:27) at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:43) at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:55) at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:37) at com.mongodb.internal.operation.Operations.createFindOperation(Operations.java:140) at com.mongodb.internal.operation.Operations.find(Operations.java:129) at com.mongodb.internal.operation.AsyncOperations.find(AsyncOperations.java:84) at com.mongodb.async.client.FindIterableImpl.createFindOperation(FindIterableImpl.java:228) at com.mongodb.async.client.FindIterableImpl.asAsyncReadOperation(FindIterableImpl.java:224) at com.mongodb.async.client.MongoIterableImpl.batchCursor(MongoIterableImpl.java:161) at com.mongodb.async.client.MongoIterableImpl.forEach(MongoIterableImpl.java:77) at eprecise.libs.arch.persistence.async.kmongo.MongoExtensionsKt$toChannel$2.doResume(MongoExtensions.kt:36)

  Investigating the problem, i've reached this line at org.bson.codecs.pojo.PojoBuilderHelper:

private static <T, S> PropertyMetadata<T> getOrCreateMethodPropertyMetadata(final String propertyName,
                                                              final String declaringClassName,
                                                              final Map<String, PropertyMetadata<?>> propertyNameMap,
                                                              final TypeData<T> typeData,
                                                              final Map<String, TypeParameterMap> propertyTypeParameterMap,
                                                              final TypeData<S> parentClassTypeData,
                                                              final List<String> genericTypeNames,
                                                              final Type genericType) {
    PropertyMetadata<T> propertyMetadata = getOrCreatePropertyMetadata(propertyName, declaringClassName, propertyNameMap, typeData);
    if (!propertyMetadata.getTypeData().getType().isAssignableFrom(typeData.getType())) {
        throw new CodecConfigurationException(format("Property '%s' in %s, has differing data types: %s and %s", propertyName,
                declaringClassName, propertyMetadata.getTypeData(), typeData));
    }
    cachePropertyTypeData(propertyMetadata, propertyTypeParameterMap, parentClassTypeData, genericTypeNames,
            genericType);
    return propertyMetadata;
}

This seems a bug to me.

In fact, what I want is a mode to parametrize or extends the PojoCodecProvider to not scan methods, and only scan fields. 

 



 Comments   
Comment by Githook User [ 14/Jan/19 ]

Author:

{'username': 'rozza', 'email': 'ross.lawley@gmail.com', 'name': 'Ross Lawley'}

Message: POJOCodec Ensure properties can be overridden by explicit types

JAVA-3040
Branch: 3.10.x
https://github.com/mongodb/mongo-java-driver/commit/3e1be295ab6d892ff218d7a6b482eece74c32681

Comment by Ross Lawley [ 22/Nov/18 ]

eprecise-clecius the fix is available in the latest 3.9.1 SNAPSHOT in sonatype:

https://oss.sonatype.org/content/repositories/snapshots/org/mongodb/mongodb-driver-async/3.9.1-SNAPSHOT/

Comment by Githook User [ 21/Nov/18 ]

Author:

{'name': 'Ross Lawley', 'email': 'ross.lawley@gmail.com', 'username': 'rozza'}

Message: POJOCodec Ensure properties can be overridden by explicit types

JAVA-3040
Branch: 3.9.x
https://github.com/mongodb/mongo-java-driver/commit/21dbd43d1c0b7981dedf927608824d9b29653a66

Comment by Githook User [ 21/Nov/18 ]

Author:

{'name': 'Ross Lawley', 'email': 'ross.lawley@gmail.com', 'username': 'rozza'}

Message: POJOCodec Ensure properties can be overridden by explicit types

JAVA-3040
Branch: master
https://github.com/mongodb/mongo-java-driver/commit/90c9e3fd78070f623c06849b1e9086cfbd977aa6

Comment by Ross Lawley [ 19/Nov/18 ]

A fix is in code review, to handle the exception. 

I've also added JAVA-3097 for looking into adding an extra convention to handle different strategies regarding which properties to serialize / deserialize in a POJO.  Please watch that ticket for updates on the convention.

Comment by Clécius J. Martinkoski [ 12/Nov/18 ]

Hi Ross,

 

Thanks, after this was corrected you can provide snapshot link with this?

 

About Jackson configuration there is 2 modes to do this configuration, first by annotations:

 @JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)

And second by global object mapper config:

ObjectMapper mapper  = new ObjectMapper();
mapper.setVisibility(IS_GETTER, NONE)
        .setVisibility(GETTER, NONE)
        .setVisibility(SETTER, NONE)
        .setVisibility(FIELD, ANY)

The second is my default configuration, and will be perfect to my persistent domain.

 

To contextualize, my domain was originally persisted with Morphia, but now i'm migrating to PojoCodec to use async driver capabilities.

 

Comment by Ross Lawley [ 09/Nov/18 ]

Apologies eprecise-clecius I didn't realise the exception was thrown before the conventions were applied - that definitely can be fixed for the next release.

Out of interest do you have a link or an example of configuring Jackson to ignore property methods?

Comment by Clécius J. Martinkoski [ 07/Nov/18 ]

Hi Ross,

As you can see in stacktrace (same provided in open of this case):

org.bson.codecs.configuration.CodecConfigurationException: Property 'documents' in Enterprise, has differing data types: TypeData{type=LegalEntityDocuments} and TypeData{type=Documents}org.bson.codecs.configuration.CodecConfigurationException: Property 'documents' in Enterprise, has differing data types: TypeData{type=LegalEntityDocuments} and TypeData{type=Documents} at org.bson.codecs.pojo.PojoBuilderHelper.getOrCreateMethodPropertyMetadata(PojoBuilderHelper.java:161) at org.bson.codecs.pojo.PojoBuilderHelper.configureClassModelBuilder(PojoBuilderHelper.java:73) at org.bson.codecs.pojo.ClassModelBuilder.<init>(ClassModelBuilder.java:59) at org.bson.codecs.pojo.ClassModel.builder(ClassModel.java:63) at org.bson.codecs.pojo.PojoCodecProvider.createClassModel(PojoCodecProvider.java:215)

The line 215 in last stacktrace line is before convention apply:

private static <T> ClassModel<T> createClassModel(final Class<T> clazz, final List<Convention> conventions) {
    ClassModelBuilder<T> builder = ClassModel.builder(clazz); // line 215
    if (conventions != null) {
        builder.conventions(conventions);
    }
    return builder.build();
}

Then I cannot apply a covention to solve that.

I've really tried solutions that no change driver, but the only that i've seed is rewrite PojoCodec to ignore methods and scan only fields, this can be solutionated with a single configuration.

And then this configuration can be helpful in situations like my domain when the persistent state is only fields, then scanning methos is only to reach bugs and consumes CPU.

 

 

Comment by Ross Lawley [ 07/Nov/18 ]

Hi eprecise-clecius,

A convention allows you to configure the PojoCodec during the build process so you can customized as needs be. Conventions are applied to any automatically created PojoCodecs. Alternatively, using the BsonIgnore annotation to ignore any properties is an alternative solution.

I'm hoping you can use them to solve the issue, without the need for a change to the driver.

Ross

Comment by Clécius J. Martinkoski [ 24/Oct/18 ]

One key feature to solve that and another problems that i see is an configuration to allow ignore getters and setters and scan only fields, like Jackson configuration.

Comment by Clécius J. Martinkoski [ 24/Oct/18 ]

Hi Ross,

As you can see in first comment, the problem is a exception during automatic POJO Coded Generation, then this class Codec isn't generated to be changed by convention.

If this only ignore this property or allow me to intercept codec generation to customize this, it'll be fine do solve.

Another solution is allow extensibility ou reusability by my software of internal classes to pojo codec generation.

 

Comment by Ross Lawley [ 23/Oct/18 ]

Hi eprecise-clecius,

Thanks for the update, currently we require POJOs to follow the java bean standards, in a similar way to http://json-b.net/. Perhaps a custom convention can be used to remove properties you don't want to serialize would help?

Ross 

Comment by Clécius J. Martinkoski [ 16/Oct/18 ]

@Ross Lawley this datas are sufficient?

Comment by Clécius J. Martinkoski [ 09/Oct/18 ]

Hi @ross.lawley,

This problem could be resolved with a parameter to instruct PojoCodecProvider in automatic mode to scan only fields and ignore methods, that will be usefull in many other cases in my domain.

I'm migrating my persistence layer from Morphia to MongoDriver, that is the cause of this problems, in Morphia my methods are irrelevant for persistence.

About this specific problem:

As explained in the body case, the problem is with a specialized getter method.

Here is the Enterprise Class:

 

public class Enterprise extends Person implements DomainEntity, Tenant, StockLocation, FiscalDocumentEmitter {
...
public Enterprise() {
    addAllNewFunctionalities();
    switchToLegalEntity();
}
 
@Override
public LegalEntityDocuments getDocuments() {
    return (LegalEntityDocuments) super.getDocuments();
}
@AssertTrue
@Override
public boolean isLegalEntity() {
    return super.isLegalEntity();
}
@Override
public void switchToNaturalPerson() {
    throw new UnsupportedOperationException("Uma empresa somente pode ser uma pessoa jurídica");
}
...
}

And here is the Person class:

public class Person implements ActivableDomainEntity, Serializable {
...
private @NotNull @Valid Documents documents = new NaturalPersonDocuments();
 
public Documents getDocuments() {
    return this.documents;
}
 
@SuppressWarnings("unchecked")
public <T extends Documents> T getDocuments(final Class<T> type) {
    return (T) this.documents;
}
 
public void switchToNaturalPerson() {
    this.documents = new NaturalPersonDocuments();
}
...
}

 

I've removed the irrelevant part to focus on the problem source.

If this isn't sufficient to simulate the problem, please ask me again.

 

Comment by Ross Lawley [ 09/Oct/18 ]

Hi eprecise-clecius

Thanks for the ticket, the PojoCodec uses reflection to specialise generic types. Here it looks like an unexpected type was encountered for a POJO and it threw an error as it wouldn't be able to cast the type as expected.

The PojoCodecProvider has been designed to be flexible - so there may be a way to achieve your goal. Perhaps providing a test case to reproduce the error would provide better insight to the issue and perhaps how to solve it.

Ross

Generated at Thu Feb 08 08:58:38 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.