Skip to content

Commit b1b0087

Browse files
Use separate Gson for MS stats serialization/deserialization
1 parent 36ae0d4 commit b1b0087

4 files changed

Lines changed: 116 additions & 115 deletions

File tree

core/src/main/java/com/cloud/serializer/GsonHelper.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import java.util.List;
2323

2424
import com.cloud.hypervisor.Hypervisor;
25-
import com.cloud.utils.DateUtil;
2625
import org.apache.cloudstack.transport.HypervisorTypeAdaptor;
2726
import org.apache.logging.log4j.Logger;
2827
import org.apache.logging.log4j.LogManager;
@@ -79,7 +78,6 @@ static Gson setDefaultGsonConfig(GsonBuilder builder) {
7978
}.getType(), new NwGroupsCommandTypeAdaptor());
8079
builder.registerTypeAdapter(Storage.StoragePoolType.class, new StoragePoolTypeAdaptor());
8180
builder.registerTypeAdapter(Hypervisor.HypervisorType.class, new HypervisorTypeAdaptor());
82-
builder.setDateFormat(DateUtil.ZONED_DATETIME_FORMAT);
8381
Gson gson = builder.create();
8482
dsAdaptor.initGson(gson);
8583
dtAdaptor.initGson(gson);

core/src/test/java/com/cloud/serializer/GsonHelperTest.java

Lines changed: 0 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,10 @@
1919

2020
import com.cloud.agent.api.to.NfsTO;
2121
import com.cloud.storage.DataStoreRole;
22-
import com.cloud.utils.DateUtil;
2322
import com.google.gson.Gson;
24-
import com.google.gson.JsonSyntaxException;
2523
import org.junit.Before;
2624
import org.junit.Test;
2725

