Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public void setManagementServerRunId(long managementServerRunId) {
}

@Override
public Date getCollectionTime(){
public Date getCollectionTime() {
return collectionTime;
}

Expand Down
13 changes: 8 additions & 5 deletions server/src/main/java/com/cloud/server/StatsCollector.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@

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.DataStoreManager;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider;
Expand Down Expand Up @@ -170,10 +171,10 @@
import com.codahale.metrics.jvm.MemoryUsageGaugeSet;
import com.codahale.metrics.jvm.ThreadStatesGaugeSet;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import com.sun.management.OperatingSystemMXBean;

/**
Expand Down Expand Up @@ -294,6 +295,9 @@ public String toString() {
private static StatsCollector s_instance = null;

private static Gson gson = new Gson();
private static Gson msStatsGson = new GsonBuilder()
.setDateFormat(DateUtil.ZONED_DATETIME_FORMAT)
.create();

private ScheduledExecutorService _executor = null;
@Inject
Expand Down Expand Up @@ -739,7 +743,6 @@ private void getDynamicDataFromDB() {
dbStats.put(uptime, (Long.valueOf(stats.get(uptime))));
}


@Override
protected Point createInfluxDbPoint(Object metricsObject) {
return null;
Expand All @@ -759,7 +762,7 @@ protected void runInContext() {
hostStatsEntry = getDataFrom(mshost);
managementServerHostStats.put(mshost.getUuid(), hostStatsEntry);
// send to other hosts
clusterManager.publishStatus(gson.toJson(hostStatsEntry));
clusterManager.publishStatus(msStatsGson.toJson(hostStatsEntry));
} catch (Throwable t) {
// pokemon catch to make sure the thread stays running
logger.error("Error trying to retrieve management server host statistics", t);
Expand Down Expand Up @@ -1158,9 +1161,9 @@ public String newStatus(ClusterServicePdu pdu) {
logger.debug(String.format("StatusUpdate from %s, json: %s", pdu.getSourcePeer(), pdu.getJsonPackage()));
}

ManagementServerHostStatsEntry hostStatsEntry = null;
ManagementServerHostStatsEntry hostStatsEntry;
try {
hostStatsEntry = gson.fromJson(pdu.getJsonPackage(),new TypeToken<ManagementServerHostStatsEntry>(){}.getType());
hostStatsEntry = msStatsGson.fromJson(pdu.getJsonPackage(), ManagementServerHostStatsEntry.class);
managementServerHostStats.put(hostStatsEntry.getManagementServerHostUuid(), hostStatsEntry);

// Update peer state to Up in mshost_peer
Expand Down
112 changes: 112 additions & 0 deletions server/src/test/java/com/cloud/server/StatsCollectorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@

import static org.mockito.Mockito.when;

import java.lang.reflect.Field;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
Expand All @@ -33,6 +35,8 @@
import java.util.concurrent.ConcurrentHashMap;
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.storage.datastore.db.StoragePoolVO;
import org.apache.commons.collections.CollectionUtils;
Expand Down Expand Up @@ -116,6 +120,8 @@ public class StatsCollectorTest {

private static Gson gson = new Gson();

private Gson msStatsGson;

private MockedStatic<InfluxDBFactory> influxDBFactoryMocked;

private AutoCloseable closeable;
Expand All @@ -125,6 +131,9 @@ public void setUp() throws Exception {
closeable = MockitoAnnotations.openMocks(this);
statsCollector.vmStatsDao = vmStatsDaoMock;
statsCollector.volumeStatsDao = volumeStatsDao;
Field msStatsGsonField = StatsCollector.class.getDeclaredField("msStatsGson");
msStatsGsonField.setAccessible(true);
msStatsGson = (Gson) msStatsGsonField.get(null);
}

@After
Expand Down Expand Up @@ -612,4 +621,107 @@ public void testPoolNeedsIopsStatsUpdating_NullIops() {
Mockito.verify(mockPool, Mockito.never()).setCapacityIops(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;
}
}
}
2 changes: 1 addition & 1 deletion utils/src/main/java/com/cloud/utils/DateUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public class DateUtil {

public static final TimeZone GMT_TIMEZONE = TimeZone.getTimeZone("GMT");
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 DateTimeFormatter[] parseFormats = new DateTimeFormatter[]{
Expand Down
Loading