Skip to content

Commit

Permalink
GEODE-10301: support LocalDate and JodaTime (apache#7737)
Browse files Browse the repository at this point in the history
* GEODE-10301: support LocalDate and JodaTime

Co-authored-by: Jinmei Liao <[email protected]>

- include libraries so that end-users won't have to add them to the java
  path
- ensure proper serialization in gfsh and pulse
  • Loading branch information
jmelchio authored Jun 3, 2022
1 parent 495f3b0 commit e370d2f
Show file tree
Hide file tree
Showing 20 changed files with 129 additions and 27 deletions.
2 changes: 1 addition & 1 deletion boms/geode-all-bom/src/test/resources/expected-pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.9</version>
<version>2.10.14</version>
</dependency>
<dependency>
<groupId>junit</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class DependencyConstraints {
api(group: 'javax.resource', name: 'javax.resource-api', version: '1.7.1')
api(group: 'javax.servlet', name: 'javax.servlet-api', version: '3.1.0')
api(group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.1')
api(group: 'joda-time', name: 'joda-time', version: '2.10.9')
api(group: 'joda-time', name: 'joda-time', version: '2.10.14')
api(group: 'junit', name: 'junit', version: get('junit.version'))
api(group: 'mx4j', name: 'mx4j-tools', version: '3.0.1')
api(group: 'mysql', name: 'mysql-connector-java', version: '5.1.46')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -123,18 +124,18 @@ locatorPort, httpPort, jmxPort, getSslParameters()),
.withName("startCluster")
.execute(gfshRule);

int expectedReturnCode = 0;
assertThat(startCluster.getProcess().exitValue())
.as("Cluster did not start correctly")
.isEqualTo(0);
.as("Cluster did not start correctly").isEqualTo(expectedReturnCode);

Process process = launchClientProcess(outputJar, httpPort);

boolean exited = process.waitFor(getTimeout().toMillis(), MILLISECONDS);
assertThat(exited)
.as("Process did not exit within 10 seconds")
long processTimeout = getTimeout().getSeconds();
boolean exited = process.waitFor(processTimeout, TimeUnit.SECONDS);
assertThat(exited).as(String.format("Process did not exit within %d seconds", processTimeout))
.isTrue();
assertThat(process.exitValue())
.as("Process did not exit with 0 return code")
.as(String.format("Process did not exit with %d return code", expectedReturnCode))
.isEqualTo(0);

GfshExecution listRegionsResult = GfshScript
Expand Down Expand Up @@ -162,6 +163,9 @@ private Process launchClientProcess(File outputJar, int httpPort) throws IOExcep
"jackson-annotations",
"jackson-core",
"jackson-databind",
"jackson-datatype-jsr310",
"jackson-datatype-joda",
"joda-time",
"httpclient",
"httpcore",
"spring-beans",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1070,3 +1070,6 @@ tools/Modules/Apache_Geode_Modules-0.0.0-Tomcat.zip
tools/Modules/Apache_Geode_Modules-0.0.0-tcServer.zip
tools/Modules/Apache_Geode_Modules-0.0.0-tcServer30.zip
tools/Pulse/geode-pulse-0.0.0.war
lib/jackson-datatype-joda-2.13.2.jar
lib/jackson-datatype-jsr310-2.13.2.jar
lib/joda-time-2.10.14.jar
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ jackson-annotations
jackson-core
jackson-databind
jackson-dataformat-yaml
jackson-datatype-joda
jackson-datatype-jsr
jakarta.activation-api
jakarta.validation-api
Expand All @@ -51,6 +52,7 @@ jgroups
jline
jna
jna-platform
joda-time
jopt-simple
json-path
json-smart
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,6 @@ jetty-http-9.4.46.v20220331.jar
jetty-io-9.4.46.v20220331.jar
jetty-util-ajax-9.4.46.v20220331.jar
jetty-util-9.4.46.v20220331.jar
jackson-datatype-joda-2.13.2.jar
jackson-datatype-jsr310-2.13.2.jar
joda-time-2.10.14.jar
2 changes: 2 additions & 0 deletions geode-common/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ dependencies {


implementation('com.fasterxml.jackson.core:jackson-databind')
implementation('com.fasterxml.jackson.datatype:jackson-datatype-jsr310')
implementation('com.fasterxml.jackson.datatype:jackson-datatype-joda')

// test
testImplementation('com.github.stefanbirkner:system-rules') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.datatype.joda.JodaModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

/**
* helper class for creating various json mappers used by Geode Project
Expand All @@ -34,13 +36,21 @@ public class GeodeJsonMapper {
*/
public static ObjectMapper getMapper() {
ObjectMapper mapper = JsonMapper.builder()
.addModule(new JavaTimeModule())
.addModule(new JodaModule())
.enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES)
.enable(MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL)
.build();
mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
return mapper;
}

public static ObjectMapper getMapperWithAlwaysInclusion() {
ObjectMapper mapper = getMapper();
mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
return mapper;
}

public static ObjectMapper getMapperIgnoringUnknownProperties() {
ObjectMapper mapper = getMapper();
mapper.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
Expand Down
10 changes: 10 additions & 0 deletions geode-common/src/test/resources/expected-pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,15 @@
<artifactId>jackson-databind</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-joda</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import java.time.LocalDate;
import java.util.Date;

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
Expand Down Expand Up @@ -63,10 +65,12 @@ public class DistributedSystemMBeanIntegrationTest {
private static Date date;
private static java.sql.Date sqlDate;
private static LocalDate localDate;
private static DateTime jodaDateTime;

@BeforeClass
public static void setupClass() throws Exception {
Region<Object, Object> testRegion = server.getCache().getRegion("testRegion");
jodaDateTime = new DateTime(2020, 1, 1, 1, 1, DateTimeZone.UTC);
localDate = LocalDate.of(2020, 1, 1);
sqlDate = java.sql.Date.valueOf(localDate);
date = new Date(sqlDate.getTime());
Expand All @@ -77,6 +81,7 @@ public static void setupClass() throws Exception {
employee.setStartDate(date);
employee.setEndDate(sqlDate);
employee.setBirthday(localDate);
employee.setAnniversary(jodaDateTime);
testRegion.put(1, employee);
}

Expand All @@ -100,7 +105,8 @@ public void queryAllUsingMBean() throws Exception {
.doesNotContain("Job Title")
.contains("\"java.util.Date\",\"" + dateString + "\"")
.contains("\"java.sql.Date\",\"" + dateString + "\"")
.contains("\"java.time.LocalDate\",\"2020-01-01\"");
.contains("\"java.time.LocalDate\",\"2020-01-01\"")
.contains("\"org.joda.time.DateTime\",\"2020-01-01T01:01:00.000Z\"");
}

@Test
Expand All @@ -125,14 +131,28 @@ public void queryAllUsingGfshDoesNotFormatDate() throws Exception {
}

// this is simply to document the current behavior of gfsh
// gfsh refused to format the date objects as of jackson 2.12's fix#2683
// code changes made to enable jsr310
@Test
public void queryAllUsingGfshRefusesToFormatLocalDate() throws Exception {
public void queryAllUsingGfshAbleToFormatLocalDate() throws Exception {
gfsh.connectAndVerify(server.getJmxPort(), GfshCommandRule.PortType.jmxManager);
gfsh.executeAndAssertThat("query --query='" + SELECT_ALL + "'")
.statusIsError()
.containsOutput(
"Java 8 date/time type `java.time.LocalDate` not supported by default: add Module \"com.fasterxml.jackson.datatype:jackson-datatype-jsr310\"");
TabularResultModelAssert tabularResultModelAssert =
gfsh.executeAndAssertThat("query --query='" + SELECT_ALL + "'")
.statusIsSuccess()
.hasTableSection();
tabularResultModelAssert.hasColumn("birthday").containsExactly("[2020,1,1]");
}

// this is simply to document the current behavior of gfsh
// code changes made to enable Joda time
@Test
public void queryAllUsingGfshAbleToParseJodaDateTime() throws Exception {
gfsh.connectAndVerify(server.getJmxPort(), GfshCommandRule.PortType.jmxManager);
TabularResultModelAssert tabularResultModelAssert =
gfsh.executeAndAssertThat("query --query='" + SELECT_ALL + "'")
.statusIsSuccess()
.hasTableSection();
tabularResultModelAssert.hasColumn("anniversary")
.containsExactly(jodaDateTime.getMillis() + "");
}

@Test
Expand All @@ -142,6 +162,7 @@ public void queryFieldsUsingGfshDoesNotHonorAnnotations() throws Exception {
.statusIsSuccess()
.hasTableSection().hasColumns().asList()
.containsExactlyInAnyOrder("id", "title");

}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
package org.apache.geode.management.internal.json;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.InstanceOfAssertFactories.map;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
Expand All @@ -31,7 +30,8 @@

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import org.joda.time.DateTime;
import org.junit.jupiter.api.Test;

import org.apache.geode.cache.query.data.CollectionHolder;
import org.apache.geode.internal.logging.DateFormatter;
Expand Down Expand Up @@ -138,6 +138,12 @@ public void testDateTimes() throws Exception {
new QueryResultFormatter(100).add(RESULT, sqlDate);
checkResult(sqlDateResult,
"{\"result\":[[\"java.sql.Date\",\"" + expectedString + "\"]]}");

DateTime jodaTime = new DateTime(time);
QueryResultFormatter jodaTimeResult =
new QueryResultFormatter(100).add(RESULT, jodaTime);
String jsonString = jodaTimeResult.toString();
assertThat(jsonString).contains("{\"result\":[[\"org.joda.time.DateTime\",\"");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import com.google.common.io.Files;
import org.apache.commons.io.FileUtils;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
Expand Down Expand Up @@ -200,7 +201,8 @@ public void canOutputComplexRegionToFile() throws Exception {
@Test
public void outputDisplaysResultsFromComplexRegion() throws Exception {
String result = gfsh
.execute("query --query='select c.name, c.address from " + SEPARATOR + "complexRegion c'");
.execute("query --query='select c.name, c.address, c.birthday from " + SEPARATOR
+ "complexRegion c'");

String[] resultLines = splitOnLineBreaks(result);

Expand All @@ -209,7 +211,7 @@ public void outputDisplaysResultsFromComplexRegion() throws Exception {
assertThat(resultLines[2]).containsPattern("Rows\\s+:\\s+100");
assertThat(resultLines[3]).containsPattern("name\\s+\\|\\s+address");
Arrays.asList(resultLines).subList(5, resultLines.length)
.forEach(line -> assertThat(line).matches("name\\d+.*\"city\":\"Hometown\".*"));
.forEach(line -> assertThat(line).matches("name\\d+.*\"city\":\"Hometown\".*\\d*"));
}

@Test
Expand Down Expand Up @@ -275,14 +277,17 @@ private String[] splitOnLineBreaks(String multilineString) {
public static class Customer implements Serializable {
public String name;
public Address address;
public DateTime birthday;


public Customer(String name, String street, String city) {
this.name = name;
address = new Address(street, city);
this.address = new Address(street, city);
this.birthday = new DateTime();
}

public String toString() {
return name + address;
return name + address + birthday;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ org/apache/geode/management/ManagementTestBase$3,false,this$0:org/apache/geode/m
org/apache/geode/management/internal/cli/commands/ExportLogsDistributedTestBase$LogLine,false,level:java/lang/String,message:java/lang/String,shouldBeIgnoredDueToTimestamp:boolean
org/apache/geode/management/internal/cli/commands/Product,false,contractSize:java/lang/String,productCodes:java/util/TreeMap,productID:java/lang/Long,status:java/lang/String
org/apache/geode/management/internal/cli/commands/QueryCommandIntegrationTestBase$Address,false,city:java/lang/String,street:java/lang/String
org/apache/geode/management/internal/cli/commands/QueryCommandIntegrationTestBase$Customer,false,address:org/apache/geode/management/internal/cli/commands/QueryCommandIntegrationTestBase$Address,name:java/lang/String
org/apache/geode/management/internal/cli/commands/QueryCommandIntegrationTestBase$Customer,false,address:org/apache/geode/management/internal/cli/commands/QueryCommandIntegrationTestBase$Address,birthday:org/joda/time/DateTime,name:java/lang/String
org/apache/geode/management/internal/cli/commands/ShowDeadlockDistributedTestBase$LockFunction,false
org/apache/geode/management/internal/cli/commands/ShowLogCommandDistributedTestBase,false
org/apache/geode/management/internal/configuration/ClusterConfig,false,groups:java/util/List
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.util.HashMap;
import java.util.Map;

import org.joda.time.DateTime;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
Expand Down Expand Up @@ -100,7 +101,11 @@ public static void beforeClass() throws Exception {

@Test
public void get() throws Exception {
gfsh.executeAndAssertThat("get --region=Users --key=jonbloom").statusIsSuccess();
gfsh.executeAndAssertThat("get --region=Users --key=jonbloom")
.statusIsSuccess()
.hasDataSection(DataCommandResult.DATA_INFO_SECTION)
.hasContent()
.hasEntrySatisfying("Value", v -> v.contains("\"startTime\":1653595626520"));
}

@Test
Expand Down Expand Up @@ -205,16 +210,28 @@ public void getOnCacheMissForStringRegion() throws Exception {

private static class User implements Serializable {
private final String username;
private final DateTime startTime;

public User(final String username) {
assert username != null : "The username cannot be null!";
this.username = username;
this.startTime = new DateTime();
}

public User(final String username, DateTime startTime) {
assert username != null : "The username cannot be null!";
this.username = username;
this.startTime = startTime;
}

public String getUsername() {
return username;
}

public DateTime getStartTime() {
return startTime;
}

public String getHashcode() {
StringBuilder sb = new StringBuilder(username.getBytes().length * 2);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.apache.geode.management.internal.i18n.CliStrings;
import org.apache.geode.pdx.JSONFormatter;
import org.apache.geode.pdx.PdxInstance;
import org.apache.geode.util.internal.GeodeJsonMapper;


/**
Expand Down Expand Up @@ -691,7 +692,7 @@ private void resolveObjectToColumns(Map<String, String> columnData, Object value
} else if (value instanceof UUID) {
columnData.put("Result", valueToJson(value));
} else {
ObjectMapper mapper = new ObjectMapper();
ObjectMapper mapper = GeodeJsonMapper.getMapperWithAlwaysInclusion();
JsonNode node = mapper.valueToTree(value);

node.fieldNames().forEachRemaining(field -> {
Expand Down Expand Up @@ -729,7 +730,7 @@ private String valueToJson(Object value) {
return JSONFormatter.toJSON((PdxInstance) value);
}

ObjectMapper mapper = new ObjectMapper();
ObjectMapper mapper = GeodeJsonMapper.getMapperWithAlwaysInclusion();
try {
return mapper.writeValueAsString(value);
} catch (JsonProcessingException jex) {
Expand Down
Loading

0 comments on commit e370d2f

Please sign in to comment.