28-
import java.text.SimpleDateFormat;
29-
import java.util.Date;
30-
3126
import static org.junit.Assert.assertEquals;
3227
import static org.junit.Assert.assertNotNull;
3328
import static org.junit.Assert.assertTrue;
@@ -83,108 +78,4 @@ public void testGsonLoggerDeserialization() {
8378
assertEquals("http://example.com", deserializedNfsTO.getUrl());
8479
assertEquals(DataStoreRole.Primary, deserializedNfsTO.getRole());
8580
}
86-
87-
@Test
88-
public void testGsonDateFormatSerialization() {
89-
Date now = new Date();
90-
TestClass testObj = new TestClass("TestString", 999, now);
91-
String json = gson.toJson(testObj);
92-
93-
assertTrue(json.contains("TestString"));
94-
assertTrue(json.contains("999"));
95-
String expectedDate = new SimpleDateFormat(DateUtil.ZONED_DATETIME_FORMAT).format(now);
96-
assertTrue(json.contains(expectedDate));
97-
}
98-
99-
@Test
100-
public void testGsonDateFormatDeserializationWithSameDateFormat() throws Exception {
101-
String json = "{\"str\":\"TestString\",\"num\":999,\"date\":\"2025-08-22T15:39:43+0000\"}";
102-
TestClass testObj = gson.fromJson(json, TestClass.class);
103-
104-
assertEquals("TestString", testObj.getStr());
105-
assertEquals(999, testObj.getNum());
106-
Date expectedDate = new SimpleDateFormat(DateUtil.ZONED_DATETIME_FORMAT).parse("2025-08-22T15:39:43+0000");
107-
assertEquals(expectedDate, testObj.getDate());
108-
}
109-
110-
@Test (expected = JsonSyntaxException.class)
111-
public void testGsonDateFormatDeserializationWithDifferentDateFormat() throws Exception {
112-
String json = "{\"str\":\"TestString\",\"num\":999,\"date\":\"22/08/2025T15:39:43+0000\"}";
113-
gson.fromJson(json, TestClass.class);
114-
/* Deserialization throws the below exception:
115-
com.google.gson.JsonSyntaxException: 22/08/2025T15:39:43+0000
116-
117-
at com.google.gson.DefaultTypeAdapters$DefaultDateTypeAdapter.deserializeToDate(DefaultTypeAdapters.java:376)
118-
at com.google.gson.DefaultTypeAdapters$DefaultDateTypeAdapter.deserialize(DefaultTypeAdapters.java:351)
119-
at com.google.gson.DefaultTypeAdapters$DefaultDateTypeAdapter.deserialize(DefaultTypeAdapters.java:307)
120-
at com.google.gson.JsonDeserializationVisitor.invokeCustomDeserializer(JsonDeserializationVisitor.java:92)
121-
at com.google.gson.JsonObjectDeserializationVisitor.visitFieldUsingCustomHandler(JsonObjectDeserializationVisitor.java:117)
122-
at com.google.gson.ReflectingFieldNavigator.visitFieldsReflectively(ReflectingFieldNavigator.java:63)
123-
at com.google.gson.ObjectNavigator.accept(ObjectNavigator.java:120)
124-
at com.google.gson.JsonDeserializationContextDefault.fromJsonObject(JsonDeserializationContextDefault.java:76)
125-
at com.google.gson.JsonDeserializationContextDefault.deserialize(JsonDeserializationContextDefault.java:54)
126-
at com.google.gson.Gson.fromJson(Gson.java:551)
127-
at com.google.gson.Gson.fromJson(Gson.java:498)
128-
at com.google.gson.Gson.fromJson(Gson.java:467)
129-
at com.google.gson.Gson.fromJson(Gson.java:417)
130-
at com.google.gson.Gson.fromJson(Gson.java:389)
131-
at com.cloud.serializer.GsonHelperTest.testGsonDateFormatDeserializationWithDifferentDateFormat(GsonHelperTest.java:113)
132-
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
133-
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
134-
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
135-
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
136-
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
137-
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
138-
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
139-
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
140-
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
141-
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
142-
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
143-
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
144-
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
145-
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
146-
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
147-
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
148-
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
149-
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
150-
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
151-
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
152-
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
153-
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
154-
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
155-
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
156-
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
157-
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
158-
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:231)
159-
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
160-
Caused by: java.text.ParseException: Unparseable date: "22/08/2025T15:39:43+0000"
161-
at java.base/java.text.DateFormat.parse(DateFormat.java:395)
162-
at com.google.gson.DefaultTypeAdapters$DefaultDateTypeAdapter.deserializeToDate(DefaultTypeAdapters.java:374)
163-
... 42 more
164-
*/
165-
}
166-
167-
private static class TestClass {
168-
private String str;
169-
private int num;
170-
private Date date;
171-
172-
public TestClass(String str, int num, Date date) {
173-
this.str = str;
174-
this.num = num;
175-
this.date = date;
176-
}
177-
178-
public String getStr() {
179-
return str;
180-
}
181-
182-
public int getNum() {
183-
return num;
184-
}
185-
186-
public Date getDate() {
187-
return date;
188-
}
189-
}
19081
}

