Skip to content

Commit

Permalink
Merge pull request #119 from holixon/feature/115_avro4k-2
Browse files Browse the repository at this point in the history
Feature/115 avro4k 2
  • Loading branch information
jangalinski authored Jul 30, 2024
2 parents ab96cf0 + 8e99363 commit 2a6bded
Show file tree
Hide file tree
Showing 25 changed files with 245 additions and 281 deletions.
5 changes: 1 addition & 4 deletions pom/parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@

<!-- libs (alphabetical) -->
<avro.version>1.11.3</avro.version>
<avro-kotlin.version>1.11.4.4</avro-kotlin.version>
<avro-kotlin.version>1.11.5.0</avro-kotlin.version>
<axon-bom.version>4.10.0</axon-bom.version>

</properties>

<modules>
Expand Down Expand Up @@ -102,8 +101,6 @@
<artifactId>kotlin-maven-plugin</artifactId>
<configuration>
<jvmTarget>${java.version}</jvmTarget>
<apiVersion>1.9</apiVersion>
<languageVersion>1.9</languageVersion>
<args>
<arg>-Xjsr305=strict</arg>
</args>
Expand Down
37 changes: 29 additions & 8 deletions serializer/core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@
<artifactId>axon-avro-serializer-core</artifactId>

<dependencies>
<dependency>
<groupId>io.toolisticon.kotlin.avro</groupId>
<artifactId>avro-kotlin-serialization</artifactId>
</dependency>

<dependency>
<groupId>org.axonframework</groupId>
<artifactId>axon-messaging</artifactId>
Expand Down Expand Up @@ -44,17 +39,43 @@
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<!-- kotlin compiler -->
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<configuration>
<jvmTarget>${java.version}</jvmTarget>
<args>
<arg>-Xjsr305=strict</arg>
</args>
<compilerPlugins>
<plugin>spring</plugin>
<plugin>jpa</plugin>
<plugin>no-arg</plugin>
<plugin>all-open</plugin>
<plugin>kotlinx-serialization</plugin>
</compilerPlugins>
<pluginOptions>
<option>all-open:annotation=org.axonframework.eventhandling.EventHandler</option>
<option>all-open:annotation=org.axonframework.queryhandling.QueryHandler</option>
</pluginOptions>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-serialization</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.avro</groupId>
<artifactId>avro-maven-plugin</artifactId>
</plugin>

</plugins>
</build>

</project>
47 changes: 14 additions & 33 deletions serializer/core/src/main/kotlin/AvroSerializer.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package io.holixon.axon.avro.serializer

