diff --git a/console-framework-client/pom.xml b/console-framework-client/pom.xml index ad2e573..bcbd53e 100644 --- a/console-framework-client/pom.xml +++ b/console-framework-client/pom.xml @@ -38,6 +38,27 @@ com.fasterxml.jackson.dataformat jackson-dataformat-cbor + provided + true + + + com.fasterxml.jackson.module + jackson-module-kotlin + + + tools.jackson.core + jackson-databind + 3.1.0 + + + tools.jackson.dataformat + jackson-dataformat-cbor + 3.1.0 + + + tools.jackson.module + jackson-module-kotlin + 3.1.0 diff --git a/console-framework-client/src/main/java/io/axoniq/console/framework/AxoniqConsoleConfigurerModule.java b/console-framework-client/src/main/java/io/axoniq/console/framework/AxoniqConsoleConfigurerModule.java index d08a125..716e74d 100644 --- a/console-framework-client/src/main/java/io/axoniq/console/framework/AxoniqConsoleConfigurerModule.java +++ b/console-framework-client/src/main/java/io/axoniq/console/framework/AxoniqConsoleConfigurerModule.java @@ -33,7 +33,8 @@ import io.axoniq.console.framework.client.RSocketHandlerRegistrar; import io.axoniq.console.framework.client.ServerProcessorReporter; import io.axoniq.console.framework.client.SetupPayloadCreator; -import io.axoniq.console.framework.client.strategy.CborEncodingStrategy; +import io.axoniq.console.framework.client.strategy.CborJackson2EncodingStrategy; +import io.axoniq.console.framework.client.strategy.CborJackson3EncodingStrategy; import io.axoniq.console.framework.client.strategy.RSocketPayloadEncodingStrategy; import io.axoniq.console.framework.eventprocessor.DeadLetterManager; import io.axoniq.console.framework.eventprocessor.EventProcessorManager; @@ -176,7 +177,7 @@ public void configureModule(@NotNull Configurer configurer) { ) ) .registerComponent(RSocketPayloadEncodingStrategy.class, - c -> new CborEncodingStrategy() + c -> createJackson2Or3EncodingStrategy() ) .registerComponent(RSocketHandlerRegistrar.class, c -> new RSocketHandlerRegistrar(c.getComponent(RSocketPayloadEncodingStrategy.class)) @@ -313,6 +314,54 @@ public void configureModule(@NotNull Configurer configurer) { new AxoniqConsoleEnhancingConfigurerModule(spanMatcherPredicateMap).configureModule(configurer); } + /** + * Checks the classpath for Jackson 2 or Jackson 3 and its requirements for this application. + * Will fail to create the component if neither is there, or if one is present and doesn't have the right modules. + */ + private static RSocketPayloadEncodingStrategy createJackson2Or3EncodingStrategy() { + try { + Class.forName("com.fasterxml.jackson.databind.ObjectMapper"); + try { + Class.forName( + "com.fasterxml.jackson.dataformat.cbor.databind.CBORMapper"); + try { + Class.forName( + "com.fasterxml.jackson.module.kotlin.KotlinModule"); + return new CborJackson2EncodingStrategy(); + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException( + "Found Jackson 2 on the classpath, but can not find the KotlinModule. Please add the com.fasterxml.jackson.module:jackson-module-kotlin dependency to your project"); + } + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException( + "Found Jackson 2 on the classpath, but cannot find the CBOR dataformat. Please add the com.fasterxml.jackson.dataformat:jackson-dataformat-cbor dependency to your project."); + } + } catch (ClassNotFoundException e) { + + } + + try { + Class.forName("tools.jackson.databind.ObjectMapper"); + try { + Class.forName("tools.jackson.dataformat.cbor.CBORMapper"); + try { + Class.forName("tools.jackson.module.kotlin.KotlinModule"); + return new CborJackson3EncodingStrategy(); + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException( + "Found Jackson 3 on the classpath, but can not find the KotlinModule. Please add the tools.jackson.module:jackson-module-kotlin dependency to your project"); + } + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException( + "Found Jackson 3 on the classpath, but cannot find the CBOR dataformat. Please add the tools.jackson.dataformat:jackson-dataformat-cbor dependency to your project."); + } + } catch (ClassNotFoundException e) { + // Do nothing, Jackson 3 is not on the classpath. Continue to check for 2 + throw new IllegalArgumentException( + "Neither Jackson 2 nor 3 was found on the classpath. Please add either Jackson 2 or 3 to your project."); + } + } + /** * Builder class to instantiate a {@link AxoniqConsoleConfigurerModule}. */ diff --git a/console-framework-client/src/main/java/io/axoniq/console/framework/client/strategy/CborEncodingStrategy.kt b/console-framework-client/src/main/java/io/axoniq/console/framework/client/strategy/CborJackson2EncodingStrategy.kt similarity index 96% rename from console-framework-client/src/main/java/io/axoniq/console/framework/client/strategy/CborEncodingStrategy.kt rename to console-framework-client/src/main/java/io/axoniq/console/framework/client/strategy/CborJackson2EncodingStrategy.kt index a19a47f..3c30225 100644 --- a/console-framework-client/src/main/java/io/axoniq/console/framework/client/strategy/CborEncodingStrategy.kt +++ b/console-framework-client/src/main/java/io/axoniq/console/framework/client/strategy/CborJackson2EncodingStrategy.kt @@ -25,7 +25,7 @@ import io.rsocket.Payload import io.rsocket.metadata.WellKnownMimeType import io.rsocket.util.DefaultPayload -class CborEncodingStrategy : RSocketPayloadEncodingStrategy { +class CborJackson2EncodingStrategy : RSocketPayloadEncodingStrategy { private val mapper = CBORMapper.builder().build().findAndRegisterModules() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) diff --git a/console-framework-client/src/main/java/io/axoniq/console/framework/client/strategy/CborJackson3EncodingStrategy.kt b/console-framework-client/src/main/java/io/axoniq/console/framework/client/strategy/CborJackson3EncodingStrategy.kt new file mode 100644 index 0000000..a9950c2 --- /dev/null +++ b/console-framework-client/src/main/java/io/axoniq/console/framework/client/strategy/CborJackson3EncodingStrategy.kt @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2022-2024. AxonIQ B.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.axoniq.console.framework.client.strategy + +import io.netty.buffer.ByteBuf +import io.netty.buffer.ByteBufAllocator +import io.netty.buffer.CompositeByteBuf +import io.rsocket.Payload +import io.rsocket.metadata.WellKnownMimeType +import io.rsocket.util.DefaultPayload +import tools.jackson.databind.DeserializationFeature +import tools.jackson.dataformat.cbor.CBORMapper + +class CborJackson3EncodingStrategy : RSocketPayloadEncodingStrategy { + private val mapper = CBORMapper.builder() + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .build() + + override fun getMimeType(): WellKnownMimeType { + return WellKnownMimeType.APPLICATION_CBOR + } + + override fun encode(payload: Any, metadata: ByteBuf?): Payload { + val payloadBuffer: CompositeByteBuf = ByteBufAllocator.DEFAULT.compositeBuffer() + payloadBuffer.writeBytes(mapper.writeValueAsBytes(payload)) + return DefaultPayload.create(payloadBuffer, metadata) + } + + override fun decode(payload: Payload, expectedType: Class): T { + if (expectedType == String::class.java) { + return payload.dataUtf8 as T + } + + return mapper.readValue(payload.data.array(), expectedType) + } +} diff --git a/pom.xml b/pom.xml index 8e945a2..158fc24 100644 --- a/pom.xml +++ b/pom.xml @@ -54,8 +54,8 @@ axoniq https://sonarcloud.io - 1.9.25 - 1.9.20 + 2.2.20 + 2.0.0 4.6.7