server/src/main/java/com/cloud/server/StatsCollector.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,8 @@ public String toString() {
294294

295295
private static StatsCollector s_instance = null;
296296

297-
private static Gson gson = new GsonBuilder()
297+
private static Gson gson = new Gson();
298+
private static Gson msStatsGson = new GsonBuilder()
298299
.setDateFormat(DateUtil.ZONED_DATETIME_FORMAT)
299300
.create();
300301

@@ -742,7 +743,6 @@ private void getDynamicDataFromDB() {
742743
dbStats.put(uptime, (Long.valueOf(stats.get(uptime))));
743744
}
744745

745-
746746
@Override
747747
protected Point createInfluxDbPoint(Object metricsObject) {
748748
return null;
@@ -762,7 +762,7 @@ protected void runInContext() {
762762
hostStatsEntry = getDataFrom(mshost);
763763
managementServerHostStats.put(mshost.getUuid(), hostStatsEntry);
764764
// send to other hosts
765-
clusterManager.publishStatus(gson.toJson(hostStatsEntry));
765+
clusterManager.publishStatus(msStatsGson.toJson(hostStatsEntry));
766766
} catch (Throwable t) {
767767
// pokemon catch to make sure the thread stays running
768768
logger.error("Error trying to retrieve management server host statistics", t);
@@ -1163,7 +1163,7 @@ public String newStatus(ClusterServicePdu pdu) {
11631163

11641164
ManagementServerHostStatsEntry hostStatsEntry;
11651165
try {
1166-
hostStatsEntry = gson.fromJson(pdu.getJsonPackage(), ManagementServerHostStatsEntry.class);
1166+
hostStatsEntry = msStatsGson.fromJson(pdu.getJsonPackage(), ManagementServerHostStatsEntry.class);
11671167
managementServerHostStats.put(hostStatsEntry.getManagementServerHostUuid(), hostStatsEntry);
11681168

11691169
// Update peer state to Up in mshost_peer

server/src/test/java/com/cloud/server/StatsCollectorTest.java

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020

2121
import static org.mockito.Mockito.when;
2222

23+
import java.lang.reflect.Field;
2324
import java.net.URI;
2425
import java.net.URISyntaxException;
26+
import java.text.SimpleDateFormat;
2527
import java.util.ArrayList;
2628
import java.util.Arrays;
2729
import java.util.Date;
@@ -33,6 +35,8 @@
3335
import java.util.concurrent.ConcurrentHashMap;
3436
import java.util.concurrent.TimeUnit;
3537

38+
import com.cloud.utils.DateUtil;
39+
import com.google.gson.JsonSyntaxException;
3640
import org.apache.cloudstack.framework.config.ConfigKey;
3741
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
3842
import org.apache.commons.collections.CollectionUtils;
@@ -116,6 +120,8 @@ public class StatsCollectorTest {
116120

117121
private static Gson gson = new Gson();
118122

123+
private Gson msStatsGson;
124+
119125
private MockedStatic<InfluxDBFactory> influxDBFactoryMocked;
120126

121127
private AutoCloseable closeable;
@@ -125,6 +131,9 @@ public void setUp() throws Exception {
125131
closeable = MockitoAnnotations.openMocks(this);
126132
statsCollector.vmStatsDao = vmStatsDaoMock;
127133
statsCollector.volumeStatsDao = volumeStatsDao;
134+
Field msStatsGsonField = StatsCollector.class.getDeclaredField("msStatsGson");
135+
msStatsGsonField.setAccessible(true);
136+
msStatsGson = (Gson) msStatsGsonField.get(null);
128137
}
129138

130139
@After
@@ -612,4 +621,107 @@ public void testPoolNeedsIopsStatsUpdating_NullIops() {
612621
Mockito.verify(mockPool, Mockito.never()).setCapacityIops(Mockito.anyLong());
613622
Mockito.verify(mockPool, Mockito.never()).setUsedIops(Mockito.anyLong());
614623
}
624+
625+
@Test
626+
public void testGsonDateFormatSerialization() {
627+
Date now = new Date();
628+
TestClass testObj = new TestClass("TestString", 999, now);
629+
String json = msStatsGson.toJson(testObj);
630+
631+
Assert.assertTrue(json.contains("TestString"));
632+
Assert.assertTrue(json.contains("999"));
633+
String expectedDate = new SimpleDateFormat(DateUtil.ZONED_DATETIME_FORMAT).format(now);
634+
Assert.assertTrue(json.contains(expectedDate));
635+
}
636+
637+
@Test
638+
public void testGsonDateFormatDeserializationWithSameDateFormat() throws Exception {
639+
String json = "{\"str\":\"TestString\",\"num\":999,\"date\":\"2025-08-22T15:39:43+0000\"}";
640+
TestClass testObj = msStatsGson.fromJson(json, TestClass.class);
641+
642+
Assert.assertEquals("TestString", testObj.getStr());
643+
Assert.assertEquals(999, testObj.getNum());
644+
Date expectedDate = new SimpleDateFormat(DateUtil.ZONED_DATETIME_FORMAT).parse("2025-08-22T15:39:43+0000");
645+
Assert.assertEquals(expectedDate, testObj.getDate());
646+
}
647+
648+
@Test (expected = JsonSyntaxException.class)
649+
public void testGsonDateFormatDeserializationWithDifferentDateFormat() throws Exception {
650+
String json = "{\"str\":\"TestString\",\"num\":999,\"date\":\"22/08/2025T15:39:43+0000\"}";
651+
msStatsGson.fromJson(json, TestClass.class);
652+
/* Deserialization throws the below exception:
653+
com.google.gson.JsonSyntaxException: 22/08/2025T15:39:43+0000
654+
at com.google.gson.DefaultTypeAdapters$DefaultDateTypeAdapter.deserializeToDate(DefaultTypeAdapters.java:376)
655+
at com.google.gson.DefaultTypeAdapters$DefaultDateTypeAdapter.deserialize(DefaultTypeAdapters.java:351)
656+
at com.google.gson.DefaultTypeAdapters$DefaultDateTypeAdapter.deserialize(DefaultTypeAdapters.java:307)
657+
at com.google.gson.JsonDeserializationVisitor.invokeCustomDeserializer(JsonDeserializationVisitor.java:92)
658+
at com.google.gson.JsonObjectDeserializationVisitor.visitFieldUsingCustomHandler(JsonObjectDeserializationVisitor.java:117)
659+
at com.google.gson.ReflectingFieldNavigator.visitFieldsReflectively(ReflectingFieldNavigator.java:63)
660+
at com.google.gson.ObjectNavigator.accept(ObjectNavigator.java:120)
661+
at com.google.gson.JsonDeserializationContextDefault.fromJsonObject(JsonDeserializationContextDefault.java:76)
662+
at com.google.gson.JsonDeserializationContextDefault.deserialize(JsonDeserializationContextDefault.java:54)
663+
at com.google.gson.Gson.fromJson(Gson.java:551)
664+
at com.google.gson.Gson.fromJson(Gson.java:498)
665+
at com.google.gson.Gson.fromJson(Gson.java:467)
666+
at com.google.gson.Gson.fromJson(Gson.java:417)
667+
at com.google.gson.Gson.fromJson(Gson.java:389)
668+
at com.cloud.serializer.GsonHelperTest.testGsonDateFormatDeserializationWithDifferentDateFormat(GsonHelperTest.java:113)
669+
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
670+
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
671+
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
672+
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
673+
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
674+
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
675+
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
676+
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
677+
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
678+
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
679+
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
680+
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
681+
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
682+
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
683+
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
684+
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
685+
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
686+
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
687+
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
688+
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
689+
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
690+
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
691+
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
692+
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
693+
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
694+
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
695+
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:231)
696+
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
697+
Caused by: java.text.ParseException: Unparseable date: "22/08/2025T15:39:43+0000"
698+
at java.base/java.text.DateFormat.parse(DateFormat.java:395)
699+
at com.google.gson.DefaultTypeAdapters$DefaultDateTypeAdapter.deserializeToDate(DefaultTypeAdapters.java:374)
700+
... 42 more
701+
*/
702+
}
703+
704+
private static class TestClass {
705+
private String str;
706+
private int num;
707+
private Date date;
708+
709+
public TestClass(String str, int num, Date date) {
710+
this.str = str;
711+
this.num = num;
712+
this.date = date;
713+
}
714+
715+
public String getStr() {
716+
return str;
717+
}
718+
719+
public int getNum() {
720+
return num;
721+
}
722+
723+
public Date getDate() {
724+
return date;
725+
}
726+
}
615727
}

0 commit comments

Comments
 (0)