From 7a7ca8edada88c32e26fd62a34803ad5502f49bb Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Thu, 5 Sep 2024 18:58:48 +0200 Subject: [PATCH] added configuration parameter serdeProviderClass --- core/src/main/java/com/arangodb/ArangoDB.java | 13 +++ .../config/ArangoConfigProperties.java | 2 +- .../internal/config/ArangoConfig.java | 32 +++++++- .../config/ArangoConfigPropertiesImpl.java | 5 ++ .../java/com/arangodb/ArangoConfigTest.java | 1 + .../resources/arangodb-config-test.properties | 19 ----- .../java/mp/ArangoConfigPropertiesMPImpl.java | 13 ++- .../src/test/java/mp/ConfigMPTest.java | 2 + .../java/serde/SerdeConfigurationTest.java | 81 +++++++++++++++++++ .../resources/arangodb-config-test.properties | 1 + .../arangodb-serde-provider.properties | 3 + 11 files changed, 146 insertions(+), 26 deletions(-) delete mode 100644 test-functional/src/test/resources/arangodb-config-test.properties create mode 100644 test-non-functional/src/test/java/serde/SerdeConfigurationTest.java create mode 100644 test-non-functional/src/test/resources/arangodb-serde-provider.properties diff --git a/core/src/main/java/com/arangodb/ArangoDB.java b/core/src/main/java/com/arangodb/ArangoDB.java index 63e62e29b..ea822d89e 100644 --- a/core/src/main/java/com/arangodb/ArangoDB.java +++ b/core/src/main/java/com/arangodb/ArangoDB.java @@ -32,6 +32,7 @@ import com.arangodb.internal.util.HostUtils; import com.arangodb.model.*; import com.arangodb.serde.ArangoSerde; +import com.arangodb.serde.ArangoSerdeProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -636,6 +637,18 @@ public Builder serde(final ArangoSerde serde) { return this; } + /** + * Sets the serde provider to be used to instantiate the user data serde. + * Ignored if {@link Builder#serde(ArangoSerde)} is used. + * + * @param serdeProviderClass class of the serde provider, it must have a public no-args constructor + * @return {@link ArangoDB.Builder} + */ + public Builder serdeProviderClass(final Class serdeProviderClass) { + config.setUserDataSerdeProvider(serdeProviderClass); + return this; + } + /** * Sets the downstream async executor that will be used to consume the responses of the async API, that are returned * as {@link java.util.concurrent.CompletableFuture} diff --git a/core/src/main/java/com/arangodb/config/ArangoConfigProperties.java b/core/src/main/java/com/arangodb/config/ArangoConfigProperties.java index 9690e5f4e..774b3c31d 100644 --- a/core/src/main/java/com/arangodb/config/ArangoConfigProperties.java +++ b/core/src/main/java/com/arangodb/config/ArangoConfigProperties.java @@ -110,7 +110,7 @@ default Optional getCompressionLevel() { return Optional.empty(); } - default Optional getReuseVertx() { + default Optional getSerdeProviderClass() { return Optional.empty(); } diff --git a/core/src/main/java/com/arangodb/internal/config/ArangoConfig.java b/core/src/main/java/com/arangodb/internal/config/ArangoConfig.java index 0528bb166..f38903532 100644 --- a/core/src/main/java/com/arangodb/internal/config/ArangoConfig.java +++ b/core/src/main/java/com/arangodb/internal/config/ArangoConfig.java @@ -16,6 +16,7 @@ import com.fasterxml.jackson.databind.Module; import javax.net.ssl.SSLContext; +import java.lang.reflect.InvocationTargetException; import java.util.*; import java.util.concurrent.Executor; import java.util.stream.Collectors; @@ -40,6 +41,7 @@ public class ArangoConfig { private LoadBalancingStrategy loadBalancingStrategy; private InternalSerde internalSerde; private ArangoSerde userDataSerde; + private Class serdeProviderClass; private Integer responseQueueTimeSamples; private Module protocolModule; private Executor asyncExecutor; @@ -81,6 +83,14 @@ public void loadProperties(final ArangoConfigProperties properties) { compression = properties.getCompression().orElse(ArangoDefaults.DEFAULT_COMPRESSION); compressionThreshold = properties.getCompressionThreshold().orElse(ArangoDefaults.DEFAULT_COMPRESSION_THRESHOLD); compressionLevel = properties.getCompressionLevel().orElse(ArangoDefaults.DEFAULT_COMPRESSION_LEVEL); + serdeProviderClass = properties.getSerdeProviderClass().map((String className) -> { + try { + //noinspection unchecked + return (Class) Class.forName(className); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + }).orElse(null); } public List getHosts() { @@ -237,11 +247,23 @@ public void setLoadBalancingStrategy(LoadBalancingStrategy loadBalancingStrategy this.loadBalancingStrategy = loadBalancingStrategy; } + public Class getSerdeProviderClass() { + return serdeProviderClass; + } + public ArangoSerde getUserDataSerde() { - if (userDataSerde == null) { - userDataSerde = ArangoSerdeProvider.of(ContentTypeFactory.of(getProtocol())).create(); + if (userDataSerde != null) { + return userDataSerde; + } else if (serdeProviderClass != null) { + try { + return serdeProviderClass.getDeclaredConstructor().newInstance().create(); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + NoSuchMethodException e) { + throw new RuntimeException(e); + } + } else { + return ArangoSerdeProvider.of(ContentTypeFactory.of(getProtocol())).create(); } - return userDataSerde; } public InternalSerde getInternalSerde() { @@ -255,6 +277,10 @@ public void setUserDataSerde(ArangoSerde userDataSerde) { this.userDataSerde = userDataSerde; } + public void setUserDataSerdeProvider(Class serdeProviderClass) { + this.serdeProviderClass = serdeProviderClass; + } + public Integer getResponseQueueTimeSamples() { return responseQueueTimeSamples; } diff --git a/core/src/main/java/com/arangodb/internal/config/ArangoConfigPropertiesImpl.java b/core/src/main/java/com/arangodb/internal/config/ArangoConfigPropertiesImpl.java index 279fb2ba0..614119ed3 100644 --- a/core/src/main/java/com/arangodb/internal/config/ArangoConfigPropertiesImpl.java +++ b/core/src/main/java/com/arangodb/internal/config/ArangoConfigPropertiesImpl.java @@ -161,4 +161,9 @@ public Optional getCompressionLevel() { return Optional.ofNullable(getProperty("compressionLevel")).map(Integer::valueOf); } + @Override + public Optional getSerdeProviderClass() { + return Optional.ofNullable(getProperty("serdeProviderClass")); + } + } diff --git a/test-functional/src/test/java/com/arangodb/ArangoConfigTest.java b/test-functional/src/test/java/com/arangodb/ArangoConfigTest.java index b0acf1181..2e00339a0 100644 --- a/test-functional/src/test/java/com/arangodb/ArangoConfigTest.java +++ b/test-functional/src/test/java/com/arangodb/ArangoConfigTest.java @@ -33,6 +33,7 @@ void ArangoConfigDefaultValues() { assertThat(cfg.getCompressionThreshold()).isEqualTo(ArangoDefaults.DEFAULT_COMPRESSION_THRESHOLD); assertThat(cfg.getCompressionLevel()).isEqualTo(ArangoDefaults.DEFAULT_COMPRESSION_LEVEL); assertThat(cfg.getProtocolConfig()).isNull(); + assertThat(cfg.getSerdeProviderClass()).isNull(); } @Test diff --git a/test-functional/src/test/resources/arangodb-config-test.properties b/test-functional/src/test/resources/arangodb-config-test.properties deleted file mode 100644 index e93668c93..000000000 --- a/test-functional/src/test/resources/arangodb-config-test.properties +++ /dev/null @@ -1,19 +0,0 @@ -adb.hosts=aaa:1111,bbb:2222 -adb.protocol=HTTP_VPACK -adb.user=testUser -adb.password=testPassword -adb.jwt=testJwt -adb.timeout=9876 -adb.useSsl=true -adb.verifyHost=false -adb.chunkSize=1234 -adb.maxConnections=123 -adb.connectionTtl=12345 -adb.keepAliveInterval=123456 -adb.acquireHostList=true -adb.acquireHostListInterval=1234567 -adb.loadBalancingStrategy=ROUND_ROBIN -adb.responseQueueTimeSamples=12345678 -adb.compression=GZIP -adb.compressionThreshold=123456789 -adb.compressionLevel=9 diff --git a/test-non-functional/src/test/java/mp/ArangoConfigPropertiesMPImpl.java b/test-non-functional/src/test/java/mp/ArangoConfigPropertiesMPImpl.java index 3f4120e12..1a0407a4c 100644 --- a/test-non-functional/src/test/java/mp/ArangoConfigPropertiesMPImpl.java +++ b/test-non-functional/src/test/java/mp/ArangoConfigPropertiesMPImpl.java @@ -33,6 +33,7 @@ public final class ArangoConfigPropertiesMPImpl implements ArangoConfigPropertie private Optional compression; private Optional compressionThreshold; private Optional compressionLevel; + private Optional serdeProviderClass; @Override public Optional> getHosts() { @@ -129,22 +130,27 @@ public Optional getCompressionLevel() { return compressionLevel; } + @Override + public Optional getSerdeProviderClass() { + return serdeProviderClass; + } + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ArangoConfigPropertiesMPImpl that = (ArangoConfigPropertiesMPImpl) o; - return Objects.equals(hosts, that.hosts) && Objects.equals(protocol, that.protocol) && Objects.equals(user, that.user) && Objects.equals(password, that.password) && Objects.equals(jwt, that.jwt) && Objects.equals(timeout, that.timeout) && Objects.equals(useSsl, that.useSsl) && Objects.equals(verifyHost, that.verifyHost) && Objects.equals(chunkSize, that.chunkSize) && Objects.equals(maxConnections, that.maxConnections) && Objects.equals(connectionTtl, that.connectionTtl) && Objects.equals(keepAliveInterval, that.keepAliveInterval) && Objects.equals(acquireHostList, that.acquireHostList) && Objects.equals(acquireHostListInterval, that.acquireHostListInterval) && Objects.equals(loadBalancingStrategy, that.loadBalancingStrategy) && Objects.equals(responseQueueTimeSamples, that.responseQueueTimeSamples) && Objects.equals(compression, that.compression) && Objects.equals(compressionThreshold, that.compressionThreshold) && Objects.equals(compressionLevel, that.compressionLevel); + return Objects.equals(hosts, that.hosts) && Objects.equals(protocol, that.protocol) && Objects.equals(user, that.user) && Objects.equals(password, that.password) && Objects.equals(jwt, that.jwt) && Objects.equals(timeout, that.timeout) && Objects.equals(useSsl, that.useSsl) && Objects.equals(verifyHost, that.verifyHost) && Objects.equals(chunkSize, that.chunkSize) && Objects.equals(maxConnections, that.maxConnections) && Objects.equals(connectionTtl, that.connectionTtl) && Objects.equals(keepAliveInterval, that.keepAliveInterval) && Objects.equals(acquireHostList, that.acquireHostList) && Objects.equals(acquireHostListInterval, that.acquireHostListInterval) && Objects.equals(loadBalancingStrategy, that.loadBalancingStrategy) && Objects.equals(responseQueueTimeSamples, that.responseQueueTimeSamples) && Objects.equals(compression, that.compression) && Objects.equals(compressionThreshold, that.compressionThreshold) && Objects.equals(compressionLevel, that.compressionLevel) && Objects.equals(serdeProviderClass, that.serdeProviderClass); } @Override public int hashCode() { - return Objects.hash(hosts, protocol, user, password, jwt, timeout, useSsl, verifyHost, chunkSize, maxConnections, connectionTtl, keepAliveInterval, acquireHostList, acquireHostListInterval, loadBalancingStrategy, responseQueueTimeSamples, compression, compressionThreshold, compressionLevel); + return Objects.hash(hosts, protocol, user, password, jwt, timeout, useSsl, verifyHost, chunkSize, maxConnections, connectionTtl, keepAliveInterval, acquireHostList, acquireHostListInterval, loadBalancingStrategy, responseQueueTimeSamples, compression, compressionThreshold, compressionLevel, serdeProviderClass); } @Override public String toString() { - return "ArangoConfigPropertiesImpl{" + + return "ArangoConfigPropertiesMPImpl{" + "hosts=" + hosts + ", protocol=" + protocol + ", user=" + user + @@ -164,6 +170,7 @@ public String toString() { ", compression=" + compression + ", compressionThreshold=" + compressionThreshold + ", compressionLevel=" + compressionLevel + + ", serdeProviderClass=" + serdeProviderClass + '}'; } } diff --git a/test-non-functional/src/test/java/mp/ConfigMPTest.java b/test-non-functional/src/test/java/mp/ConfigMPTest.java index 06bb8f254..4a7aaa993 100644 --- a/test-non-functional/src/test/java/mp/ConfigMPTest.java +++ b/test-non-functional/src/test/java/mp/ConfigMPTest.java @@ -33,6 +33,7 @@ class ConfigMPTest { private final Compression compression = Compression.GZIP; private final Integer compressionThreshold = 123456789; private final Integer compressionLevel = 9; + private final String serdeProviderClass = "com.arangodb.serde.jsonb.JsonbSerdeProvider"; @Test void readConfig() { @@ -73,5 +74,6 @@ private void checkResult(ArangoConfigProperties config) { assertThat(config.getCompression()).hasValue(compression); assertThat(config.getCompressionThreshold()).hasValue(compressionThreshold); assertThat(config.getCompressionLevel()).hasValue(compressionLevel); + assertThat(config.getSerdeProviderClass()).isPresent().hasValue(serdeProviderClass); } } diff --git a/test-non-functional/src/test/java/serde/SerdeConfigurationTest.java b/test-non-functional/src/test/java/serde/SerdeConfigurationTest.java new file mode 100644 index 000000000..adb05f1c0 --- /dev/null +++ b/test-non-functional/src/test/java/serde/SerdeConfigurationTest.java @@ -0,0 +1,81 @@ +package serde; + +import com.arangodb.ArangoDB; +import com.arangodb.config.ArangoConfigProperties; +import com.arangodb.serde.ArangoSerde; +import com.arangodb.serde.jackson.internal.JacksonSerdeImpl; +import com.arangodb.serde.jackson.json.JacksonJsonSerdeProvider; +import com.arangodb.serde.jackson.vpack.JacksonVPackSerdeProvider; +import com.arangodb.serde.jsonb.JsonbSerde; +import com.arangodb.serde.jsonb.JsonbSerdeProvider; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SerdeConfigurationTest { + private final VarHandle JACKSON_SERDE_IMPL_MAPPER; + { + try { + JACKSON_SERDE_IMPL_MAPPER = MethodHandles + .privateLookupIn(JacksonSerdeImpl.class, MethodHandles.lookup()) + .findVarHandle(JacksonSerdeImpl.class, "mapper", ObjectMapper.class); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + @Test + void vpackSerdeProvider() { + ArangoDB adb = new ArangoDB.Builder() + .host("foo", 1111) + .serdeProviderClass(JacksonVPackSerdeProvider.class) + .build(); + + ArangoSerde serde = adb.getSerde().getUserSerde(); + assertThat(serde).isInstanceOf(JacksonSerdeImpl.class); + + ObjectMapper mapper = (ObjectMapper) JACKSON_SERDE_IMPL_MAPPER.get(serde); + assertThat(mapper.getFactory().getFormatName()).isEqualTo("Velocypack"); + } + + @Test + void jsonSerdeProvider() { + ArangoDB adb = new ArangoDB.Builder() + .host("foo", 1111) + .serdeProviderClass(JacksonJsonSerdeProvider.class) + .build(); + + ArangoSerde serde = adb.getSerde().getUserSerde(); + assertThat(serde).isInstanceOf(JacksonSerdeImpl.class); + + ObjectMapper mapper = (ObjectMapper) JACKSON_SERDE_IMPL_MAPPER.get(serde); + assertThat(mapper.getFactory().getFormatName()).isEqualTo("JSON"); + } + + + @Test + void jsonBSerdeProvider() { + ArangoDB adb = new ArangoDB.Builder() + .host("foo", 1111) + .serdeProviderClass(JsonbSerdeProvider.class) + .build(); + + ArangoSerde serde = adb.getSerde().getUserSerde(); + assertThat(serde).isInstanceOf(JsonbSerde.class); + } + + @Test + void jsonBSerdeProviderFromConfigFile() { + ArangoDB adb = new ArangoDB.Builder() + .loadProperties(ArangoConfigProperties.fromFile("arangodb-serde-provider.properties")) + .build(); + + ArangoSerde serde = adb.getSerde().getUserSerde(); + assertThat(serde).isInstanceOf(JsonbSerde.class); + } + +} diff --git a/test-non-functional/src/test/resources/arangodb-config-test.properties b/test-non-functional/src/test/resources/arangodb-config-test.properties index e93668c93..ef25aaf11 100644 --- a/test-non-functional/src/test/resources/arangodb-config-test.properties +++ b/test-non-functional/src/test/resources/arangodb-config-test.properties @@ -17,3 +17,4 @@ adb.responseQueueTimeSamples=12345678 adb.compression=GZIP adb.compressionThreshold=123456789 adb.compressionLevel=9 +adb.serdeProviderClass=com.arangodb.serde.jsonb.JsonbSerdeProvider diff --git a/test-non-functional/src/test/resources/arangodb-serde-provider.properties b/test-non-functional/src/test/resources/arangodb-serde-provider.properties new file mode 100644 index 000000000..560134c78 --- /dev/null +++ b/test-non-functional/src/test/resources/arangodb-serde-provider.properties @@ -0,0 +1,3 @@ +arangodb.hosts=172.28.0.1:8529 +arangodb.password=test +arangodb.serdeProviderClass=com.arangodb.serde.jsonb.JsonbSerdeProvider