Skip to content

Commit

Permalink
merge: #9867
Browse files Browse the repository at this point in the history
9867: [Backport stable/1.3] Allow relaxed instantiation of exporter configuration r=npepinpe a=npepinpe

## Description

This PR backports #9854 to stable/1.3.

## Related issues

backports #9854 



Co-authored-by: Nicolas Pepin-Perreault <nicolas.pepin-perreault@camunda.com>
  • Loading branch information
zeebe-bors-camunda[bot] and npepinpe committed Jul 26, 2022
2 parents 181bc11 + 223e0b3 commit 93826f4
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 32 deletions.
4 changes: 0 additions & 4 deletions broker/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,31 @@
*/
package io.camunda.zeebe.broker.exporter.context;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import io.camunda.zeebe.exporter.api.ExporterException;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import io.camunda.zeebe.exporter.api.context.Configuration;
import io.camunda.zeebe.util.ReflectUtil;
import java.util.Map;
import java.util.Objects;

public final class ExporterConfiguration implements Configuration {
private static final Gson CONFIG_INSTANTIATOR = new GsonBuilder().create();

// Accepts more lenient cases, such that the property "something" would match a field "someThing"
// Note however that if a field "something" and "someThing" are present, only one of them will be
// instantiated (the last declared one), using the last matching value.
private static final ObjectMapper MAPPER =
JsonMapper.builder()
.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS)
.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)
.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES)
.enable(JsonReadFeature.ALLOW_SINGLE_QUOTES)
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.build();
private final String id;
private final Map<String, Object> arguments;

private JsonElement intermediateConfiguration;

public ExporterConfiguration(final String id, final Map<String, Object> arguments) {
this.id = id;
this.arguments = arguments;
Expand All @@ -40,25 +50,31 @@ public Map<String, Object> getArguments() {
@Override
public <T> T instantiate(final Class<T> configClass) {
if (arguments != null) {
return CONFIG_INSTANTIATOR.fromJson(getIntermediateConfiguration(), configClass);
return MAPPER.convertValue(arguments, configClass);
} else {
try {
return configClass.newInstance();
} catch (final Exception e) {
throw new ExporterException(
"Unable to instantiate config class "
+ configClass.getName()
+ " with default constructor",
e);
}
return ReflectUtil.newInstance(configClass);
}
}

private JsonElement getIntermediateConfiguration() {
if (intermediateConfiguration == null) {
intermediateConfiguration = CONFIG_INSTANTIATOR.toJsonTree(arguments);
@Override
public int hashCode() {
return Objects.hash(getId(), getArguments());
}

@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof ExporterConfiguration)) {
return false;
}
final ExporterConfiguration that = (ExporterConfiguration) o;
return getId().equals(that.getId()) && getArguments().equals(that.getArguments());
}

return intermediateConfiguration;
@Override
public String toString() {
return "ExporterConfiguration{" + "id='" + id + '\'' + ", arguments=" + arguments + '}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
* one or more contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright ownership.
* Licensed under the Zeebe Community License 1.1. You may not use this file
* except in compliance with the Zeebe Community License 1.1.
*/
package io.camunda.zeebe.broker.exporter.context;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.Map;
import java.util.Objects;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

@Execution(ExecutionMode.CONCURRENT)
final class ExporterConfigurationTest {
@ParameterizedTest
@ValueSource(strings = {"numberofshards", "numberOfShards", "NUMBEROFSHARDS"})
void shouldInstantiateConfigWithCaseInsensitiveProperties(final String property) {
// given
final var args = Map.<String, Object>of(property, 1);
final var expected = new Config(1);
final var config = new ExporterConfiguration("id", args);

// when
final var instance = config.instantiate(Config.class);

// then
assertThat(instance).isEqualTo(expected);
}

@ParameterizedTest
@ValueSource(strings = {"numberofshards", "numberOfShards", "NUMBEROFSHARDS"})
void shouldInstantiateNestedConfigWithCaseInsensitiveProperties(final String property) {
// given
final var args = Map.<String, Object>of("nested", Map.of(property, 1));
final var expected = new ContainerConfig(new Config(1));
final var config = new ExporterConfiguration("id", args);

// when
final var instance = config.instantiate(ContainerConfig.class);

// then
assertThat(instance).isEqualTo(expected);
}

@SuppressWarnings("unused")
private static final class ContainerConfig {
private Config nested;

public ContainerConfig(final Config nested) {
this.nested = nested;
}

public ContainerConfig() {}

public Config getNested() {
return nested;
}

@Override
public int hashCode() {
return Objects.hash(nested);
}

@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof ContainerConfig)) {
return false;
}
final ContainerConfig that = (ContainerConfig) o;
return Objects.equals(nested, that.nested);
}
}

@SuppressWarnings("unused")
private static final class Config {
private int numberOfShards;

public Config(final int numberOfShards) {
this.numberOfShards = numberOfShards;
}

public Config() {}

public int getNumberOfShards() {
return numberOfShards;
}

@Override
public int hashCode() {
return Objects.hash(numberOfShards);
}

@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Config)) {
return false;
}
final Config config = (Config) o;
return numberOfShards == config.numberOfShards;
}
}
}
13 changes: 7 additions & 6 deletions parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -457,12 +457,6 @@
<version>${version.guava}</version>
</dependency>

<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${version.gson}</version>
</dependency>

<dependency>
<groupId>io.zeebe</groupId>
<artifactId>zeebe-test-container</artifactId>
Expand Down Expand Up @@ -791,6 +785,13 @@
<artifactId>jnr-posix</artifactId>
<version>${version.jnr-posix}</version>
</dependency>

<!-- grpc-core & protobuf-java-util require different versions of gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${version.gson}</version>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down

0 comments on commit 93826f4

Please sign in to comment.