import io.holixon.axon.avro.serializer.converter.*
import io.holixon.axon.avro.serializer.strategy.*
import io.holixon.axon.avro.serializer.strategy.InstanceResponseTypeStrategy
import io.holixon.axon.avro.serializer.strategy.MetaDataStrategy
import io.holixon.axon.avro.serializer.strategy.MultipleInstancesResponseTypeStrategy
import io.toolisticon.kotlin.avro.AvroKotlin
import io.toolisticon.kotlin.avro.repository.AvroSchemaResolver
import io.toolisticon.kotlin.avro.repository.AvroSchemaResolverMap
import io.toolisticon.kotlin.avro.repository.plus
import io.toolisticon.kotlin.avro.serialization.AvroKotlinSerialization
import io.toolisticon.kotlin.avro.serialization.strategy.GenericRecordSerializationStrategy
import io.toolisticon.kotlin.avro.serialization.strategy.KotlinxDataClassStrategy
import io.toolisticon.kotlin.avro.serialization.strategy.KotlinxEnumClassStrategy
import io.toolisticon.kotlin.avro.serialization.strategy.SpecificRecordBaseStrategy
import io.toolisticon.kotlin.avro.value.SingleObjectEncodedBytes
import mu.KLogging
import org.apache.avro.generic.GenericData
Expand All @@ -19,11 +23,9 @@ import java.util.function.Supplier
class AvroSerializer private constructor(
private val converter: Converter,
private val revisionResolver: RevisionResolver,
private val serializationStrategies: List<AvroSerializationStrategy>,
private val deserializationStrategies: List<AvroDeserializationStrategy>
private val serializationStrategies: List<GenericRecordSerializationStrategy>,
) : Serializer {


companion object : KLogging() {

private val axonSchemaResolver: AvroSchemaResolverMap = AvroSchemaResolverMap(
Expand All @@ -38,12 +40,7 @@ class AvroSerializer private constructor(
fun builder() = Builder()

operator fun invoke(builder: Builder): AvroSerializer {

val schemaResolver = if (builder.builderAvroSchemaResolver is AvroSchemaResolverMap) {
builder.builderAvroSchemaResolver + axonSchemaResolver
} else {
builder.builderAvroSchemaResolver + axonSchemaResolver
}
axonSchemaResolver.values.forEach(builder.avroKotlinSerialization::registerSchema)

val converter = if (builder.converter is ChainingConverter) {
logger.debug { "" }
Expand Down Expand Up @@ -71,7 +68,7 @@ class AvroSerializer private constructor(
registerConverter(ListRecordToSingleObjectEncodedConverter())

registerConverter(ByteArrayToSingleObjectEncodedConverter())
registerConverter(SingleObjectEncodedToGenericRecordConverter(schemaResolver))
registerConverter(SingleObjectEncodedToGenericRecordConverter(builder.avroKotlinSerialization))

// JSON handling in inverted order: GenericRecord -> String
registerConverter(JsonStringToStringConverter())
Expand Down Expand Up @@ -108,30 +105,17 @@ class AvroSerializer private constructor(
multipleInstancesResponseTypeStrategy,
specificRecordBaseStrategy
),
deserializationStrategies = listOf(
instanceResponseTypeStrategy,
kotlinxDataClassStrategy,
kotlinxEnumClassStrategy,
metaDataStrategy,
multipleInstancesResponseTypeStrategy,
specificRecordBaseStrategy
),
)
}
}

class Builder {
internal lateinit var builderAvroSchemaResolver: AvroSchemaResolver
internal var avroKotlinSerialization = AvroKotlinSerialization()
internal var converter: Converter = ChainingConverter()
internal var revisionResolver: RevisionResolver = AnnotationRevisionResolver()
internal val contentTypeConverters: MutableList<ContentTypeConverter<*, *>> = mutableListOf()
internal var avroKotlinSerialization = AvroKotlinSerialization()
internal var genericDataSupplier: Supplier<GenericData> = Supplier { AvroKotlin.genericData }

fun avroSchemaResolver(avroSchemaResolver: AvroSchemaResolver) = apply {
builderAvroSchemaResolver = avroSchemaResolver
}

fun addContentTypeConverter(contentTypeConverter: ContentTypeConverter<*, *>) = apply {
this.contentTypeConverters.add(contentTypeConverter)
}
Expand All @@ -145,19 +129,16 @@ class AvroSerializer private constructor(
}

fun build(): AvroSerializer {
require(this::builderAvroSchemaResolver.isInitialized) { "AvroSchemaResolver must be provided." }

return AvroSerializer(this)
}
}


override fun <T : Any> serialize(data: Any?, expectedRepresentation: Class<T>): SerializedObject<T> {
requireNotNull(data) {
"Can't serialize null."
}

val strategy = serializationStrategies.firstOrNull { it.canSerialize(data::class.java) }.also {
val strategy = serializationStrategies.firstOrNull { it.test(data::class) }.also {
if (it != null) {
logger.debug { "Using strategy ${it::class.java.name} for ${data::class.java}." }
} else {
Expand Down Expand Up @@ -186,7 +167,7 @@ class AvroSerializer private constructor(
override fun <S : Any, T : Any> deserialize(serializedObject: SerializedObject<S>): T {
val serializedType = classForType(serializedObject.type)

val strategy = deserializationStrategies.firstOrNull { it.canDeserialize(serializedType) }.also {
val strategy = serializationStrategies.firstOrNull { it.test(serializedType.kotlin) }.also {
if (it != null) {
logger.debug { "Using strategy ${it::class.java.name} for $serializedType." }
} else {
Expand All @@ -197,7 +178,7 @@ class AvroSerializer private constructor(
@Suppress("UNCHECKED_CAST", "IfThenToElvis")
return if (strategy != null) {
strategy.deserialize(
serializedType = serializedType,
serializedType = serializedType.kotlin,
data = converter.convert(serializedObject, GenericRecord::class.java).data
)
} else {
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,29 @@ package io.holixon.axon.avro.serializer.strategy
import _ktx.ResourceKtx
import io.toolisticon.kotlin.avro.AvroKotlin
import io.toolisticon.kotlin.avro.model.wrapper.AvroSchema
import io.toolisticon.kotlin.avro.serialization.strategy.GenericRecordSerializationStrategy
import io.toolisticon.kotlin.avro.value.Name.Companion.toName
import org.apache.avro.generic.GenericRecord
import org.apache.avro.util.Utf8
import org.axonframework.messaging.responsetypes.InstanceResponseType
import kotlin.reflect.KClass

@Suppress("UNCHECKED_CAST")
class InstanceResponseTypeStrategy() : AvroDeserializationStrategy, AvroSerializationStrategy {
class InstanceResponseTypeStrategy() : GenericRecordSerializationStrategy {
companion object {
val SCHEMA = AvroSchema.of(resource = ResourceKtx.resourceUrl("schema/AvroInstanceResponseType.avsc"))
const val FIELD = "expectedResponseType"
val FIELD_SCHEMA = SCHEMA.getField(FIELD.toName())!!.schema
}

override fun test(serializedType: KClass<*>): Boolean = InstanceResponseType::class.java == serializedType

override fun canDeserialize(serializedType: Class<*>): Boolean = InstanceResponseType::class.java == serializedType

override fun <T : Any> deserialize(serializedType: Class<*>, data: GenericRecord): T {
override fun <T : Any> deserialize(serializedType: KClass<*>, data: GenericRecord): T {
val className = data.get(FIELD) as Utf8
return InstanceResponseType(Class.forName(className.toString())) as T
}

override fun canSerialize(serializedType: Class<*>): Boolean = InstanceResponseType::class.java == serializedType

override fun serialize(data: Any): GenericRecord {
override fun <T:Any> serialize(data: T): GenericRecord {
require(data is InstanceResponseType<*>)
return AvroKotlin.createGenericRecord(SCHEMA) {
put(FIELD, data.expectedResponseType.canonicalName)
Expand Down

This file was deleted.

This file was deleted.

12 changes: 6 additions & 6 deletions serializer/core/src/main/kotlin/strategy/MetaDataStrategy.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,30 @@ package io.holixon.axon.avro.serializer.strategy
import _ktx.ResourceKtx
import io.toolisticon.kotlin.avro.AvroKotlin
import io.toolisticon.kotlin.avro.model.wrapper.AvroSchema
import io.toolisticon.kotlin.avro.serialization.strategy.GenericRecordSerializationStrategy
import io.toolisticon.kotlin.avro.value.Name.Companion.toName
import org.apache.avro.generic.GenericData
import org.apache.avro.generic.GenericRecord
import org.axonframework.messaging.MetaData
import kotlin.reflect.KClass

class MetaDataStrategy(
private val genericData: GenericData
) : AvroSerializationStrategy, AvroDeserializationStrategy {
) : GenericRecordSerializationStrategy {
companion object {
val SCHEMA = AvroSchema.of(resource = ResourceKtx.resourceUrl("schema/AvroMetaData.avsc"))
const val FIELD_VALUES = "values"
val SCHEMA_VALUES = SCHEMA.getField(FIELD_VALUES.toName())!!.schema
}

override fun canDeserialize(serializedType: Class<*>): Boolean = MetaData::class.java == serializedType
override fun test(serializedType: KClass<*>): Boolean = MetaData::class.java == serializedType

@Suppress("UNCHECKED_CAST")
override fun <T : Any> deserialize(serializedType: Class<*>, data: GenericRecord): T {
override fun <T : Any> deserialize(serializedType: KClass<*>, data: GenericRecord): T {
return MetaData.from(data.get(FIELD_VALUES) as Map<String, *>) as T
}

override fun canSerialize(serializedType: Class<*>): Boolean = MetaData::class.java == serializedType

override fun serialize(data: Any): GenericRecord {
override fun <T : Any> serialize(data: T): GenericRecord {
require(isSchemaCompliant(data)) { "Data: $data not compliant with schema=$SCHEMA" }
return AvroKotlin.createGenericRecord(SCHEMA) {
put(FIELD_VALUES, data)
Expand Down
Loading

0 comments on commit 2a6bded

Please sign in to comment.