diff --git a/jdk/src/share/classes/java/time/Duration.java b/jdk/src/share/classes/java/time/Duration.java index ab7da66f83a..ce2ba7781b0 100644 --- a/jdk/src/share/classes/java/time/Duration.java +++ b/jdk/src/share/classes/java/time/Duration.java @@ -74,7 +74,7 @@ import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.io.InvalidObjectException; -import java.io.ObjectStreamException; +import java.io.InvalidObjectException; import java.io.Serializable; import java.math.BigDecimal; import java.math.BigInteger; @@ -1299,8 +1299,9 @@ public final class Duration /** * Writes the object using a * dedicated serialized form. + * @serialData *
- * out.writeByte(1); // identifies this as a Duration
+ * out.writeByte(1); // identifies a Duration
* out.writeLong(seconds);
* out.writeInt(nanos);
*
@@ -1316,7 +1317,7 @@ public final class Duration
* @return never
* @throws InvalidObjectException always
*/
- private Object readResolve() throws ObjectStreamException {
+ private Object readResolve() throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
diff --git a/jdk/src/share/classes/java/time/Instant.java b/jdk/src/share/classes/java/time/Instant.java
index aeecdbed524..9d74e29f91a 100644
--- a/jdk/src/share/classes/java/time/Instant.java
+++ b/jdk/src/share/classes/java/time/Instant.java
@@ -76,7 +76,7 @@ import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InvalidObjectException;
-import java.io.ObjectStreamException;
+import java.io.InvalidObjectException;
import java.io.Serializable;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
@@ -1317,8 +1317,9 @@ public final class Instant
/**
* Writes the object using a
* dedicated serialized form.
+ * @serialData
*
- * out.writeByte(2); // identifies this as an Instant
+ * out.writeByte(2); // identifies an Instant
* out.writeLong(seconds);
* out.writeInt(nanos);
*
@@ -1334,7 +1335,7 @@ public final class Instant
* @return never
* @throws InvalidObjectException always
*/
- private Object readResolve() throws ObjectStreamException {
+ private Object readResolve() throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
diff --git a/jdk/src/share/classes/java/time/LocalDate.java b/jdk/src/share/classes/java/time/LocalDate.java
index d96f5b1fd1e..3005658366a 100644
--- a/jdk/src/share/classes/java/time/LocalDate.java
+++ b/jdk/src/share/classes/java/time/LocalDate.java
@@ -78,7 +78,7 @@ import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InvalidObjectException;
-import java.io.ObjectStreamException;
+import java.io.InvalidObjectException;
import java.io.Serializable;
import java.time.chrono.ChronoLocalDate;
import java.time.chrono.Era;
@@ -2019,8 +2019,9 @@ public final class LocalDate
/**
* Writes the object using a
* dedicated serialized form.
+ * @serialData
*
- * out.writeByte(3); // identifies this as a LocalDate
+ * out.writeByte(3); // identifies a LocalDate
* out.writeInt(year);
* out.writeByte(month);
* out.writeByte(day);
@@ -2037,7 +2038,7 @@ public final class LocalDate
* @return never
* @throws InvalidObjectException always
*/
- private Object readResolve() throws ObjectStreamException {
+ private Object readResolve() throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
diff --git a/jdk/src/share/classes/java/time/LocalDateTime.java b/jdk/src/share/classes/java/time/LocalDateTime.java
index d68d6f52537..de8b246d6bb 100644
--- a/jdk/src/share/classes/java/time/LocalDateTime.java
+++ b/jdk/src/share/classes/java/time/LocalDateTime.java
@@ -76,7 +76,7 @@ import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InvalidObjectException;
-import java.io.ObjectStreamException;
+import java.io.InvalidObjectException;
import java.io.Serializable;
import java.time.chrono.ChronoLocalDateTime;
import java.time.format.DateTimeFormatter;
@@ -1953,8 +1953,9 @@ public final class LocalDateTime
/**
* Writes the object using a
* dedicated serialized form.
+ * @serialData
*
- * out.writeByte(5); // identifies this as a LocalDateTime
+ * out.writeByte(5); // identifies a LocalDateTime
* // the date excluding the one byte header
* // the time excluding the one byte header
*
@@ -1970,7 +1971,7 @@ public final class LocalDateTime
* @return never
* @throws InvalidObjectException always
*/
- private Object readResolve() throws ObjectStreamException {
+ private Object readResolve() throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
diff --git a/jdk/src/share/classes/java/time/LocalTime.java b/jdk/src/share/classes/java/time/LocalTime.java
index 2bace6e7bc9..a6270db19cf 100644
--- a/jdk/src/share/classes/java/time/LocalTime.java
+++ b/jdk/src/share/classes/java/time/LocalTime.java
@@ -74,7 +74,7 @@ import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InvalidObjectException;
-import java.io.ObjectStreamException;
+import java.io.InvalidObjectException;
import java.io.Serializable;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
@@ -1595,8 +1595,11 @@ public final class LocalTime
/**
* Writes the object using a
* dedicated serialized form.
+ * @serialData
+ * A twos-complement value indicates the remaining values are not in the stream
+ * and should be set to zero.
*
- * out.writeByte(4); // identifies this as a LocalTime
+ * out.writeByte(4); // identifies a LocalTime
* if (nano == 0) {
* if (second == 0) {
* if (minute == 0) {
@@ -1629,7 +1632,7 @@ public final class LocalTime
* @return never
* @throws InvalidObjectException always
*/
- private Object readResolve() throws ObjectStreamException {
+ private Object readResolve() throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
diff --git a/jdk/src/share/classes/java/time/MonthDay.java b/jdk/src/share/classes/java/time/MonthDay.java
index 06aa0437af8..22807822f2b 100644
--- a/jdk/src/share/classes/java/time/MonthDay.java
+++ b/jdk/src/share/classes/java/time/MonthDay.java
@@ -68,7 +68,7 @@ import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InvalidObjectException;
-import java.io.ObjectStreamException;
+import java.io.InvalidObjectException;
import java.io.Serializable;
import java.time.chrono.Chronology;
import java.time.chrono.IsoChronology;
@@ -744,9 +744,10 @@ public final class MonthDay
//-----------------------------------------------------------------------
/**
* Writes the object using a
- * dedicated serialized form.
+ * dedicated serialized form.
+ * @serialData
*
- * out.writeByte(13); // identifies this as a MonthDay
+ * out.writeByte(13); // identifies a MonthDay
* out.writeByte(month);
* out.writeByte(day);
*
@@ -762,7 +763,7 @@ public final class MonthDay
* @return never
* @throws InvalidObjectException always
*/
- private Object readResolve() throws ObjectStreamException {
+ private Object readResolve() throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
diff --git a/jdk/src/share/classes/java/time/OffsetDateTime.java b/jdk/src/share/classes/java/time/OffsetDateTime.java
index 5641154cf3e..f894e53e46f 100644
--- a/jdk/src/share/classes/java/time/OffsetDateTime.java
+++ b/jdk/src/share/classes/java/time/OffsetDateTime.java
@@ -72,7 +72,7 @@ import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
-import java.io.ObjectStreamException;
+import java.io.InvalidObjectException;
import java.io.Serializable;
import java.time.chrono.IsoChronology;
import java.time.format.DateTimeFormatter;
@@ -1901,9 +1901,10 @@ public final class OffsetDateTime
//-----------------------------------------------------------------------
/**
* Writes the object using a
- * dedicated serialized form.
+ * dedicated serialized form.
+ * @serialData
*
- * out.writeByte(10); // identifies this as a OffsetDateTime
+ * out.writeByte(10); // identifies a OffsetDateTime
* out.writeObject(dateTime);
* out.writeObject(offset);
*
@@ -1919,7 +1920,7 @@ public final class OffsetDateTime
* @return never
* @throws InvalidObjectException always
*/
- private Object readResolve() throws ObjectStreamException {
+ private Object readResolve() throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
diff --git a/jdk/src/share/classes/java/time/OffsetTime.java b/jdk/src/share/classes/java/time/OffsetTime.java
index 2872cff4b26..6c67ef82bb1 100644
--- a/jdk/src/share/classes/java/time/OffsetTime.java
+++ b/jdk/src/share/classes/java/time/OffsetTime.java
@@ -73,7 +73,7 @@ import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
-import java.io.ObjectStreamException;
+import java.io.InvalidObjectException;
import java.io.Serializable;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
@@ -1372,9 +1372,10 @@ public final class OffsetTime
//-----------------------------------------------------------------------
/**
* Writes the object using a
- * dedicated serialized form.
+ * dedicated serialized form.
+ * @serialData
*
- * out.writeByte(9); // identifies this as a OffsetTime
+ * out.writeByte(9); // identifies a OffsetTime
* out.writeObject(time);
* out.writeObject(offset);
*
@@ -1390,7 +1391,7 @@ public final class OffsetTime
* @return never
* @throws InvalidObjectException always
*/
- private Object readResolve() throws ObjectStreamException {
+ private Object readResolve() throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
diff --git a/jdk/src/share/classes/java/time/Period.java b/jdk/src/share/classes/java/time/Period.java
index 45980d08740..161ce49e565 100644
--- a/jdk/src/share/classes/java/time/Period.java
+++ b/jdk/src/share/classes/java/time/Period.java
@@ -70,7 +70,7 @@ import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InvalidObjectException;
-import java.io.ObjectStreamException;
+import java.io.InvalidObjectException;
import java.io.Serializable;
import java.time.chrono.ChronoLocalDate;
import java.time.chrono.Chronology;
@@ -993,11 +993,12 @@ public final class Period
/**
* Writes the object using a
* dedicated serialized form.
+ * @serialData
*
- * out.writeByte(14); // identifies this as a Period
+ * out.writeByte(14); // identifies a Period
* out.writeInt(years);
* out.writeInt(months);
- * out.writeInt(seconds);
+ * out.writeInt(days);
*
*
* @return the instance of {@code Ser}, not null
@@ -1011,7 +1012,7 @@ public final class Period
* @return never
* @throws java.io.InvalidObjectException always
*/
- private Object readResolve() throws ObjectStreamException {
+ private Object readResolve() throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
diff --git a/jdk/src/share/classes/java/time/Ser.java b/jdk/src/share/classes/java/time/Ser.java
index 607198952c1..885b43abd83 100644
--- a/jdk/src/share/classes/java/time/Ser.java
+++ b/jdk/src/share/classes/java/time/Ser.java
@@ -72,14 +72,14 @@ import java.io.StreamCorruptedException;
* byte flag would be used in order to specify an alternative version of the type format.
* For example {@code LOCAL_DATE_TYPE_VERSION_2 = 21}.
*
- * In order to serialise the object it writes its byte and then calls back to the appropriate class where
- * the serialisation is performed. In order to deserialise the object it read in the type byte, switching
+ * In order to serialize the object it writes its byte and then calls back to the appropriate class where
+ * the serialization is performed. In order to deserialize the object it read in the type byte, switching
* in order to select which class to call back into.
*
- * The serialisation format is determined on a per class basis. In the case of field based classes each
+ * The serialization format is determined on a per class basis. In the case of field based classes each
* of the fields is written out with an appropriate size format in descending order of the field's size. For
* example in the case of {@link LocalDate} year is written before month. Composite classes, such as
- * {@link LocalDateTime} are serialised as one object.
+ * {@link LocalDateTime} are serialized as one object.
*
* This class is mutable and should be created once per serialization.
*
@@ -133,6 +133,27 @@ final class Ser implements Externalizable {
//-----------------------------------------------------------------------
/**
* Implements the {@code Externalizable} interface to write the object.
+ * @serialData
+ *
+ * Each serializable class is mapped to a type that is the first byte
+ * in the stream. Refer to each class {@code writeReplace}
+ * serialized form for the value of the type and sequence of values for the type.
+ *
- * out.writeByte(11); // identifies this as a Year
+ * out.writeByte(11); // identifies a Year
* out.writeInt(year);
*
*
@@ -1097,7 +1098,7 @@ public final class Year
* @return never
* @throws InvalidObjectException always
*/
- private Object readResolve() throws ObjectStreamException {
+ private Object readResolve() throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
diff --git a/jdk/src/share/classes/java/time/YearMonth.java b/jdk/src/share/classes/java/time/YearMonth.java
index 1d974095336..541676117eb 100644
--- a/jdk/src/share/classes/java/time/YearMonth.java
+++ b/jdk/src/share/classes/java/time/YearMonth.java
@@ -77,7 +77,7 @@ import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InvalidObjectException;
-import java.io.ObjectStreamException;
+import java.io.InvalidObjectException;
import java.io.Serializable;
import java.time.chrono.Chronology;
import java.time.chrono.IsoChronology;
@@ -1205,9 +1205,10 @@ public final class YearMonth
//-----------------------------------------------------------------------
/**
* Writes the object using a
- * dedicated serialized form.
+ * dedicated serialized form.
+ * @serialData
*
- * out.writeByte(12); // identifies this as a YearMonth
+ * out.writeByte(12); // identifies a YearMonth
* out.writeInt(year);
* out.writeByte(month);
*
@@ -1223,7 +1224,7 @@ public final class YearMonth
* @return never
* @throws InvalidObjectException always
*/
- private Object readResolve() throws ObjectStreamException {
+ private Object readResolve() throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
diff --git a/jdk/src/share/classes/java/time/ZoneId.java b/jdk/src/share/classes/java/time/ZoneId.java
index dcde85ae884..86b0c74fd9d 100644
--- a/jdk/src/share/classes/java/time/ZoneId.java
+++ b/jdk/src/share/classes/java/time/ZoneId.java
@@ -63,6 +63,7 @@ package java.time;
import java.io.DataOutput;
import java.io.IOException;
+import java.io.InvalidObjectException;
import java.io.Serializable;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.TextStyle;
@@ -661,6 +662,15 @@ public abstract class ZoneId implements Serializable {
}
//-----------------------------------------------------------------------
+ /**
+ * Defend against malicious streams.
+ * @return never
+ * @throws InvalidObjectException always
+ */
+ private Object readResolve() throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
/**
* Outputs this zone as a {@code String}, using the ID.
*
@@ -675,9 +685,10 @@ public abstract class ZoneId implements Serializable {
/**
* Writes the object using a
* dedicated serialized form.
+ * @serialData
*
- * out.writeByte(7); // identifies this as a ZoneId (not ZoneOffset)
- * out.writeUTF(zoneId);
+ * out.writeByte(7); // identifies a ZoneId (not ZoneOffset)
+ * out.writeUTF(getId());
*
* * When read back in, the {@code ZoneId} will be created as though using diff --git a/jdk/src/share/classes/java/time/ZoneOffset.java b/jdk/src/share/classes/java/time/ZoneOffset.java index c5e4d056ee8..2d63a978d8c 100644 --- a/jdk/src/share/classes/java/time/ZoneOffset.java +++ b/jdk/src/share/classes/java/time/ZoneOffset.java @@ -70,7 +70,7 @@ import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.io.InvalidObjectException; -import java.io.ObjectStreamException; +import java.io.InvalidObjectException; import java.io.Serializable; import java.time.temporal.ChronoField; import java.time.temporal.Temporal; @@ -740,12 +740,13 @@ public final class ZoneOffset /** * Writes the object using a * dedicated serialized form. + * @serialData *
- * out.writeByte(8); // identifies this as a ZoneOffset
+ * out.writeByte(8); // identifies a ZoneOffset
* int offsetByte = totalSeconds % 900 == 0 ? totalSeconds / 900 : 127;
* out.writeByte(offsetByte);
* if (offsetByte == 127) {
- * out.writeInt(totalSeconds);
+ * out.writeInt(totalSeconds);
* }
*
*
@@ -760,7 +761,7 @@ public final class ZoneOffset
* @return never
* @throws InvalidObjectException always
*/
- private Object readResolve() throws ObjectStreamException {
+ private Object readResolve() throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
diff --git a/jdk/src/share/classes/java/time/ZoneRegion.java b/jdk/src/share/classes/java/time/ZoneRegion.java
index 66d30709d98..31669d79c03 100644
--- a/jdk/src/share/classes/java/time/ZoneRegion.java
+++ b/jdk/src/share/classes/java/time/ZoneRegion.java
@@ -60,7 +60,7 @@ import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InvalidObjectException;
-import java.io.ObjectStreamException;
+import java.io.InvalidObjectException;
import java.io.Serializable;
import java.time.zone.ZoneRules;
import java.time.zone.ZoneRulesException;
@@ -181,8 +181,9 @@ final class ZoneRegion extends ZoneId implements Serializable {
/**
* Writes the object using a
* dedicated serialized form.
+ * @serialData
*
- * out.writeByte(7); // identifies this as a ZoneId (not ZoneOffset)
+ * out.writeByte(7); // identifies a ZoneId (not ZoneOffset)
* out.writeUTF(zoneId);
*
*
@@ -197,7 +198,7 @@ final class ZoneRegion extends ZoneId implements Serializable {
* @return never
* @throws InvalidObjectException always
*/
- private Object readResolve() throws ObjectStreamException {
+ private Object readResolve() throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
diff --git a/jdk/src/share/classes/java/time/ZonedDateTime.java b/jdk/src/share/classes/java/time/ZonedDateTime.java
index 151470ecf93..251ca888a14 100644
--- a/jdk/src/share/classes/java/time/ZonedDateTime.java
+++ b/jdk/src/share/classes/java/time/ZonedDateTime.java
@@ -69,7 +69,7 @@ import java.io.DataOutput;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInput;
-import java.io.ObjectStreamException;
+import java.io.InvalidObjectException;
import java.io.Serializable;
import java.time.chrono.ChronoZonedDateTime;
import java.time.format.DateTimeFormatter;
@@ -2192,9 +2192,10 @@ public final class ZonedDateTime
/**
* Writes the object using a
* dedicated serialized form.
+ * @serialData
*
- * out.writeByte(6); // identifies this as a ZonedDateTime
- * // the date-time excluding the one byte header
+ * out.writeByte(6); // identifies a ZonedDateTime
+ * // the dateTime excluding the one byte header
* // the offset excluding the one byte header
* // the zone ID excluding the one byte header
*
@@ -2210,7 +2211,7 @@ public final class ZonedDateTime
* @return never
* @throws InvalidObjectException always
*/
- private Object readResolve() throws ObjectStreamException {
+ private Object readResolve() throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
diff --git a/jdk/src/share/classes/java/time/chrono/ChronoLocalDateTimeImpl.java b/jdk/src/share/classes/java/time/chrono/ChronoLocalDateTimeImpl.java
index 14f7bd927c2..cd7f04e4798 100644
--- a/jdk/src/share/classes/java/time/chrono/ChronoLocalDateTimeImpl.java
+++ b/jdk/src/share/classes/java/time/chrono/ChronoLocalDateTimeImpl.java
@@ -94,7 +94,7 @@ import java.util.Objects;
*
* @implSpec
* This class is immutable and thread-safe.
- *
+ * @serial
* @param + * out.writeByte(2); // identifies a ChronoLocalDateTime + * out.writeObject(toLocalDate()); + * out.witeObject(toLocalTime()); + *+ * + * @return the instance of {@code Ser}, not null + */ private Object writeReplace() { return new Ser(Ser.CHRONO_LOCAL_DATE_TIME_TYPE, this); } @@ -411,7 +423,7 @@ final class ChronoLocalDateTimeImpl
+ * out.writeByte(3); // identifies a ChronoZonedDateTime + * out.writeObject(toLocalDateTime()); + * out.writeObject(getOffset()); + * out.writeObject(getZone()); + *+ * + * @return the instance of {@code Ser}, not null + */ private Object writeReplace() { return new Ser(Ser.CHRONO_ZONE_DATE_TIME_TYPE, this); } @@ -330,7 +347,7 @@ final class ChronoZonedDateTimeImpl
- * out.writeByte(1); // identifies this as a Chronology
+ * out.writeByte(1); // identifies a Chronology
* out.writeUTF(getId());
*
*
* @return the instance of {@code Ser}, not null
*/
- protected Object writeReplace() {
+ Object writeReplace() {
return new Ser(Ser.CHRONO_TYPE, this);
}
@@ -1274,14 +1275,26 @@ public abstract class Chronology implements Comparable+ * out.writeByte(1); // identifies a Chronology + * out.writeUTF(getId()); + *+ * + * @return the instance of {@code Ser}, not null + */ + @Override + Object writeReplace() { + return super.writeReplace(); + } + + /** + * Defend against malicious streams. + * @return never + * @throws InvalidObjectException always + */ + private Object readResolve() throws InvalidObjectException { + throw new InvalidObjectException("Deserialization via serialization delegate"); + } } diff --git a/jdk/src/share/classes/java/time/chrono/HijrahDate.java b/jdk/src/share/classes/java/time/chrono/HijrahDate.java index ef40b6dc948..8e385c9470b 100644 --- a/jdk/src/share/classes/java/time/chrono/HijrahDate.java +++ b/jdk/src/share/classes/java/time/chrono/HijrahDate.java @@ -65,6 +65,7 @@ import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.YEAR; import java.io.IOException; +import java.io.InvalidObjectException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.Serializable; @@ -118,7 +119,7 @@ public final class HijrahDate /** * The Chronology of this HijrahDate. */ - private final HijrahChronology chrono; + private final transient HijrahChronology chrono; /** * The proleptic year. */ @@ -600,29 +601,41 @@ public final class HijrahDate } //----------------------------------------------------------------------- + /** + * Defend against malicious streams. + * @return never + * @throws InvalidObjectException always + */ + private Object readResolve() throws InvalidObjectException { + throw new InvalidObjectException("Deserialization via serialization delegate"); + } + + /** + * Writes the object using a + * dedicated serialized form. + * @serialData + *
+ * out.writeByte(6); // identifies a HijrahDate + * out.writeObject(chrono); // the HijrahChronology variant + * out.writeInt(get(YEAR)); + * out.writeByte(get(MONTH_OF_YEAR)); + * out.writeByte(get(DAY_OF_MONTH)); + *+ * + * @return the instance of {@code Ser}, not null + */ private Object writeReplace() { return new Ser(Ser.HIJRAH_DATE_TYPE, this); } void writeExternal(ObjectOutput out) throws IOException { // HijrahChronology is implicit in the Hijrah_DATE_TYPE - out.writeObject(chrono); + out.writeObject(getChronology()); out.writeInt(get(YEAR)); out.writeByte(get(MONTH_OF_YEAR)); out.writeByte(get(DAY_OF_MONTH)); } - /** - * Replaces the date instance from the stream with a valid one. - * ReadExternal has already read the fields and created a new instance - * from the data. - * - * @return the resolved date, never null - */ - private Object readResolve() { - return this; - } - static HijrahDate readExternal(ObjectInput in) throws IOException, ClassNotFoundException { HijrahChronology chrono = (HijrahChronology) in.readObject(); int year = in.readInt(); diff --git a/jdk/src/share/classes/java/time/chrono/HijrahEra.java b/jdk/src/share/classes/java/time/chrono/HijrahEra.java index 1e99d6062ab..4390f698c32 100644 --- a/jdk/src/share/classes/java/time/chrono/HijrahEra.java +++ b/jdk/src/share/classes/java/time/chrono/HijrahEra.java @@ -63,9 +63,6 @@ package java.time.chrono; import static java.time.temporal.ChronoField.ERA; -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; import java.time.DateTimeException; import java.time.temporal.ChronoField; import java.time.temporal.TemporalField; @@ -158,18 +155,4 @@ public enum HijrahEra implements Era { return Era.super.range(field); } - //----------------------------------------------------------------------- - private Object writeReplace() { - return new Ser(Ser.HIJRAH_ERA_TYPE, this); - } - - void writeExternal(DataOutput out) throws IOException { - out.writeByte(this.getValue()); - } - - static HijrahEra readExternal(DataInput in) throws IOException { - byte eraValue = in.readByte(); - return HijrahEra.of(eraValue); - } - } diff --git a/jdk/src/share/classes/java/time/chrono/IsoChronology.java b/jdk/src/share/classes/java/time/chrono/IsoChronology.java index a2f6badeaa5..2012e64e04d 100644 --- a/jdk/src/share/classes/java/time/chrono/IsoChronology.java +++ b/jdk/src/share/classes/java/time/chrono/IsoChronology.java @@ -61,6 +61,8 @@ */ package java.time.chrono; +import java.io.InvalidObjectException; +import java.io.ObjectStreamException; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.ERA; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; @@ -563,4 +565,29 @@ public final class IsoChronology extends Chronology implements Serializable { return field.range(); } + //----------------------------------------------------------------------- + /** + * Writes the Chronology using a + * dedicated serialized form. + * @serialData + *
+ * out.writeByte(1); // identifies a Chronology + * out.writeUTF(getId()); + *+ * + * @return the instance of {@code Ser}, not null + */ + @Override + Object writeReplace() { + return super.writeReplace(); + } + + /** + * Defend against malicious streams. + * @return never + * @throws InvalidObjectException always + */ + private Object readResolve() throws InvalidObjectException { + throw new InvalidObjectException("Deserialization via serialization delegate"); + } } diff --git a/jdk/src/share/classes/java/time/chrono/JapaneseChronology.java b/jdk/src/share/classes/java/time/chrono/JapaneseChronology.java index 68a12755ea3..b01707e63ab 100644 --- a/jdk/src/share/classes/java/time/chrono/JapaneseChronology.java +++ b/jdk/src/share/classes/java/time/chrono/JapaneseChronology.java @@ -56,6 +56,8 @@ */ package java.time.chrono; +import java.io.InvalidObjectException; +import java.io.ObjectStreamException; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.ERA; @@ -503,4 +505,29 @@ public final class JapaneseChronology extends Chronology implements Serializable return dateYearDay(era, yoe, doy); // smart is same as strict } + //----------------------------------------------------------------------- + /** + * Writes the Chronology using a + * dedicated serialized form. + * @serialData + *
+ * out.writeByte(1); // identifies a Chronology + * out.writeUTF(getId()); + *+ * + * @return the instance of {@code Ser}, not null + */ + @Override + Object writeReplace() { + return super.writeReplace(); + } + + /** + * Defend against malicious streams. + * @return never + * @throws InvalidObjectException always + */ + private Object readResolve() throws InvalidObjectException { + throw new InvalidObjectException("Deserialization via serialization delegate"); + } } diff --git a/jdk/src/share/classes/java/time/chrono/JapaneseDate.java b/jdk/src/share/classes/java/time/chrono/JapaneseDate.java index e49a8f33c75..24ad7a921c0 100644 --- a/jdk/src/share/classes/java/time/chrono/JapaneseDate.java +++ b/jdk/src/share/classes/java/time/chrono/JapaneseDate.java @@ -69,6 +69,7 @@ import static java.time.temporal.ChronoField.YEAR_OF_ERA; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; +import java.io.InvalidObjectException; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; @@ -129,7 +130,7 @@ public final class JapaneseDate /** * The underlying ISO local date. */ - private transient final LocalDate isoDate; + private final transient LocalDate isoDate; /** * The JapaneseEra of this date. */ @@ -689,6 +690,28 @@ public final class JapaneseDate } //----------------------------------------------------------------------- + /** + * Defend against malicious streams. + * @return never + * @throws InvalidObjectException always + */ + private Object readResolve() throws InvalidObjectException { + throw new InvalidObjectException("Deserialization via serialization delegate"); + } + + /** + * Writes the object using a + * dedicated serialized form. + * @serialData + *
+ * out.writeByte(4); // identifies a JapaneseDate + * out.writeInt(get(YEAR)); + * out.writeByte(get(MONTH_OF_YEAR)); + * out.writeByte(get(DAY_OF_MONTH)); + *+ * + * @return the instance of {@code Ser}, not null + */ private Object writeReplace() { return new Ser(Ser.JAPANESE_DATE_TYPE, this); } diff --git a/jdk/src/share/classes/java/time/chrono/JapaneseEra.java b/jdk/src/share/classes/java/time/chrono/JapaneseEra.java index 29dda6bbbcf..46eee0b59f8 100644 --- a/jdk/src/share/classes/java/time/chrono/JapaneseEra.java +++ b/jdk/src/share/classes/java/time/chrono/JapaneseEra.java @@ -155,7 +155,7 @@ public final class JapaneseEra * The era value. * @serial */ - private final int eraValue; + private final transient int eraValue; // the first day of the era private final transient LocalDate since; @@ -371,6 +371,17 @@ public final class JapaneseEra } //----------------------------------------------------------------------- + /** + * Writes the object using a + * dedicated serialized form. + * @serialData + *
+ * out.writeByte(5); // identifies a JapaneseEra + * out.writeInt(getValue()); + *+ * + * @return the instance of {@code Ser}, not null + */ private Object writeReplace() { return new Ser(Ser.JAPANESE_ERA_TYPE, this); } diff --git a/jdk/src/share/classes/java/time/chrono/MinguoChronology.java b/jdk/src/share/classes/java/time/chrono/MinguoChronology.java index 088588004c3..db75a8645d6 100644 --- a/jdk/src/share/classes/java/time/chrono/MinguoChronology.java +++ b/jdk/src/share/classes/java/time/chrono/MinguoChronology.java @@ -56,6 +56,8 @@ */ package java.time.chrono; +import java.io.InvalidObjectException; +import java.io.ObjectStreamException; import static java.time.temporal.ChronoField.PROLEPTIC_MONTH; import static java.time.temporal.ChronoField.YEAR; @@ -333,4 +335,29 @@ public final class MinguoChronology extends Chronology implements Serializable { return (MinguoDate) super.resolveDate(fieldValues, resolverStyle); } + //----------------------------------------------------------------------- + /** + * Writes the Chronology using a + * dedicated serialized form. + * @serialData + *
+ * out.writeByte(1); // identifies a Chronology + * out.writeUTF(getId()); + *+ * + * @return the instance of {@code Ser}, not null + */ + @Override + Object writeReplace() { + return super.writeReplace(); + } + + /** + * Defend against malicious streams. + * @return never + * @throws InvalidObjectException always + */ + private Object readResolve() throws InvalidObjectException { + throw new InvalidObjectException("Deserialization via serialization delegate"); + } } diff --git a/jdk/src/share/classes/java/time/chrono/MinguoDate.java b/jdk/src/share/classes/java/time/chrono/MinguoDate.java index e15b0e90a15..16585e7e992 100644 --- a/jdk/src/share/classes/java/time/chrono/MinguoDate.java +++ b/jdk/src/share/classes/java/time/chrono/MinguoDate.java @@ -64,6 +64,7 @@ import static java.time.temporal.ChronoField.YEAR; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; +import java.io.InvalidObjectException; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; @@ -106,7 +107,7 @@ public final class MinguoDate /** * The underlying date. */ - private final LocalDate isoDate; + private final transient LocalDate isoDate; //----------------------------------------------------------------------- /** @@ -448,6 +449,28 @@ public final class MinguoDate } //----------------------------------------------------------------------- + /** + * Defend against malicious streams. + * @return never + * @throws InvalidObjectException always + */ + private Object readResolve() throws InvalidObjectException { + throw new InvalidObjectException("Deserialization via serialization delegate"); + } + + /** + * Writes the object using a + * dedicated serialized form. + * @serialData + *
+ * out.writeByte(8); // identifies a MinguoDate + * out.writeInt(get(YEAR)); + * out.writeByte(get(MONTH_OF_YEAR)); + * out.writeByte(get(DAY_OF_MONTH)); + *+ * + * @return the instance of {@code Ser}, not null + */ private Object writeReplace() { return new Ser(Ser.MINGUO_DATE_TYPE, this); } diff --git a/jdk/src/share/classes/java/time/chrono/MinguoEra.java b/jdk/src/share/classes/java/time/chrono/MinguoEra.java index edf1ad5610a..6bfdab2551d 100644 --- a/jdk/src/share/classes/java/time/chrono/MinguoEra.java +++ b/jdk/src/share/classes/java/time/chrono/MinguoEra.java @@ -61,9 +61,6 @@ */ package java.time.chrono; -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; import java.time.DateTimeException; /** @@ -155,18 +152,4 @@ public enum MinguoEra implements Era { return ordinal(); } - //----------------------------------------------------------------------- - private Object writeReplace() { - return new Ser(Ser.MINGUO_ERA_TYPE, this); - } - - void writeExternal(DataOutput out) throws IOException { - out.writeByte(this.getValue()); - } - - static MinguoEra readExternal(DataInput in) throws IOException { - byte eraValue = in.readByte(); - return MinguoEra.of(eraValue); - } - } diff --git a/jdk/src/share/classes/java/time/chrono/Ser.java b/jdk/src/share/classes/java/time/chrono/Ser.java index ff59aec4190..cc99f481f8c 100644 --- a/jdk/src/share/classes/java/time/chrono/Ser.java +++ b/jdk/src/share/classes/java/time/chrono/Ser.java @@ -74,14 +74,14 @@ import java.time.LocalDateTime; * byte flag would be used in order to specify an alternative version of the type format. * For example {@code CHRONO_TYPE_VERSION_2 = 21} *
- * In order to serialise the object it writes its byte and then calls back to the appropriate class where - * the serialisation is performed. In order to deserialise the object it read in the type byte, switching + * In order to serialize the object it writes its byte and then calls back to the appropriate class where + * the serialization is performed. In order to deserialize the object it read in the type byte, switching * in order to select which class to call back into. *
- * The serialisation format is determined on a per class basis. In the case of field based classes each + * The serialization format is determined on a per class basis. In the case of field based classes each * of the fields is written out with an appropriate size format in descending order of the field's size. For * example in the case of {@link LocalDate} year is written before month. Composite classes, such as - * {@link LocalDateTime} are serialised as one object. Enum classes are serialised using the index of their + * {@link LocalDateTime} are serialized as one object. Enum classes are serialized using the index of their * element. *
* This class is mutable and should be created once per serialization. @@ -102,11 +102,8 @@ final class Ser implements Externalizable { static final byte JAPANESE_DATE_TYPE = 4; static final byte JAPANESE_ERA_TYPE = 5; static final byte HIJRAH_DATE_TYPE = 6; - static final byte HIJRAH_ERA_TYPE = 7; - static final byte MINGUO_DATE_TYPE = 8; - static final byte MINGUO_ERA_TYPE = 9; - static final byte THAIBUDDHIST_DATE_TYPE = 10; - static final byte THAIBUDDHIST_ERA_TYPE = 11; + static final byte MINGUO_DATE_TYPE = 7; + static final byte THAIBUDDHIST_DATE_TYPE = 8; /** The type being serialized. */ private byte type; @@ -133,6 +130,24 @@ final class Ser implements Externalizable { //----------------------------------------------------------------------- /** * Implements the {@code Externalizable} interface to write the object. + * @serialData + * Each serializable class is mapped to a type that is the first byte + * in the stream. Refer to each class {@code writeReplace} + * serialized form for the value of the type and sequence of values for the type. + *
+ * out.writeByte(1); // identifies a Chronology + * out.writeUTF(getId()); + *+ * + * @return the instance of {@code Ser}, not null + */ + @Override + Object writeReplace() { + return super.writeReplace(); + } + + /** + * Defend against malicious streams. + * @return never + * @throws InvalidObjectException always + */ + private Object readResolve() throws InvalidObjectException { + throw new InvalidObjectException("Deserialization via serialization delegate"); + } } diff --git a/jdk/src/share/classes/java/time/chrono/ThaiBuddhistDate.java b/jdk/src/share/classes/java/time/chrono/ThaiBuddhistDate.java index 67799ad5af8..3d8f4078cc9 100644 --- a/jdk/src/share/classes/java/time/chrono/ThaiBuddhistDate.java +++ b/jdk/src/share/classes/java/time/chrono/ThaiBuddhistDate.java @@ -64,6 +64,7 @@ import static java.time.temporal.ChronoField.YEAR; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; +import java.io.InvalidObjectException; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; @@ -106,7 +107,7 @@ public final class ThaiBuddhistDate /** * The underlying date. */ - private final LocalDate isoDate; + private final transient LocalDate isoDate; //----------------------------------------------------------------------- /** @@ -448,6 +449,28 @@ public final class ThaiBuddhistDate } //----------------------------------------------------------------------- + /** + * Defend against malicious streams. + * @return never + * @throws InvalidObjectException always + */ + private Object readResolve() throws InvalidObjectException { + throw new InvalidObjectException("Deserialization via serialization delegate"); + } + + /** + * Writes the object using a + * dedicated serialized form. + * @serialData + *
+ * out.writeByte(10); // identifies a ThaiBuddhistDate + * out.writeInt(get(YEAR)); + * out.writeByte(get(MONTH_OF_YEAR)); + * out.writeByte(get(DAY_OF_MONTH)); + *+ * + * @return the instance of {@code Ser}, not null + */ private Object writeReplace() { return new Ser(Ser.THAIBUDDHIST_DATE_TYPE, this); } diff --git a/jdk/src/share/classes/java/time/chrono/ThaiBuddhistEra.java b/jdk/src/share/classes/java/time/chrono/ThaiBuddhistEra.java index d91eb81d79d..c549229d35e 100644 --- a/jdk/src/share/classes/java/time/chrono/ThaiBuddhistEra.java +++ b/jdk/src/share/classes/java/time/chrono/ThaiBuddhistEra.java @@ -61,9 +61,6 @@ */ package java.time.chrono; -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; import java.time.DateTimeException; /** @@ -155,18 +152,4 @@ public enum ThaiBuddhistEra implements Era { return ordinal(); } - //----------------------------------------------------------------------- - private Object writeReplace() { - return new Ser(Ser.THAIBUDDHIST_ERA_TYPE, this); - } - - void writeExternal(DataOutput out) throws IOException { - out.writeByte(this.getValue()); - } - - static ThaiBuddhistEra readExternal(DataInput in) throws IOException { - byte eraValue = in.readByte(); - return ThaiBuddhistEra.of(eraValue); - } - } diff --git a/jdk/src/share/classes/java/time/zone/Ser.java b/jdk/src/share/classes/java/time/zone/Ser.java index e34126436af..b2c3b259fb3 100644 --- a/jdk/src/share/classes/java/time/zone/Ser.java +++ b/jdk/src/share/classes/java/time/zone/Ser.java @@ -119,9 +119,20 @@ final class Ser implements Externalizable { //----------------------------------------------------------------------- /** * Implements the {@code Externalizable} interface to write the object. + * @serialData + * Each serializable class is mapped to a type that is the first byte + * in the stream. Refer to each class {@code writeReplace} + * serialized form for the value of the type and sequence of values for the type. + * + *
{@code
*
+ * out.writeByte(2); // identifies a ZoneOffsetTransition
+ * out.writeEpochSec(toEpochSecond);
+ * out.writeOffset(offsetBefore);
+ * out.writeOfset(offsetAfter);
+ * }
+ *
* @return the replacing object, not null
*/
private Object writeReplace() {
diff --git a/jdk/src/share/classes/java/time/zone/ZoneOffsetTransitionRule.java b/jdk/src/share/classes/java/time/zone/ZoneOffsetTransitionRule.java
index ad52f82de55..20e97f023f8 100644
--- a/jdk/src/share/classes/java/time/zone/ZoneOffsetTransitionRule.java
+++ b/jdk/src/share/classes/java/time/zone/ZoneOffsetTransitionRule.java
@@ -67,6 +67,7 @@ import static java.time.temporal.TemporalAdjuster.previousOrSame;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
+import java.io.InvalidObjectException;
import java.io.Serializable;
import java.time.DayOfWeek;
import java.time.LocalDate;
@@ -231,7 +232,56 @@ public final class ZoneOffsetTransitionRule implements Serializable {
//-----------------------------------------------------------------------
/**
- * Uses a serialization delegate.
+ * Defend against malicious streams.
+ * @return never
+ * @throws InvalidObjectException always
+ */
+ private Object readResolve() throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ /**
+ * Writes the object using a
+ * dedicated serialized form.
+ * @serialData
+ * Refer to the serialized form of
+ * ZoneRules.writeReplace
+ * for the encoding of epoch seconds and offsets.
+ * {@code
+ *
+ * out.writeByte(3); // identifies a ZoneOffsetTransition
+ * final int timeSecs = (timeEndOfDay ? 86400 : time.toSecondOfDay());
+ * final int stdOffset = standardOffset.getTotalSeconds();
+ * final int beforeDiff = offsetBefore.getTotalSeconds() - stdOffset;
+ * final int afterDiff = offsetAfter.getTotalSeconds() - stdOffset;
+ * final int timeByte = (timeSecs % 3600 == 0 ? (timeEndOfDay ? 24 : time.getHour()) : 31);
+ * final int stdOffsetByte = (stdOffset % 900 == 0 ? stdOffset / 900 + 128 : 255);
+ * final int beforeByte = (beforeDiff == 0 || beforeDiff == 1800 || beforeDiff == 3600 ? beforeDiff / 1800 : 3);
+ * final int afterByte = (afterDiff == 0 || afterDiff == 1800 || afterDiff == 3600 ? afterDiff / 1800 : 3);
+ * final int dowByte = (dow == null ? 0 : dow.getValue());
+ * int b = (month.getValue() << 28) + // 4 bits
+ * ((dom + 32) << 22) + // 6 bits
+ * (dowByte << 19) + // 3 bits
+ * (timeByte << 14) + // 5 bits
+ * (timeDefinition.ordinal() << 12) + // 2 bits
+ * (stdOffsetByte << 4) + // 8 bits
+ * (beforeByte << 2) + // 2 bits
+ * afterByte; // 2 bits
+ * out.writeInt(b);
+ * if (timeByte == 31) {
+ * out.writeInt(timeSecs);
+ * }
+ * if (stdOffsetByte == 255) {
+ * out.writeInt(stdOffset);
+ * }
+ * if (beforeByte == 3) {
+ * out.writeInt(offsetBefore.getTotalSeconds());
+ * }
+ * if (afterByte == 3) {
+ * out.writeInt(offsetAfter.getTotalSeconds());
+ * }
+ * }
+ *
*
* @return the replacing object, not null
*/
diff --git a/jdk/src/share/classes/java/time/zone/ZoneRules.java b/jdk/src/share/classes/java/time/zone/ZoneRules.java
index 070ad6e8398..064a2d8f817 100644
--- a/jdk/src/share/classes/java/time/zone/ZoneRules.java
+++ b/jdk/src/share/classes/java/time/zone/ZoneRules.java
@@ -64,6 +64,7 @@ package java.time.zone;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
+import java.io.InvalidObjectException;
import java.io.Serializable;
import java.time.Duration;
import java.time.Instant;
@@ -145,7 +146,7 @@ public final class ZoneRules implements Serializable {
/**
* The map of recent transitions.
*/
- private final ConcurrentMap{@code
*
+ * out.writeByte(1); // identifies a ZoneRules
+ * out.writeInt(standardTransitions.length);
+ * for (long trans : standardTransitions) {
+ * Ser.writeEpochSec(trans, out);
+ * }
+ * for (ZoneOffset offset : standardOffsets) {
+ * Ser.writeOffset(offset, out);
+ * }
+ * out.writeInt(savingsInstantTransitions.length);
+ * for (long trans : savingsInstantTransitions) {
+ * Ser.writeEpochSec(trans, out);
+ * }
+ * for (ZoneOffset offset : wallOffsets) {
+ * Ser.writeOffset(offset, out);
+ * }
+ * out.writeByte(lastRules.length);
+ * for (ZoneOffsetTransitionRule rule : lastRules) {
+ * rule.writeExternal(out);
+ * }
+ * }
+ *
+ * + * Epoch second values used for offsets are encoded in a variable + * length form to make the common cases put fewer bytes in the stream. + *
{@code
+ *
+ * static void writeEpochSec(long epochSec, DataOutput out) throws IOException {
+ * if (epochSec >= -4575744000L && epochSec < 10413792000L && epochSec % 900 == 0) { // quarter hours between 1825 and 2300
+ * int store = (int) ((epochSec + 4575744000L) / 900);
+ * out.writeByte((store >>> 16) & 255);
+ * out.writeByte((store >>> 8) & 255);
+ * out.writeByte(store & 255);
+ * } else {
+ * out.writeByte(255);
+ * out.writeLong(epochSec);
+ * }
+ * }
+ * }
+ *
+ * + * ZoneOffset values are encoded in a variable length form so the + * common cases put fewer bytes in the stream. + *
{@code
+ *
+ * static void writeOffset(ZoneOffset offset, DataOutput out) throws IOException {
+ * final int offsetSecs = offset.getTotalSeconds();
+ * int offsetByte = offsetSecs % 900 == 0 ? offsetSecs / 900 : 127; // compress to -72 to +72
+ * out.writeByte(offsetByte);
+ * if (offsetByte == 127) {
+ * out.writeInt(offsetSecs);
+ * }
+ * }
+ *}
+ *
* @return the replacing object, not null
*/
private Object writeReplace() {
diff --git a/jdk/test/java/time/tck/java/time/chrono/TCKChronologySerialization.java b/jdk/test/java/time/tck/java/time/chrono/TCKChronologySerialization.java
index e9d5fb4f1da..35620ad755e 100644
--- a/jdk/test/java/time/tck/java/time/chrono/TCKChronologySerialization.java
+++ b/jdk/test/java/time/tck/java/time/chrono/TCKChronologySerialization.java
@@ -97,7 +97,9 @@ public class TCKChronologySerialization {
ObjectOutputStream out = new ObjectOutputStream(baos);
out.writeObject(chrono);
out.close();
- ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+
+ byte[] bytes = baos.toByteArray();
+ ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream in = new ObjectInputStream(bais);
@SuppressWarnings("unchecked")