Skip to content

Repositories initialization logic not compatible with Kotlinx.serialization #966

Closed
@lamba92

Description

When creating a repository, the initialization logic goes for:

// stack
org.dizitart.no2.exceptions.ValidationException: Invalid repository type
    at org.dizitart.no2.common.util.ValidationUtils.validateRepositoryType(ValidationUtils.java:170)
    at org.dizitart.no2.repository.RepositoryFactory.createRepository(RepositoryFactory.java:139)
    at org.dizitart.no2.repository.RepositoryFactory.getRepository(RepositoryFactory.java:79)
    at org.dizitart.no2.NitriteDatabase.getRepository(NitriteDatabase.java:79)

What happens at validateRepositoryType#L160 is very peculiar. The function ObjectUtils#newInstance is invoked in an attempt to verify if an empty document can be deserialized into an object of the type of the repository as some kind of validation step. The "magic" happens in ObjectUtils#newInstance#L176.

I assume that this is fine for the Java world, where when deserializing, constructor signatures are rarely respected and reflections allow you to do whatever anyway. In the Kotlin world, this is not the case and Kotlinx.serialization is meant to avoid the use of reflection or weird Java-ish checks just like the one in ObjectUtils#newInstance#L176.

But why I am reporting this? The check mentioned above forced the KotlinxSerializationMapper to use the the empty document decoder EmptyDecoder with defaults on primitives to "fake that there is something in an empty document".

This workaround is fine most of the times, but when there are some checks on the primitives in the constructor properties of a class, things gets nasty. Look here for example:

@kotlinx.serialization.Serializable
data class CacheEntry(
  val sha256: String,
  val lastUpdated: kotlinx.datetime.Instant
)

following the workaround logic, this class will be instanciated with this code:

CacheEntry(
  sha256 = "",
  lastUpdated = kotlinx.datetime.Instant(java.time.Instant.parse("")) // simplified but that's the gimmick
)

db.getRepository< CacheEntry>("hashes") // KABOOOOM!

This will always fail because there is an attempt to create an object of type CacheEntry starting from an empty document here.

I would recommend to remove any attempt to instanciate objects from empty documents during repository creation and just let the decoder fail at the first deserialisation of a document.

This is a blocking issue for Package Search to update from v3.x to v4.x.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

Labels

Type

No type

Projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions