From c2ae78dbba263876c5d5b4521401ab6439b1b998 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 19 Mar 2020 15:05:44 -0700 Subject: [PATCH] Fix Joda compatibility in stream protocol (#53823) The JodaCompatibleZonedDateTime is a compatibility object that unions Joda's DateTime and Java's ZonedDateTime, meant for use in scripts. When it was added, we serialized the JCZDT as a Joda DateTime so that when sending to older nodes they could still read the object. However, on newer nodes, we continued also reading this as a Joda DateTime. This commit changes the read side to form a JCZDT. closes #53586 --- .../elasticsearch/common/io/stream/StreamInput.java | 12 ++++++++---- .../common/io/stream/BytesStreamsTests.java | 10 ++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java b/server/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java index 300b42153dc47..d30b226dfb2dc 100644 --- a/server/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java +++ b/server/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java @@ -38,9 +38,10 @@ import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.text.Text; +import org.elasticsearch.common.time.DateUtils; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException; -import org.joda.time.DateTime; +import org.elasticsearch.script.JodaCompatibleZonedDateTime; import org.joda.time.DateTimeZone; import java.io.ByteArrayInputStream; @@ -768,9 +769,12 @@ private List readArrayList() throws IOException { return list; } - private DateTime readDateTime() throws IOException { - final String timeZoneId = readString(); - return new DateTime(readLong(), DateTimeZone.forID(timeZoneId)); + private JodaCompatibleZonedDateTime readDateTime() throws IOException { + // we reuse DateTime to communicate with older nodes that don't know about the joda compat layer, but + // here we are on a new node so we always want a compat datetime + final ZoneId zoneId = DateUtils.dateTimeZoneToZoneId(DateTimeZone.forID(readString())); + long millis = readLong(); + return new JodaCompatibleZonedDateTime(Instant.ofEpochMilli(millis), zoneId); } private ZonedDateTime readZonedDateTime() throws IOException { diff --git a/server/src/test/java/org/elasticsearch/common/io/stream/BytesStreamsTests.java b/server/src/test/java/org/elasticsearch/common/io/stream/BytesStreamsTests.java index aad01d56f2496..94e2801df8cee 100644 --- a/server/src/test/java/org/elasticsearch/common/io/stream/BytesStreamsTests.java +++ b/server/src/test/java/org/elasticsearch/common/io/stream/BytesStreamsTests.java @@ -27,11 +27,14 @@ import org.elasticsearch.common.lucene.BytesRefs; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.PageCacheRecycler; +import org.elasticsearch.script.JodaCompatibleZonedDateTime; import org.elasticsearch.test.ESTestCase; +import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import java.io.EOFException; import java.io.IOException; +import java.time.ZoneId; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -49,6 +52,7 @@ import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; @@ -303,6 +307,7 @@ public void testSimpleStreams() throws Exception { out.writeTimeZone(DateTimeZone.forID("CET")); out.writeOptionalTimeZone(DateTimeZone.getDefault()); out.writeOptionalTimeZone(null); + out.writeGenericValue(new DateTime(123456, DateTimeZone.forID("America/Los_Angeles"))); final byte[] bytes = BytesReference.toBytes(out.bytes()); StreamInput in = StreamInput.wrap(BytesReference.toBytes(out.bytes())); assertEquals(in.available(), bytes.length); @@ -335,6 +340,11 @@ public void testSimpleStreams() throws Exception { assertEquals(DateTimeZone.forID("CET"), in.readTimeZone()); assertEquals(DateTimeZone.getDefault(), in.readOptionalTimeZone()); assertNull(in.readOptionalTimeZone()); + Object dt = in.readGenericValue(); + assertThat(dt, instanceOf(JodaCompatibleZonedDateTime.class)); + JodaCompatibleZonedDateTime jdt = (JodaCompatibleZonedDateTime) dt; + assertThat(jdt.getZonedDateTime().toInstant().toEpochMilli(), equalTo(123456L)); + assertThat(jdt.getZonedDateTime().getZone(), equalTo(ZoneId.of("America/Los_Angeles"))); assertEquals(0, in.available()); in.close(); out.close();