diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c68c121c3b..9094260fd11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,12 @@ ## 6.7.0 [unreleased] ### Features -1. [#439](https://github.com/influxdata/influxdb-client-java/pull/439): Added `FluxRecord.getRow()` which stores response data in a list +1. [#439](https://github.com/influxdata/influxdb-client-java/pull/439): Add `FluxRecord.getRow()` which stores response data in a list ### Dependencies + +1. [#446](https://github.com/influxdata/influxdb-client-java/pull/446): Remove `gson-fire` + Update dependencies: #### Build: diff --git a/client/pom.xml b/client/pom.xml index d004b94864b..65b1e8b3803 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -132,11 +132,6 @@ adapter-rxjava3 - - io.gsonfire - gson-fire - - com.squareup.retrofit2 converter-scalars diff --git a/client/src/generated/java/com/influxdb/client/JSON.java b/client/src/generated/java/com/influxdb/client/JSON.java index a102d5a6857..389bab0887d 100644 --- a/client/src/generated/java/com/influxdb/client/JSON.java +++ b/client/src/generated/java/com/influxdb/client/JSON.java @@ -13,24 +13,7 @@ package com.influxdb.client; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonIOException; -import com.google.gson.JsonParseException; -import com.google.gson.JsonSerializer; -import com.google.gson.TypeAdapter; -import com.google.gson.internal.bind.util.ISO8601Utils; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import com.google.gson.JsonElement; -import io.gsonfire.GsonFireBuilder; -import io.gsonfire.TypeSelector; - -import com.influxdb.client.domain.*; - import java.io.IOException; -import java.io.StringReader; -import java.lang.reflect.Type; import java.text.DateFormat; import java.text.ParseException; import java.text.ParsePosition; @@ -40,138 +23,97 @@ import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.util.Date; -import java.util.Locale; +import java.util.LinkedHashMap; import java.util.Map; -import java.util.HashMap; + +import javax.annotation.Nonnull; + +import com.influxdb.client.domain.*; +import com.influxdb.utils.Arguments; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonIOException; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.internal.bind.util.ISO8601Utils; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; public class JSON { private Gson gson; - private DateTypeAdapter dateTypeAdapter = new DateTypeAdapter(); - private SqlDateTypeAdapter sqlDateTypeAdapter = new SqlDateTypeAdapter(); - private OffsetDateTimeTypeAdapter offsetDateTimeTypeAdapter = new OffsetDateTimeTypeAdapter(); - private LocalDateTypeAdapter localDateTypeAdapter = new LocalDateTypeAdapter(); public static GsonBuilder createGson() { - GsonFireBuilder fireBuilder = new GsonFireBuilder() - .registerTypeSelector(Check.class, new TypeSelector() { - @Override - public Class getClassForElement(JsonElement readElement) { - Map classByDiscriminatorValue = new HashMap(); - classByDiscriminatorValue.put("deadman".toUpperCase(Locale.ROOT), DeadmanCheck.class); - classByDiscriminatorValue.put("threshold".toUpperCase(Locale.ROOT), ThresholdCheck.class); - classByDiscriminatorValue.put("custom".toUpperCase(Locale.ROOT), CustomCheck.class); - classByDiscriminatorValue.put("Check".toUpperCase(Locale.ROOT), Check.class); - return getClassByDiscriminator( - classByDiscriminatorValue, - getDiscriminatorValue(readElement, "type")); - } - }) - .registerTypeSelector(CheckDiscriminator.class, new TypeSelector() { - @Override - public Class getClassForElement(JsonElement readElement) { - Map classByDiscriminatorValue = new HashMap(); - classByDiscriminatorValue.put("deadman".toUpperCase(Locale.ROOT), DeadmanCheck.class); - classByDiscriminatorValue.put("threshold".toUpperCase(Locale.ROOT), ThresholdCheck.class); - classByDiscriminatorValue.put("custom".toUpperCase(Locale.ROOT), CustomCheck.class); - classByDiscriminatorValue.put("CheckDiscriminator".toUpperCase(Locale.ROOT), CheckDiscriminator.class); - return getClassByDiscriminator( - classByDiscriminatorValue, - getDiscriminatorValue(readElement, "type")); - } - }) - .registerTypeSelector(NotificationEndpoint.class, new TypeSelector() { - @Override - public Class getClassForElement(JsonElement readElement) { - Map classByDiscriminatorValue = new HashMap(); - classByDiscriminatorValue.put("slack".toUpperCase(Locale.ROOT), SlackNotificationEndpoint.class); - classByDiscriminatorValue.put("pagerduty".toUpperCase(Locale.ROOT), PagerDutyNotificationEndpoint.class); - classByDiscriminatorValue.put("http".toUpperCase(Locale.ROOT), HTTPNotificationEndpoint.class); - classByDiscriminatorValue.put("telegram".toUpperCase(Locale.ROOT), TelegramNotificationEndpoint.class); - classByDiscriminatorValue.put("NotificationEndpoint".toUpperCase(Locale.ROOT), NotificationEndpoint.class); - return getClassByDiscriminator( - classByDiscriminatorValue, - getDiscriminatorValue(readElement, "type")); - } - }) - .registerTypeSelector(NotificationEndpointDiscriminator.class, new TypeSelector() { - @Override - public Class getClassForElement(JsonElement readElement) { - Map classByDiscriminatorValue = new HashMap(); - classByDiscriminatorValue.put("slack".toUpperCase(Locale.ROOT), SlackNotificationEndpoint.class); - classByDiscriminatorValue.put("pagerduty".toUpperCase(Locale.ROOT), PagerDutyNotificationEndpoint.class); - classByDiscriminatorValue.put("http".toUpperCase(Locale.ROOT), HTTPNotificationEndpoint.class); - classByDiscriminatorValue.put("telegram".toUpperCase(Locale.ROOT), TelegramNotificationEndpoint.class); - classByDiscriminatorValue.put("NotificationEndpointDiscriminator".toUpperCase(Locale.ROOT), NotificationEndpointDiscriminator.class); - return getClassByDiscriminator( - classByDiscriminatorValue, - getDiscriminatorValue(readElement, "type")); - } - }) - .registerTypeSelector(NotificationRule.class, new TypeSelector() { - @Override - public Class getClassForElement(JsonElement readElement) { - Map classByDiscriminatorValue = new HashMap(); - classByDiscriminatorValue.put("slack".toUpperCase(Locale.ROOT), SlackNotificationRule.class); - classByDiscriminatorValue.put("smtp".toUpperCase(Locale.ROOT), SMTPNotificationRule.class); - classByDiscriminatorValue.put("pagerduty".toUpperCase(Locale.ROOT), PagerDutyNotificationRule.class); - classByDiscriminatorValue.put("http".toUpperCase(Locale.ROOT), HTTPNotificationRule.class); - classByDiscriminatorValue.put("telegram".toUpperCase(Locale.ROOT), TelegramNotificationRule.class); - classByDiscriminatorValue.put("NotificationRule".toUpperCase(Locale.ROOT), NotificationRule.class); - return getClassByDiscriminator( - classByDiscriminatorValue, - getDiscriminatorValue(readElement, "type")); - } - }) - .registerTypeSelector(NotificationRuleDiscriminator.class, new TypeSelector() { - @Override - public Class getClassForElement(JsonElement readElement) { - Map classByDiscriminatorValue = new HashMap(); - classByDiscriminatorValue.put("slack".toUpperCase(Locale.ROOT), SlackNotificationRule.class); - classByDiscriminatorValue.put("smtp".toUpperCase(Locale.ROOT), SMTPNotificationRule.class); - classByDiscriminatorValue.put("pagerduty".toUpperCase(Locale.ROOT), PagerDutyNotificationRule.class); - classByDiscriminatorValue.put("http".toUpperCase(Locale.ROOT), HTTPNotificationRule.class); - classByDiscriminatorValue.put("telegram".toUpperCase(Locale.ROOT), TelegramNotificationRule.class); - classByDiscriminatorValue.put("NotificationRuleDiscriminator".toUpperCase(Locale.ROOT), NotificationRuleDiscriminator.class); - return getClassByDiscriminator( - classByDiscriminatorValue, - getDiscriminatorValue(readElement, "type")); - } - }) - .registerTypeSelector(Threshold.class, new TypeSelector() { - @Override - public Class getClassForElement(JsonElement readElement) { - Map classByDiscriminatorValue = new HashMap(); - classByDiscriminatorValue.put("greater".toUpperCase(Locale.ROOT), GreaterThreshold.class); - classByDiscriminatorValue.put("lesser".toUpperCase(Locale.ROOT), LesserThreshold.class); - classByDiscriminatorValue.put("range".toUpperCase(Locale.ROOT), RangeThreshold.class); - classByDiscriminatorValue.put("Threshold".toUpperCase(Locale.ROOT), Threshold.class); - return getClassByDiscriminator( - classByDiscriminatorValue, - getDiscriminatorValue(readElement, "type")); - } - }) + GsonBuilder builder = new GsonBuilder(); - ; - return fireBuilder.createGsonBuilder(); - } - - private static String getDiscriminatorValue(JsonElement readElement, String discriminatorField) { - JsonElement element = readElement.getAsJsonObject().get(discriminatorField); - if(null == element) { - throw new IllegalArgumentException("missing discriminator field: <" + discriminatorField + ">"); - } - return element.getAsString(); - } - - private static Class getClassByDiscriminator(Map classByDiscriminatorValue, String discriminatorValue) { - Class clazz = (Class) classByDiscriminatorValue.get(discriminatorValue.toUpperCase(Locale.ROOT)); - if(null == clazz) { - throw new IllegalArgumentException("cannot determine model class of name: <" + discriminatorValue + ">"); - } - return clazz; + builder.registerTypeAdapterFactory(new DiscriminatorAdapter<>(Check.class, + new LinkedHashMap>() {{ + put("deadman", DeadmanCheck.class); + put("threshold", ThresholdCheck.class); + put("custom", CustomCheck.class); + }})); + + builder.registerTypeAdapterFactory(new DiscriminatorAdapter<>(CheckDiscriminator.class, + new LinkedHashMap>() {{ + put("deadman", DeadmanCheck.class); + put("threshold", ThresholdCheck.class); + put("custom", CustomCheck.class); + }})); + + builder.registerTypeAdapterFactory(new DiscriminatorAdapter<>(NotificationEndpoint.class, + new LinkedHashMap>() {{ + put("slack", SlackNotificationEndpoint.class); + put("pagerduty", PagerDutyNotificationEndpoint.class); + put("http", HTTPNotificationEndpoint.class); + put("telegram", TelegramNotificationEndpoint.class); + }})); + + builder.registerTypeAdapterFactory(new DiscriminatorAdapter<>(NotificationEndpointDiscriminator.class, + new LinkedHashMap>() {{ + put("slack", SlackNotificationEndpoint.class); + put("pagerduty", PagerDutyNotificationEndpoint.class); + put("http", HTTPNotificationEndpoint.class); + put("telegram", TelegramNotificationEndpoint.class); + }})); + + builder.registerTypeAdapterFactory(new DiscriminatorAdapter<>(NotificationRule.class, + new LinkedHashMap>() {{ + put("slack", SlackNotificationRule.class); + put("smtp", SMTPNotificationRule.class); + put("pagerduty", PagerDutyNotificationRule.class); + put("http", HTTPNotificationRule.class); + put("telegram", TelegramNotificationRule.class); + }})); + + builder.registerTypeAdapterFactory(new DiscriminatorAdapter<>(NotificationRuleDiscriminator.class, + new LinkedHashMap>() {{ + put("slack", SlackNotificationRule.class); + put("smtp", SMTPNotificationRule.class); + put("pagerduty", PagerDutyNotificationRule.class); + put("http", HTTPNotificationRule.class); + put("telegram", TelegramNotificationRule.class); + }})); + + builder.registerTypeAdapterFactory(new DiscriminatorAdapter<>(Threshold.class, + new LinkedHashMap>() {{ + put("greater", GreaterThreshold.class); + put("lesser", LesserThreshold.class); + put("range", RangeThreshold.class); + }})); + + return builder; } public JSON() { + DateTypeAdapter dateTypeAdapter = new DateTypeAdapter(); + SqlDateTypeAdapter sqlDateTypeAdapter = new SqlDateTypeAdapter(); + OffsetDateTimeTypeAdapter offsetDateTimeTypeAdapter = new OffsetDateTimeTypeAdapter(); + LocalDateTypeAdapter localDateTypeAdapter = new LocalDateTypeAdapter(); + gson = createGson() .registerTypeAdapter(Date.class, dateTypeAdapter) .registerTypeAdapter(java.sql.Date.class, sqlDateTypeAdapter) @@ -246,7 +188,7 @@ public OffsetDateTime read(JsonReader in) throws IOException { default: String date = in.nextString(); if (date.endsWith("+0000")) { - date = date.substring(0, date.length()-5) + "Z"; + date = date.substring(0, date.length() - 5) + "Z"; } return OffsetDateTime.parse(date, formatter); } @@ -294,16 +236,6 @@ public LocalDate read(JsonReader in) throws IOException { } } - public JSON setOffsetDateTimeFormat(DateTimeFormatter dateFormat) { - offsetDateTimeTypeAdapter.setFormat(dateFormat); - return this; - } - - public JSON setLocalDateFormat(DateTimeFormatter dateFormat) { - localDateTypeAdapter.setFormat(dateFormat); - return this; - } - /** * Gson TypeAdapter for java.sql.Date type * If the dateFormat is null, a simple "yyyy-MM-dd" format will be used @@ -316,10 +248,6 @@ public static class SqlDateTypeAdapter extends TypeAdapter { public SqlDateTypeAdapter() { } - public SqlDateTypeAdapter(DateFormat dateFormat) { - this.dateFormat = dateFormat; - } - public void setFormat(DateFormat dateFormat) { this.dateFormat = dateFormat; } @@ -370,10 +298,6 @@ public static class DateTypeAdapter extends TypeAdapter { public DateTypeAdapter() { } - public DateTypeAdapter(DateFormat dateFormat) { - this.dateFormat = dateFormat; - } - public void setFormat(DateFormat dateFormat) { this.dateFormat = dateFormat; } @@ -417,14 +341,93 @@ public Date read(JsonReader in) throws IOException { } } - public JSON setDateFormat(DateFormat dateFormat) { - dateTypeAdapter.setFormat(dateFormat); - return this; - } + @SuppressWarnings("unchecked") + static final class DiscriminatorAdapter implements TypeAdapterFactory { + private final Class type; + private final Map> subTypes; + private TypeAdapter cachedAdapter; + + DiscriminatorAdapter(@Nonnull final Class type, + @Nonnull final Map> subTypes) { + Arguments.checkNotNull(type, "type"); + Arguments.checkNotNull(subTypes, "subTypes"); + this.type = type; + this.subTypes = subTypes; + } - public JSON setSqlDateFormat(DateFormat dateFormat) { - sqlDateTypeAdapter.setFormat(dateFormat); - return this; - } + @Override + public TypeAdapter create(Gson gson, TypeToken type) { + if (type == null) { + return null; + } + + if (!this.type.isAssignableFrom(type.getRawType())) { + return null; + } + + if (cachedAdapter == null) { + cachedAdapter = new InnerDiscriminatorAdapter(gson).nullSafe(); + } + return (TypeAdapter) cachedAdapter; + } + + private class InnerDiscriminatorAdapter extends TypeAdapter { + private final Gson gson; + private final TypeAdapter jsonAdapter; + private final Map> cachedSubtypesReadAdapters; + private final Map, TypeAdapter> cachedSubtypesWriteAdapters; + + InnerDiscriminatorAdapter(@Nonnull final Gson gson) { + Arguments.checkNotNull(gson, "gson"); + + this.gson = gson; + this.jsonAdapter = gson.getAdapter(JsonElement.class); + this.cachedSubtypesReadAdapters = new LinkedHashMap<>(); + this.cachedSubtypesWriteAdapters = new LinkedHashMap<>(); + + for (Map.Entry> entry : subTypes.entrySet()) { + TypeAdapter adapter = gson.getDelegateAdapter(DiscriminatorAdapter.this, + TypeToken.get(entry.getValue())); + cachedSubtypesReadAdapters.put(entry.getKey(), adapter); + cachedSubtypesWriteAdapters.put(entry.getValue(), adapter); + } + } + + @Override + public R read(JsonReader in) throws IOException { + JsonObject json = jsonAdapter.read(in).getAsJsonObject(); + JsonElement discriminatorJson = json.get("type"); + + if (discriminatorJson == null) { + String msg = String.format("Cannot find JSON field 'type' for %s adapter. JSON value: '%s'", + DiscriminatorAdapter.this.type, json); + throw new JsonParseException(msg); + } + + String discriminator = discriminatorJson.getAsString(); + TypeAdapter adapter = (TypeAdapter) cachedSubtypesReadAdapters.get(discriminator); + if (adapter == null) { + String msg = String.format("Cannot find model: '%s' for discriminator: '%s'. " + + "The discriminator wasn't registered.", discriminator, DiscriminatorAdapter.this.type); + throw new JsonParseException(msg); + } + return adapter.fromJsonTree(json); + } + + @Override + public void write(JsonWriter out, R value) throws IOException { + Class outputType = value.getClass(); + TypeAdapter adapter = (TypeAdapter) cachedSubtypesWriteAdapters.get(outputType); + + if (adapter == null) { + adapter = (TypeAdapter) gson.getDelegateAdapter(DiscriminatorAdapter.this, + TypeToken.get(outputType)); + cachedSubtypesWriteAdapters.put(type, adapter); + } + + jsonAdapter.write(out, adapter.toJsonTree(value).getAsJsonObject()); + } + } + } } diff --git a/client/src/test/java/com/influxdb/client/JSONTest.java b/client/src/test/java/com/influxdb/client/JSONTest.java new file mode 100644 index 00000000000..34d9f6e8137 --- /dev/null +++ b/client/src/test/java/com/influxdb/client/JSONTest.java @@ -0,0 +1,63 @@ +/* + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.influxdb.client; + +import com.influxdb.client.domain.CheckStatusLevel; +import com.influxdb.client.domain.Threshold; + +import com.google.gson.Gson; +import com.google.gson.JsonParseException; +import com.google.gson.annotations.SerializedName; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +public class JSONTest { + @Test + public void serializeUnknownDiscriminator() { + DummyThreshold dummyThreshold = new DummyThreshold(); + dummyThreshold.setLevel(CheckStatusLevel.OK); + + Gson gson = JSON.createGson().create(); + String json = gson.toJson(dummyThreshold); + Assertions.assertThat(json).isEqualTo("{\"type\":\"dummy\",\"value\":15,\"level\":\"OK\"}"); + } + + @Test + public void deSerializeUnknownDiscriminator() { + Gson gson = JSON.createGson().create(); + Assertions.assertThatThrownBy(() -> { + gson.fromJson("{\"type\":\"not_registered\",\"value\":16,\"level\":\"OK\"}", Threshold.class); + }) + .isInstanceOf(JsonParseException.class) + .hasMessage("Cannot find model: 'not_registered' for discriminator: " + + "'class com.influxdb.client.domain.Threshold'. The discriminator wasn't registered."); + } + + @SuppressWarnings("unused") + public static class DummyThreshold extends Threshold { + @SerializedName("type") + private String type = "dummy"; + + @SerializedName("value") + private Integer value = 15; + } +} diff --git a/pom.xml b/pom.xml index 406db9e92e5..bcb4871ff7b 100644 --- a/pom.xml +++ b/pom.xml @@ -601,18 +601,6 @@ ${dependency.gson.version} - - io.gsonfire - gson-fire - 1.8.5 - - - com.google.code.gson - gson - - - - io.reactivex.rxjava3 rxjava