mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
server: Update gson date format for serializing/deserializing Date in MS stats (#11506)
* Update gson date format for serializing/deserializing Date in MS stats (across multiple management servers) * review * review comments, and unit tests * added unit test with different date format * Use separate Gson for MS stats serialization/deserialization
This commit is contained in:
parent
393b5d2b77
commit
12513e18fa
@ -46,6 +46,7 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import com.cloud.utils.DateUtil;
|
||||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
||||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider;
|
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider;
|
||||||
@ -170,10 +171,10 @@ import com.codahale.metrics.jvm.GarbageCollectorMetricSet;
|
|||||||
import com.codahale.metrics.jvm.MemoryUsageGaugeSet;
|
import com.codahale.metrics.jvm.MemoryUsageGaugeSet;
|
||||||
import com.codahale.metrics.jvm.ThreadStatesGaugeSet;
|
import com.codahale.metrics.jvm.ThreadStatesGaugeSet;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonParseException;
|
import com.google.gson.JsonParseException;
|
||||||
import com.google.gson.reflect.TypeToken;
|
|
||||||
import com.sun.management.OperatingSystemMXBean;
|
import com.sun.management.OperatingSystemMXBean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -294,6 +295,9 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
|
|||||||
private static StatsCollector s_instance = null;
|
private static StatsCollector s_instance = null;
|
||||||
|
|
||||||
private static Gson gson = new Gson();
|
private static Gson gson = new Gson();
|
||||||
|
private static Gson msStatsGson = new GsonBuilder()
|
||||||
|
.setDateFormat(DateUtil.ZONED_DATETIME_FORMAT)
|
||||||
|
.create();
|
||||||
|
|
||||||
private ScheduledExecutorService _executor = null;
|
private ScheduledExecutorService _executor = null;
|
||||||
@Inject
|
@Inject
|
||||||
@ -739,7 +743,6 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
|
|||||||
dbStats.put(uptime, (Long.valueOf(stats.get(uptime))));
|
dbStats.put(uptime, (Long.valueOf(stats.get(uptime))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Point createInfluxDbPoint(Object metricsObject) {
|
protected Point createInfluxDbPoint(Object metricsObject) {
|
||||||
return null;
|
return null;
|
||||||
@ -759,7 +762,7 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
|
|||||||
hostStatsEntry = getDataFrom(mshost);
|
hostStatsEntry = getDataFrom(mshost);
|
||||||
managementServerHostStats.put(mshost.getUuid(), hostStatsEntry);
|
managementServerHostStats.put(mshost.getUuid(), hostStatsEntry);
|
||||||
// send to other hosts
|
// send to other hosts
|
||||||
clusterManager.publishStatus(gson.toJson(hostStatsEntry));
|
clusterManager.publishStatus(msStatsGson.toJson(hostStatsEntry));
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
// pokemon catch to make sure the thread stays running
|
// pokemon catch to make sure the thread stays running
|
||||||
logger.error("Error trying to retrieve management server host statistics", t);
|
logger.error("Error trying to retrieve management server host statistics", t);
|
||||||
@ -1158,9 +1161,9 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
|
|||||||
logger.debug(String.format("StatusUpdate from %s, json: %s", pdu.getSourcePeer(), pdu.getJsonPackage()));
|
logger.debug(String.format("StatusUpdate from %s, json: %s", pdu.getSourcePeer(), pdu.getJsonPackage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
ManagementServerHostStatsEntry hostStatsEntry = null;
|
ManagementServerHostStatsEntry hostStatsEntry;
|
||||||
try {
|
try {
|
||||||
hostStatsEntry = gson.fromJson(pdu.getJsonPackage(),new TypeToken<ManagementServerHostStatsEntry>(){}.getType());
|
hostStatsEntry = msStatsGson.fromJson(pdu.getJsonPackage(), ManagementServerHostStatsEntry.class);
|
||||||
managementServerHostStats.put(hostStatsEntry.getManagementServerHostUuid(), hostStatsEntry);
|
managementServerHostStats.put(hostStatsEntry.getManagementServerHostUuid(), hostStatsEntry);
|
||||||
|
|
||||||
// Update peer state to Up in mshost_peer
|
// Update peer state to Up in mshost_peer
|
||||||
|
|||||||
@ -20,8 +20,10 @@ package com.cloud.server;
|
|||||||
|
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@ -33,6 +35,8 @@ import java.util.TreeMap;
|
|||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import com.cloud.utils.DateUtil;
|
||||||
|
import com.google.gson.JsonSyntaxException;
|
||||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
@ -116,6 +120,8 @@ public class StatsCollectorTest {
|
|||||||
|
|
||||||
private static Gson gson = new Gson();
|
private static Gson gson = new Gson();
|
||||||
|
|
||||||
|
private Gson msStatsGson;
|
||||||
|
|
||||||
private MockedStatic<InfluxDBFactory> influxDBFactoryMocked;
|
private MockedStatic<InfluxDBFactory> influxDBFactoryMocked;
|
||||||
|
|
||||||
private AutoCloseable closeable;
|
private AutoCloseable closeable;
|
||||||
@ -125,6 +131,9 @@ public class StatsCollectorTest {
|
|||||||
closeable = MockitoAnnotations.openMocks(this);
|
closeable = MockitoAnnotations.openMocks(this);
|
||||||
statsCollector.vmStatsDao = vmStatsDaoMock;
|
statsCollector.vmStatsDao = vmStatsDaoMock;
|
||||||
statsCollector.volumeStatsDao = volumeStatsDao;
|
statsCollector.volumeStatsDao = volumeStatsDao;
|
||||||
|
Field msStatsGsonField = StatsCollector.class.getDeclaredField("msStatsGson");
|
||||||
|
msStatsGsonField.setAccessible(true);
|
||||||
|
msStatsGson = (Gson) msStatsGsonField.get(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@ -612,4 +621,107 @@ public class StatsCollectorTest {
|
|||||||
Mockito.verify(mockPool, Mockito.never()).setCapacityIops(Mockito.anyLong());
|
Mockito.verify(mockPool, Mockito.never()).setCapacityIops(Mockito.anyLong());
|
||||||
Mockito.verify(mockPool, Mockito.never()).setUsedIops(Mockito.anyLong());
|
Mockito.verify(mockPool, Mockito.never()).setUsedIops(Mockito.anyLong());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGsonDateFormatSerialization() {
|
||||||
|
Date now = new Date();
|
||||||
|
TestClass testObj = new TestClass("TestString", 999, now);
|
||||||
|
String json = msStatsGson.toJson(testObj);
|
||||||
|
|
||||||
|
Assert.assertTrue(json.contains("TestString"));
|
||||||
|
Assert.assertTrue(json.contains("999"));
|
||||||
|
String expectedDate = new SimpleDateFormat(DateUtil.ZONED_DATETIME_FORMAT).format(now);
|
||||||
|
Assert.assertTrue(json.contains(expectedDate));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGsonDateFormatDeserializationWithSameDateFormat() throws Exception {
|
||||||
|
String json = "{\"str\":\"TestString\",\"num\":999,\"date\":\"2025-08-22T15:39:43+0000\"}";
|
||||||
|
TestClass testObj = msStatsGson.fromJson(json, TestClass.class);
|
||||||
|
|
||||||
|
Assert.assertEquals("TestString", testObj.getStr());
|
||||||
|
Assert.assertEquals(999, testObj.getNum());
|
||||||
|
Date expectedDate = new SimpleDateFormat(DateUtil.ZONED_DATETIME_FORMAT).parse("2025-08-22T15:39:43+0000");
|
||||||
|
Assert.assertEquals(expectedDate, testObj.getDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test (expected = JsonSyntaxException.class)
|
||||||
|
public void testGsonDateFormatDeserializationWithDifferentDateFormat() throws Exception {
|
||||||
|
String json = "{\"str\":\"TestString\",\"num\":999,\"date\":\"22/08/2025T15:39:43+0000\"}";
|
||||||
|
msStatsGson.fromJson(json, TestClass.class);
|
||||||
|
/* Deserialization throws the below exception:
|
||||||
|
com.google.gson.JsonSyntaxException: 22/08/2025T15:39:43+0000
|
||||||
|
at com.google.gson.DefaultTypeAdapters$DefaultDateTypeAdapter.deserializeToDate(DefaultTypeAdapters.java:376)
|
||||||
|
at com.google.gson.DefaultTypeAdapters$DefaultDateTypeAdapter.deserialize(DefaultTypeAdapters.java:351)
|
||||||
|
at com.google.gson.DefaultTypeAdapters$DefaultDateTypeAdapter.deserialize(DefaultTypeAdapters.java:307)
|
||||||
|
at com.google.gson.JsonDeserializationVisitor.invokeCustomDeserializer(JsonDeserializationVisitor.java:92)
|
||||||
|
at com.google.gson.JsonObjectDeserializationVisitor.visitFieldUsingCustomHandler(JsonObjectDeserializationVisitor.java:117)
|
||||||
|
at com.google.gson.ReflectingFieldNavigator.visitFieldsReflectively(ReflectingFieldNavigator.java:63)
|
||||||
|
at com.google.gson.ObjectNavigator.accept(ObjectNavigator.java:120)
|
||||||
|
at com.google.gson.JsonDeserializationContextDefault.fromJsonObject(JsonDeserializationContextDefault.java:76)
|
||||||
|
at com.google.gson.JsonDeserializationContextDefault.deserialize(JsonDeserializationContextDefault.java:54)
|
||||||
|
at com.google.gson.Gson.fromJson(Gson.java:551)
|
||||||
|
at com.google.gson.Gson.fromJson(Gson.java:498)
|
||||||
|
at com.google.gson.Gson.fromJson(Gson.java:467)
|
||||||
|
at com.google.gson.Gson.fromJson(Gson.java:417)
|
||||||
|
at com.google.gson.Gson.fromJson(Gson.java:389)
|
||||||
|
at com.cloud.serializer.GsonHelperTest.testGsonDateFormatDeserializationWithDifferentDateFormat(GsonHelperTest.java:113)
|
||||||
|
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
|
||||||
|
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
|
||||||
|
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
|
||||||
|
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
|
||||||
|
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
|
||||||
|
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
|
||||||
|
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
|
||||||
|
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
|
||||||
|
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
|
||||||
|
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
|
||||||
|
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
|
||||||
|
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
|
||||||
|
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
|
||||||
|
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
|
||||||
|
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
|
||||||
|
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
|
||||||
|
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
|
||||||
|
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
|
||||||
|
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
|
||||||
|
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
|
||||||
|
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
|
||||||
|
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
|
||||||
|
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
|
||||||
|
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
|
||||||
|
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
|
||||||
|
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
|
||||||
|
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:231)
|
||||||
|
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
|
||||||
|
Caused by: java.text.ParseException: Unparseable date: "22/08/2025T15:39:43+0000"
|
||||||
|
at java.base/java.text.DateFormat.parse(DateFormat.java:395)
|
||||||
|
at com.google.gson.DefaultTypeAdapters$DefaultDateTypeAdapter.deserializeToDate(DefaultTypeAdapters.java:374)
|
||||||
|
... 42 more
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TestClass {
|
||||||
|
private String str;
|
||||||
|
private int num;
|
||||||
|
private Date date;
|
||||||
|
|
||||||
|
public TestClass(String str, int num, Date date) {
|
||||||
|
this.str = str;
|
||||||
|
this.num = num;
|
||||||
|
this.date = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStr() {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNum() {
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getDate() {
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,7 +48,7 @@ public class DateUtil {
|
|||||||
|
|
||||||
public static final TimeZone GMT_TIMEZONE = TimeZone.getTimeZone("GMT");
|
public static final TimeZone GMT_TIMEZONE = TimeZone.getTimeZone("GMT");
|
||||||
public static final String YYYYMMDD_FORMAT = "yyyyMMddHHmmss";
|
public static final String YYYYMMDD_FORMAT = "yyyyMMddHHmmss";
|
||||||
private static final String ZONED_DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZ";
|
public static final String ZONED_DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZ";
|
||||||
private static final DateFormat ZONED_DATETIME_SIMPLE_FORMATTER = new SimpleDateFormat(ZONED_DATETIME_FORMAT);
|
private static final DateFormat ZONED_DATETIME_SIMPLE_FORMATTER = new SimpleDateFormat(ZONED_DATETIME_FORMAT);
|
||||||
|
|
||||||
private static final DateTimeFormatter[] parseFormats = new DateTimeFormatter[]{
|
private static final DateTimeFormatter[] parseFormats = new DateTimeFormatter[]{
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user