diff --git a/build.gradle b/build.gradle
index e13f65690..357c07b9d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -501,7 +501,8 @@ task javadoc(type: Javadoc) {
options.addBooleanOption('Xdoclint:all,-missing', true)
options.addBooleanOption("-allow-script-in-comments",true)
options.tags = ["type:a:Generic Type",
- "error:a:Expected Error"]
+ "error:a:Expected Error",
+ "invariant:a:Guaranteed Property"]
}
tasks.build.dependsOn(tasks.javadoc)
diff --git a/core/src/main/java/google/registry/model/BackupGroupRoot.java b/core/src/main/java/google/registry/model/BackupGroupRoot.java
index 37682c8ef..78c1a9480 100644
--- a/core/src/main/java/google/registry/model/BackupGroupRoot.java
+++ b/core/src/main/java/google/registry/model/BackupGroupRoot.java
@@ -31,7 +31,7 @@ import javax.xml.bind.annotation.XmlTransient;
* that we can enforce strictly increasing timestamps.
*/
@MappedSuperclass
-public abstract class BackupGroupRoot extends ImmutableObject {
+public abstract class BackupGroupRoot extends ImmutableObject implements UnsafeSerializable {
/**
* An automatically managed timestamp of when this object was last written to Datastore.
diff --git a/core/src/main/java/google/registry/model/CreateAutoTimestamp.java b/core/src/main/java/google/registry/model/CreateAutoTimestamp.java
index 2a3200514..606e3a13e 100644
--- a/core/src/main/java/google/registry/model/CreateAutoTimestamp.java
+++ b/core/src/main/java/google/registry/model/CreateAutoTimestamp.java
@@ -23,7 +23,7 @@ import org.joda.time.DateTime;
*
* @see CreateAutoTimestampTranslatorFactory
*/
-public class CreateAutoTimestamp extends ImmutableObject {
+public class CreateAutoTimestamp extends ImmutableObject implements UnsafeSerializable {
DateTime timestamp;
diff --git a/core/src/main/java/google/registry/model/ImmutableObject.java b/core/src/main/java/google/registry/model/ImmutableObject.java
index 27b0e7dda..7144c8a85 100644
--- a/core/src/main/java/google/registry/model/ImmutableObject.java
+++ b/core/src/main/java/google/registry/model/ImmutableObject.java
@@ -85,6 +85,9 @@ public abstract class ImmutableObject implements Cloneable {
@Target(FIELD)
public @interface Insignificant {}
+ // Note: if this class is made to implement Serializable, this field must become 'transient' since
+ // hashing is not stable across executions. Also note that @XmlTransient is forbidden on transient
+ // fields and need to be removed if transient is added.
@Ignore @XmlTransient protected Integer hashCode;
private boolean equalsImmutableObject(ImmutableObject other) {
diff --git a/core/src/main/java/google/registry/model/UnsafeSerializable.java b/core/src/main/java/google/registry/model/UnsafeSerializable.java
new file mode 100644
index 000000000..e7d9f797d
--- /dev/null
+++ b/core/src/main/java/google/registry/model/UnsafeSerializable.java
@@ -0,0 +1,34 @@
+// Copyright 2021 The Nomulus Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package google.registry.model;
+
+import java.io.Serializable;
+
+/**
+ * Marker interface for Nomulus entities whose serialization are implemented in a fragile way. These
+ * entities are made {@link Serializable} so that they can be passed between JVMs. The intended use
+ * case is BEAM pipeline-based cross-database data validation between Datastore and Cloud SQL during
+ * the migration. Note that only objects loaded from the SQL database need serialization support.
+ * Objects exported from Datastore can already be serialized as protocol buffers.
+ *
+ *
All entities implementing this interface take advantage of the fact that all Java collection
+ * classes we use, either directly or indirectly, including those in Java libraries, Guava,
+ * Objectify, and Hibernate are {@code Serializable}.
+ *
+ *
The {@code serialVersionUID} field has also been omitted in the implementing classes, since
+ * they are not used for persistence.
+ */
+// TODO(b/203609782): either remove this interface or fix implementors post migration.
+public interface UnsafeSerializable extends Serializable {}
diff --git a/core/src/main/java/google/registry/model/UpdateAutoTimestamp.java b/core/src/main/java/google/registry/model/UpdateAutoTimestamp.java
index 20370af06..5781409ad 100644
--- a/core/src/main/java/google/registry/model/UpdateAutoTimestamp.java
+++ b/core/src/main/java/google/registry/model/UpdateAutoTimestamp.java
@@ -38,7 +38,7 @@ import org.joda.time.DateTime;
* @see UpdateAutoTimestampTranslatorFactory
*/
@Embeddable
-public class UpdateAutoTimestamp extends ImmutableObject {
+public class UpdateAutoTimestamp extends ImmutableObject implements UnsafeSerializable {
// When set to true, database converters/translators should do the auto update. When set to
// false, auto update should be suspended (this exists to allow us to preserve the original value
diff --git a/core/src/main/java/google/registry/model/billing/BillingEvent.java b/core/src/main/java/google/registry/model/billing/BillingEvent.java
index c4910018c..f702fd9ab 100644
--- a/core/src/main/java/google/registry/model/billing/BillingEvent.java
+++ b/core/src/main/java/google/registry/model/billing/BillingEvent.java
@@ -39,6 +39,7 @@ import com.googlecode.objectify.annotation.Parent;
import com.googlecode.objectify.condition.IfNull;
import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
+import google.registry.model.UnsafeSerializable;
import google.registry.model.annotations.ReportedOn;
import google.registry.model.common.TimeOfYear;
import google.registry.model.domain.DomainBase;
@@ -72,7 +73,7 @@ import org.joda.time.DateTime;
/** A billable event in a domain's lifecycle. */
@MappedSuperclass
public abstract class BillingEvent extends ImmutableObject
- implements Buildable, TransferServerApproveEntity {
+ implements Buildable, TransferServerApproveEntity, UnsafeSerializable {
/** The reason for the bill, which maps 1:1 to skus in go/registry-billing-skus. */
public enum Reason {
diff --git a/core/src/main/java/google/registry/model/common/Cursor.java b/core/src/main/java/google/registry/model/common/Cursor.java
index 409906877..2aca2ad80 100644
--- a/core/src/main/java/google/registry/model/common/Cursor.java
+++ b/core/src/main/java/google/registry/model/common/Cursor.java
@@ -27,13 +27,13 @@ import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.OnLoad;
import com.googlecode.objectify.annotation.Parent;
import google.registry.model.ImmutableObject;
+import google.registry.model.UnsafeSerializable;
import google.registry.model.UpdateAutoTimestamp;
import google.registry.model.annotations.InCrossTld;
import google.registry.model.common.Cursor.CursorId;
import google.registry.model.replay.DatastoreAndSqlEntity;
import google.registry.model.tld.Registry;
import google.registry.persistence.VKey;
-import java.io.Serializable;
import java.util.List;
import java.util.Optional;
import javax.persistence.Column;
@@ -53,7 +53,7 @@ import org.joda.time.DateTime;
@javax.persistence.Entity
@IdClass(CursorId.class)
@InCrossTld
-public class Cursor extends ImmutableObject implements DatastoreAndSqlEntity {
+public class Cursor extends ImmutableObject implements DatastoreAndSqlEntity, UnsafeSerializable {
/** The scope of a global cursor. A global cursor is a cursor that is not specific to one tld. */
public static final String GLOBAL = "GLOBAL";
@@ -283,7 +283,7 @@ public class Cursor extends ImmutableObject implements DatastoreAndSqlEntity {
return cursorTime;
}
- static class CursorId extends ImmutableObject implements Serializable {
+ public static class CursorId extends ImmutableObject implements UnsafeSerializable {
public CursorType type;
public String scope;
diff --git a/core/src/main/java/google/registry/model/common/TimeOfYear.java b/core/src/main/java/google/registry/model/common/TimeOfYear.java
index e08ad0d6c..f6663cdef 100644
--- a/core/src/main/java/google/registry/model/common/TimeOfYear.java
+++ b/core/src/main/java/google/registry/model/common/TimeOfYear.java
@@ -28,6 +28,7 @@ import com.google.common.collect.Range;
import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.Index;
import google.registry.model.ImmutableObject;
+import google.registry.model.UnsafeSerializable;
import java.util.List;
import javax.persistence.Embeddable;
import org.joda.time.DateTime;
@@ -45,7 +46,7 @@ import org.joda.time.DateTime;
*/
@Embed
@Embeddable
-public class TimeOfYear extends ImmutableObject {
+public class TimeOfYear extends ImmutableObject implements UnsafeSerializable {
/**
* The time as "month day millis" with all fields left-padded with zeroes so that lexographic
diff --git a/core/src/main/java/google/registry/model/common/TimedTransitionProperty.java b/core/src/main/java/google/registry/model/common/TimedTransitionProperty.java
index 2d0ad62bb..65fab8076 100644
--- a/core/src/main/java/google/registry/model/common/TimedTransitionProperty.java
+++ b/core/src/main/java/google/registry/model/common/TimedTransitionProperty.java
@@ -27,7 +27,9 @@ import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
import com.googlecode.objectify.mapper.Mapper;
import google.registry.model.ImmutableObject;
+import google.registry.model.UnsafeSerializable;
import google.registry.util.TypeUtils;
+import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -53,11 +55,12 @@ import org.joda.time.DateTime;
* to use for storing the list of transitions. The user is given this choice of subclass so that the
* field of the value type stored in the transition can be given a customized name.
*/
-public class TimedTransitionProperty>
- extends ForwardingMap {
+public class TimedTransitionProperty<
+ V extends Serializable, T extends TimedTransitionProperty.TimedTransition>
+ extends ForwardingMap implements UnsafeSerializable {
/**
- * A transition to a value of type {@code V} at a certain time. This superclass only has a field
+ * A transition to a value of type {@code V} at a certain time. This superclass only has a field
* for the {@code DateTime}, which means that subclasses should supply the field of type {@code V}
* and implementations of the abstract getter and setter methods to access that field. This design
* is so that subclasses tagged with @Embed can define a custom field name for their value, for
@@ -65,11 +68,12 @@ public class TimedTransitionPropertyThe public visibility of this class exists only so that it can be subclassed; clients should
* never call any methods on this class or attempt to access its members, but should instead treat
- * it as a customizable implementation detail of {@code TimedTransitionProperty}. However, note
+ * it as a customizable implementation detail of {@code TimedTransitionProperty}. However, note
* that subclasses must also have public visibility so that they can be instantiated via
* reflection in a call to {@code fromValueMap}.
*/
- public abstract static class TimedTransition extends ImmutableObject {
+ public abstract static class TimedTransition extends ImmutableObject
+ implements UnsafeSerializable {
/** The time at which this value becomes the active value. */
private DateTime transitionTime;
@@ -89,16 +93,16 @@ public class TimedTransitionProperty parameter could be eliminated by getting the class via reflection, but then
// the callsite cannot infer T, so unless you explicitly call this as .fromValueMap() it
// will default to using just TimedTransition, which fails at runtime.
- private static > NavigableMap makeTransitionMap(
- ImmutableSortedMap valueMap,
- final Class timedTransitionSubclass) {
+ private static >
+ NavigableMap makeTransitionMap(
+ ImmutableSortedMap valueMap, final Class timedTransitionSubclass) {
checkArgument(
Ordering.natural().equals(valueMap.comparator()),
"Timed transition value map must have transition time keys in chronological order");
@@ -121,9 +125,9 @@ public class TimedTransitionPropertyThis method should be the normal method for constructing a {@link TimedTransitionProperty}.
*/
- public static > TimedTransitionProperty fromValueMap(
- ImmutableSortedMap valueMap,
- final Class timedTransitionSubclass) {
+ public static >
+ TimedTransitionProperty fromValueMap(
+ ImmutableSortedMap valueMap, final Class timedTransitionSubclass) {
return new TimedTransitionProperty<>(ImmutableSortedMap.copyOf(
makeTransitionMap(valueMap, timedTransitionSubclass)));
}
@@ -175,10 +179,10 @@ public class TimedTransitionProperty>
+ public static >
TimedTransitionProperty make(
ImmutableSortedMap newTransitions,
Class transitionClass,
@@ -200,7 +204,7 @@ public class TimedTransitionProperty>
+ public static >
void validateTimedTransitionMap(
@Nullable NavigableMap transitionMap,
ImmutableMultimap allowedTransitions,
@@ -240,8 +244,9 @@ public class TimedTransitionProperty> TimedTransitionProperty forMapify(
- ImmutableSortedMap valueMap, Class timedTransitionSubclass) {
+ public static >
+ TimedTransitionProperty forMapify(
+ ImmutableSortedMap valueMap, Class timedTransitionSubclass) {
return new TimedTransitionProperty<>(
new TreeMap<>(makeTransitionMap(valueMap, timedTransitionSubclass)));
}
@@ -254,8 +259,9 @@ public class TimedTransitionProperty> TimedTransitionProperty forMapify(
- V valueAtStartOfTime, Class timedTransitionSubclass) {
+ public static >
+ TimedTransitionProperty forMapify(
+ V valueAtStartOfTime, Class timedTransitionSubclass) {
return forMapify(
ImmutableSortedMap.of(START_OF_TIME, valueAtStartOfTime), timedTransitionSubclass);
}
diff --git a/core/src/main/java/google/registry/model/contact/ContactHistory.java b/core/src/main/java/google/registry/model/contact/ContactHistory.java
index bc0e4794a..ab2f6de18 100644
--- a/core/src/main/java/google/registry/model/contact/ContactHistory.java
+++ b/core/src/main/java/google/registry/model/contact/ContactHistory.java
@@ -20,6 +20,7 @@ import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.EntitySubclass;
import google.registry.model.EppResource;
import google.registry.model.ImmutableObject;
+import google.registry.model.UnsafeSerializable;
import google.registry.model.contact.ContactHistory.ContactHistoryId;
import google.registry.model.replay.DatastoreEntity;
import google.registry.model.replay.SqlEntity;
@@ -59,7 +60,7 @@ import javax.persistence.PostLoad;
@EntitySubclass
@Access(AccessType.FIELD)
@IdClass(ContactHistoryId.class)
-public class ContactHistory extends HistoryEntry implements SqlEntity {
+public class ContactHistory extends HistoryEntry implements SqlEntity, UnsafeSerializable {
// Store ContactBase instead of ContactResource so we don't pick up its @Id
// Nullable for the sake of pre-Registry-3.0 history objects
diff --git a/core/src/main/java/google/registry/model/contact/Disclose.java b/core/src/main/java/google/registry/model/contact/Disclose.java
index 25923376e..2e96eb453 100644
--- a/core/src/main/java/google/registry/model/contact/Disclose.java
+++ b/core/src/main/java/google/registry/model/contact/Disclose.java
@@ -20,7 +20,9 @@ import com.google.common.collect.ImmutableList;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
+import google.registry.model.UnsafeSerializable;
import google.registry.model.eppcommon.PresenceMarker;
+import java.io.Serializable;
import java.util.List;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
@@ -31,7 +33,7 @@ import javax.xml.bind.annotation.XmlType;
@Embed
@Embeddable
@XmlType(propOrder = {"name", "org", "addr", "voice", "fax", "email"})
-public class Disclose extends ImmutableObject {
+public class Disclose extends ImmutableObject implements UnsafeSerializable {
List name;
@@ -78,7 +80,7 @@ public class Disclose extends ImmutableObject {
/** The "intLocType" from RFC5733. */
@Embed
- public static class PostalInfoChoice extends ImmutableObject {
+ public static class PostalInfoChoice extends ImmutableObject implements Serializable {
@XmlAttribute
PostalInfo.Type type;
diff --git a/core/src/main/java/google/registry/model/contact/PostalInfo.java b/core/src/main/java/google/registry/model/contact/PostalInfo.java
index dcc2b90a3..0fb422f21 100644
--- a/core/src/main/java/google/registry/model/contact/PostalInfo.java
+++ b/core/src/main/java/google/registry/model/contact/PostalInfo.java
@@ -20,6 +20,7 @@ import com.googlecode.objectify.annotation.Embed;
import google.registry.model.Buildable;
import google.registry.model.Buildable.Overlayable;
import google.registry.model.ImmutableObject;
+import google.registry.model.UnsafeSerializable;
import java.util.Optional;
import javax.persistence.Embeddable;
import javax.persistence.EnumType;
@@ -38,7 +39,8 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@Embed
@Embeddable
@XmlType(propOrder = {"name", "org", "address", "type"})
-public class PostalInfo extends ImmutableObject implements Overlayable {
+public class PostalInfo extends ImmutableObject
+ implements Overlayable, UnsafeSerializable {
/** The type of the address, either localized or international. */
public enum Type {
diff --git a/core/src/main/java/google/registry/model/domain/DesignatedContact.java b/core/src/main/java/google/registry/model/domain/DesignatedContact.java
index e38f2a3c6..e4d1e39a5 100644
--- a/core/src/main/java/google/registry/model/domain/DesignatedContact.java
+++ b/core/src/main/java/google/registry/model/domain/DesignatedContact.java
@@ -21,6 +21,7 @@ import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.Index;
import google.registry.model.ImmutableObject;
+import google.registry.model.UnsafeSerializable;
import google.registry.model.contact.ContactResource;
import google.registry.persistence.VKey;
import javax.persistence.Embeddable;
@@ -46,7 +47,7 @@ import javax.xml.bind.annotation.XmlEnumValue;
*/
@Embed
@Embeddable
-public class DesignatedContact extends ImmutableObject {
+public class DesignatedContact extends ImmutableObject implements UnsafeSerializable {
/**
* XML type for contact types. This can be either: {@code "admin"}, {@code "billing"}, or
diff --git a/core/src/main/java/google/registry/model/domain/GracePeriodBase.java b/core/src/main/java/google/registry/model/domain/GracePeriodBase.java
index b6894648c..0c22ed065 100644
--- a/core/src/main/java/google/registry/model/domain/GracePeriodBase.java
+++ b/core/src/main/java/google/registry/model/domain/GracePeriodBase.java
@@ -17,6 +17,7 @@ package google.registry.model.domain;
import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.ImmutableObject;
+import google.registry.model.UnsafeSerializable;
import google.registry.model.billing.BillingEvent;
import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.persistence.BillingVKey.BillingEventVKey;
@@ -35,7 +36,7 @@ import org.joda.time.DateTime;
@Embed
@MappedSuperclass
@Access(AccessType.FIELD)
-public class GracePeriodBase extends ImmutableObject {
+public class GracePeriodBase extends ImmutableObject implements UnsafeSerializable {
/** Unique id required for hibernate representation. */
@Transient long gracePeriodId;
diff --git a/core/src/main/java/google/registry/model/domain/Period.java b/core/src/main/java/google/registry/model/domain/Period.java
index 6d0df8df3..c2841bf7a 100644
--- a/core/src/main/java/google/registry/model/domain/Period.java
+++ b/core/src/main/java/google/registry/model/domain/Period.java
@@ -16,6 +16,7 @@ package google.registry.model.domain;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.ImmutableObject;
+import google.registry.model.UnsafeSerializable;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.xml.bind.annotation.XmlAttribute;
@@ -25,7 +26,7 @@ import javax.xml.bind.annotation.XmlValue;
/** The "periodType" from RFC5731. */
@Embed
@javax.persistence.Embeddable
-public class Period extends ImmutableObject {
+public class Period extends ImmutableObject implements UnsafeSerializable {
@Enumerated(EnumType.STRING)
@XmlAttribute
diff --git a/core/src/main/java/google/registry/model/domain/launch/LaunchNotice.java b/core/src/main/java/google/registry/model/domain/launch/LaunchNotice.java
index df1d2ba3f..048a333d4 100644
--- a/core/src/main/java/google/registry/model/domain/launch/LaunchNotice.java
+++ b/core/src/main/java/google/registry/model/domain/launch/LaunchNotice.java
@@ -27,6 +27,7 @@ import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.IgnoreSave;
import com.googlecode.objectify.condition.IfNull;
import google.registry.model.ImmutableObject;
+import google.registry.model.UnsafeSerializable;
import java.util.Optional;
import javax.persistence.Embedded;
import javax.xml.bind.annotation.XmlAttribute;
@@ -39,7 +40,7 @@ import org.joda.time.DateTime;
@Embed
@XmlType(propOrder = {"noticeId", "expirationTime", "acceptedTime"})
@javax.persistence.Embeddable
-public class LaunchNotice extends ImmutableObject {
+public class LaunchNotice extends ImmutableObject implements UnsafeSerializable {
/** An empty instance to use in place of null. */
private static final NoticeIdType EMPTY_NOTICE_ID = new NoticeIdType();
@@ -47,14 +48,13 @@ public class LaunchNotice extends ImmutableObject {
/** An id with a validator-id attribute. */
@Embed
@javax.persistence.Embeddable
- public static class NoticeIdType extends ImmutableObject {
+ public static class NoticeIdType extends ImmutableObject implements UnsafeSerializable {
/**
- * The Trademark Claims Notice ID from
- * {@link "http://tools.ietf.org/html/draft-lozano-tmch-func-spec-08#section-6.3"}.
+ * The Trademark Claims Notice ID from the RFC.
*/
- @XmlValue
- String tcnId;
+ @XmlValue String tcnId;
/** The identifier of the TMDB provider to use, defaulting to the TMCH. */
@IgnoreSave(IfNull.class)
diff --git a/core/src/main/java/google/registry/model/domain/secdns/DomainDsDataBase.java b/core/src/main/java/google/registry/model/domain/secdns/DomainDsDataBase.java
index f09516aa1..739c7e20e 100644
--- a/core/src/main/java/google/registry/model/domain/secdns/DomainDsDataBase.java
+++ b/core/src/main/java/google/registry/model/domain/secdns/DomainDsDataBase.java
@@ -17,6 +17,7 @@ package google.registry.model.domain.secdns;
import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.ImmutableObject;
+import google.registry.model.UnsafeSerializable;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.MappedSuperclass;
@@ -31,7 +32,7 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@Embed
@MappedSuperclass
@Access(AccessType.FIELD)
-public abstract class DomainDsDataBase extends ImmutableObject {
+public abstract class DomainDsDataBase extends ImmutableObject implements UnsafeSerializable {
@Ignore @XmlTransient @Transient String domainRepoId;
diff --git a/core/src/main/java/google/registry/model/domain/secdns/DomainDsDataHistory.java b/core/src/main/java/google/registry/model/domain/secdns/DomainDsDataHistory.java
index f7ea90cb4..4d1e646a3 100644
--- a/core/src/main/java/google/registry/model/domain/secdns/DomainDsDataHistory.java
+++ b/core/src/main/java/google/registry/model/domain/secdns/DomainDsDataHistory.java
@@ -16,6 +16,7 @@ package google.registry.model.domain.secdns;
import static google.registry.model.IdService.allocateId;
+import google.registry.model.UnsafeSerializable;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.replay.SqlOnlyEntity;
@@ -27,7 +28,8 @@ import javax.persistence.Id;
/** Entity class to represent a historic {@link DelegationSignerData}. */
@Entity
-public class DomainDsDataHistory extends DomainDsDataBase implements SqlOnlyEntity {
+public class DomainDsDataHistory extends DomainDsDataBase
+ implements SqlOnlyEntity, UnsafeSerializable {
@Id Long dsDataHistoryRevisionId;
diff --git a/core/src/main/java/google/registry/model/eppcommon/Address.java b/core/src/main/java/google/registry/model/eppcommon/Address.java
index 28cf6f558..1a4de6b41 100644
--- a/core/src/main/java/google/registry/model/eppcommon/Address.java
+++ b/core/src/main/java/google/registry/model/eppcommon/Address.java
@@ -27,6 +27,7 @@ import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
import google.registry.model.JsonMapBuilder;
import google.registry.model.Jsonifiable;
+import google.registry.model.UnsafeSerializable;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -56,7 +57,7 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlTransient
@Embeddable
@MappedSuperclass
-public class Address extends ImmutableObject implements Jsonifiable {
+public class Address extends ImmutableObject implements Jsonifiable, UnsafeSerializable {
/** The schema validation will enforce that this has 3 lines at most. */
// TODO(b/177569726): Remove this field after migration. We need to figure out how to generate
diff --git a/core/src/main/java/google/registry/model/eppcommon/AuthInfo.java b/core/src/main/java/google/registry/model/eppcommon/AuthInfo.java
index 4fdd4f2a7..3f89c45f8 100644
--- a/core/src/main/java/google/registry/model/eppcommon/AuthInfo.java
+++ b/core/src/main/java/google/registry/model/eppcommon/AuthInfo.java
@@ -16,6 +16,7 @@ package google.registry.model.eppcommon;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.ImmutableObject;
+import google.registry.model.UnsafeSerializable;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.MappedSuperclass;
@@ -35,7 +36,7 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlTransient
@Embeddable
@MappedSuperclass
-public abstract class AuthInfo extends ImmutableObject {
+public abstract class AuthInfo extends ImmutableObject implements UnsafeSerializable {
@Embedded protected PasswordAuth pw;
@@ -47,7 +48,7 @@ public abstract class AuthInfo extends ImmutableObject {
@Embed
@XmlType(namespace = "urn:ietf:params:xml:ns:eppcom-1.0")
@Embeddable
- public static class PasswordAuth extends ImmutableObject {
+ public static class PasswordAuth extends ImmutableObject implements UnsafeSerializable {
@XmlValue
@XmlJavaTypeAdapter(NormalizedStringAdapter.class)
String value;
diff --git a/core/src/main/java/google/registry/model/eppcommon/PhoneNumber.java b/core/src/main/java/google/registry/model/eppcommon/PhoneNumber.java
index 22a854e81..dfb1919e5 100644
--- a/core/src/main/java/google/registry/model/eppcommon/PhoneNumber.java
+++ b/core/src/main/java/google/registry/model/eppcommon/PhoneNumber.java
@@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
+import google.registry.model.UnsafeSerializable;
import javax.persistence.Embeddable;
import javax.persistence.MappedSuperclass;
import javax.xml.bind.annotation.XmlAttribute;
@@ -49,7 +50,7 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlTransient
@Embeddable
@MappedSuperclass
-public class PhoneNumber extends ImmutableObject {
+public class PhoneNumber extends ImmutableObject implements UnsafeSerializable {
@XmlValue
@XmlJavaTypeAdapter(CollapsedStringAdapter.class)
diff --git a/core/src/main/java/google/registry/model/eppcommon/Trid.java b/core/src/main/java/google/registry/model/eppcommon/Trid.java
index 471f95440..f16b6f90c 100644
--- a/core/src/main/java/google/registry/model/eppcommon/Trid.java
+++ b/core/src/main/java/google/registry/model/eppcommon/Trid.java
@@ -18,6 +18,7 @@ import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.ImmutableObject;
+import google.registry.model.UnsafeSerializable;
import java.util.Optional;
import javax.annotation.Nullable;
import javax.xml.bind.annotation.XmlElement;
@@ -32,7 +33,7 @@ import javax.xml.bind.annotation.XmlType;
@Embed
@XmlType(propOrder = {"clientTransactionId", "serverTransactionId"})
@javax.persistence.Embeddable
-public class Trid extends ImmutableObject {
+public class Trid extends ImmutableObject implements UnsafeSerializable {
/** The server transaction id. */
@XmlElement(name = "svTRID", namespace = "urn:ietf:params:xml:ns:epp-1.0")
diff --git a/core/src/main/java/google/registry/model/host/HostHistory.java b/core/src/main/java/google/registry/model/host/HostHistory.java
index 2ef1b1bb8..0041da076 100644
--- a/core/src/main/java/google/registry/model/host/HostHistory.java
+++ b/core/src/main/java/google/registry/model/host/HostHistory.java
@@ -20,6 +20,7 @@ import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.EntitySubclass;
import google.registry.model.EppResource;
import google.registry.model.ImmutableObject;
+import google.registry.model.UnsafeSerializable;
import google.registry.model.host.HostHistory.HostHistoryId;
import google.registry.model.replay.DatastoreEntity;
import google.registry.model.replay.SqlEntity;
@@ -60,7 +61,7 @@ import javax.persistence.PostLoad;
@EntitySubclass
@Access(AccessType.FIELD)
@IdClass(HostHistoryId.class)
-public class HostHistory extends HistoryEntry implements SqlEntity {
+public class HostHistory extends HistoryEntry implements SqlEntity, UnsafeSerializable {
// Store HostBase instead of HostResource so we don't pick up its @Id
// Nullable for the sake of pre-Registry-3.0 history objects
diff --git a/core/src/main/java/google/registry/model/poll/PendingActionNotificationResponse.java b/core/src/main/java/google/registry/model/poll/PendingActionNotificationResponse.java
index 1e96de44d..32c94726a 100644
--- a/core/src/main/java/google/registry/model/poll/PendingActionNotificationResponse.java
+++ b/core/src/main/java/google/registry/model/poll/PendingActionNotificationResponse.java
@@ -17,6 +17,7 @@ package google.registry.model.poll;
import com.google.common.annotations.VisibleForTesting;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.ImmutableObject;
+import google.registry.model.UnsafeSerializable;
import google.registry.model.eppcommon.Trid;
import google.registry.model.eppoutput.EppResponse.ResponseData;
import javax.persistence.Embeddable;
@@ -31,12 +32,13 @@ import org.joda.time.DateTime;
/** The {@link ResponseData} returned when completing a pending action on a domain. */
@XmlTransient
@Embeddable
-public class PendingActionNotificationResponse extends ImmutableObject implements ResponseData {
+public class PendingActionNotificationResponse extends ImmutableObject
+ implements ResponseData, UnsafeSerializable {
/** The inner name type that contains a name and the result boolean. */
@Embed
@Embeddable
- static class NameOrId extends ImmutableObject {
+ static class NameOrId extends ImmutableObject implements UnsafeSerializable {
@XmlValue
String value;
diff --git a/core/src/main/java/google/registry/model/poll/PollMessage.java b/core/src/main/java/google/registry/model/poll/PollMessage.java
index 6903dc21c..d8232f756 100644
--- a/core/src/main/java/google/registry/model/poll/PollMessage.java
+++ b/core/src/main/java/google/registry/model/poll/PollMessage.java
@@ -33,6 +33,7 @@ import com.googlecode.objectify.annotation.Parent;
import google.registry.model.Buildable;
import google.registry.model.EppResource;
import google.registry.model.ImmutableObject;
+import google.registry.model.UnsafeSerializable;
import google.registry.model.annotations.ExternalMessagingName;
import google.registry.model.annotations.ReportedOn;
import google.registry.model.contact.ContactResource;
@@ -98,7 +99,7 @@ import org.joda.time.DateTime;
@javax.persistence.Index(columnList = "eventTime")
})
public abstract class PollMessage extends ImmutableObject
- implements Buildable, DatastoreAndSqlEntity, TransferServerApproveEntity {
+ implements Buildable, DatastoreAndSqlEntity, TransferServerApproveEntity, UnsafeSerializable {
/** Entity id. */
@Id
diff --git a/core/src/main/java/google/registry/model/registrar/Registrar.java b/core/src/main/java/google/registry/model/registrar/Registrar.java
index c66d479b6..75cf0dd33 100644
--- a/core/src/main/java/google/registry/model/registrar/Registrar.java
+++ b/core/src/main/java/google/registry/model/registrar/Registrar.java
@@ -72,6 +72,7 @@ import google.registry.model.CreateAutoTimestamp;
import google.registry.model.ImmutableObject;
import google.registry.model.JsonMapBuilder;
import google.registry.model.Jsonifiable;
+import google.registry.model.UnsafeSerializable;
import google.registry.model.UpdateAutoTimestamp;
import google.registry.model.annotations.InCrossTld;
import google.registry.model.annotations.ReportedOn;
@@ -116,7 +117,7 @@ import org.joda.time.DateTime;
})
@InCrossTld
public class Registrar extends ImmutableObject
- implements Buildable, DatastoreAndSqlEntity, Jsonifiable {
+ implements Buildable, DatastoreAndSqlEntity, Jsonifiable, UnsafeSerializable {
/** Represents the type of a registrar entity. */
public enum Type {
@@ -404,7 +405,7 @@ public class Registrar extends ImmutableObject
/** A billing account entry for this registrar, consisting of a currency and an account Id. */
@Embed
- public static class BillingAccountEntry extends ImmutableObject {
+ public static class BillingAccountEntry extends ImmutableObject implements UnsafeSerializable {
CurrencyUnit currency;
String accountId;
diff --git a/core/src/main/java/google/registry/model/registrar/RegistrarContact.java b/core/src/main/java/google/registry/model/registrar/RegistrarContact.java
index aff28987b..d4aefc041 100644
--- a/core/src/main/java/google/registry/model/registrar/RegistrarContact.java
+++ b/core/src/main/java/google/registry/model/registrar/RegistrarContact.java
@@ -46,6 +46,7 @@ import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
import google.registry.model.JsonMapBuilder;
import google.registry.model.Jsonifiable;
+import google.registry.model.UnsafeSerializable;
import google.registry.model.annotations.InCrossTld;
import google.registry.model.annotations.ReportedOn;
import google.registry.model.registrar.RegistrarContact.RegistrarPocId;
@@ -82,7 +83,7 @@ import javax.persistence.Transient;
@IdClass(RegistrarPocId.class)
@InCrossTld
public class RegistrarContact extends ImmutableObject
- implements DatastoreAndSqlEntity, Jsonifiable {
+ implements DatastoreAndSqlEntity, Jsonifiable, UnsafeSerializable {
@Parent @Transient Key parent;
diff --git a/core/src/main/java/google/registry/model/reporting/DomainTransactionRecord.java b/core/src/main/java/google/registry/model/reporting/DomainTransactionRecord.java
index 01e0f81e3..1f9ccaf8d 100644
--- a/core/src/main/java/google/registry/model/reporting/DomainTransactionRecord.java
+++ b/core/src/main/java/google/registry/model/reporting/DomainTransactionRecord.java
@@ -22,6 +22,7 @@ import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
+import google.registry.model.UnsafeSerializable;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.replay.DatastoreAndSqlEntity;
import javax.persistence.Column;
@@ -46,7 +47,7 @@ import org.joda.time.DateTime;
@Embed
@Entity
public class DomainTransactionRecord extends ImmutableObject
- implements Buildable, DatastoreAndSqlEntity {
+ implements Buildable, DatastoreAndSqlEntity, UnsafeSerializable {
@Id
@Ignore
diff --git a/core/src/main/java/google/registry/model/reporting/HistoryEntry.java b/core/src/main/java/google/registry/model/reporting/HistoryEntry.java
index 42b38f0ef..a09c69588 100644
--- a/core/src/main/java/google/registry/model/reporting/HistoryEntry.java
+++ b/core/src/main/java/google/registry/model/reporting/HistoryEntry.java
@@ -31,6 +31,7 @@ import com.googlecode.objectify.condition.IfNull;
import google.registry.model.Buildable;
import google.registry.model.EppResource;
import google.registry.model.ImmutableObject;
+import google.registry.model.UnsafeSerializable;
import google.registry.model.annotations.ReportedOn;
import google.registry.model.contact.ContactBase;
import google.registry.model.contact.ContactHistory;
@@ -83,7 +84,8 @@ import org.joda.time.DateTime;
@Entity
@MappedSuperclass
@Access(AccessType.FIELD)
-public class HistoryEntry extends ImmutableObject implements Buildable, DatastoreEntity {
+public class HistoryEntry extends ImmutableObject
+ implements Buildable, DatastoreEntity, UnsafeSerializable {
/** Represents the type of history entry. */
public enum Type {
diff --git a/core/src/main/java/google/registry/model/tld/Registry.java b/core/src/main/java/google/registry/model/tld/Registry.java
index 71d731a73..faa81b26a 100644
--- a/core/src/main/java/google/registry/model/tld/Registry.java
+++ b/core/src/main/java/google/registry/model/tld/Registry.java
@@ -52,6 +52,7 @@ import com.googlecode.objectify.annotation.Parent;
import google.registry.model.Buildable;
import google.registry.model.CreateAutoTimestamp;
import google.registry.model.ImmutableObject;
+import google.registry.model.UnsafeSerializable;
import google.registry.model.annotations.InCrossTld;
import google.registry.model.annotations.ReportedOn;
import google.registry.model.common.EntityGroupRoot;
@@ -88,7 +89,8 @@ import org.joda.time.Duration;
@Entity
@javax.persistence.Entity(name = "Tld")
@InCrossTld
-public class Registry extends ImmutableObject implements Buildable, DatastoreAndSqlEntity {
+public class Registry extends ImmutableObject
+ implements Buildable, DatastoreAndSqlEntity, UnsafeSerializable {
@Parent @Transient Key parent = getCrossTldKey();
@@ -308,7 +310,7 @@ public class Registry extends ImmutableObject implements Buildable, DatastoreAnd
* There must be at least one entry in this set.
*
*
All entries of this list must be valid keys for the map of {@code DnsWriter}s injected by
- * @Inject Map
+ * {@code @Inject Map}
*/
@Column(nullable = false)
Set dnsWriters;
diff --git a/core/src/main/java/google/registry/model/transfer/BaseTransferObject.java b/core/src/main/java/google/registry/model/transfer/BaseTransferObject.java
index f87fec788..bf92f1be4 100644
--- a/core/src/main/java/google/registry/model/transfer/BaseTransferObject.java
+++ b/core/src/main/java/google/registry/model/transfer/BaseTransferObject.java
@@ -16,6 +16,7 @@ package google.registry.model.transfer;
import google.registry.model.Buildable.GenericBuilder;
import google.registry.model.ImmutableObject;
+import google.registry.model.UnsafeSerializable;
import javax.persistence.Column;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
@@ -27,7 +28,7 @@ import org.joda.time.DateTime;
/** Fields common to {@link TransferData} and {@link TransferResponse}. */
@XmlTransient
@MappedSuperclass
-public abstract class BaseTransferObject extends ImmutableObject {
+public abstract class BaseTransferObject extends ImmutableObject implements UnsafeSerializable {
/**
* The status of the current or last transfer. Can be null if never transferred. Note that we
* leave IgnoreSave off this field so that we can ensure that TransferData loaded from Objectify
diff --git a/core/src/main/java/google/registry/persistence/converter/TimedTransitionPropertyConverterBase.java b/core/src/main/java/google/registry/persistence/converter/TimedTransitionPropertyConverterBase.java
index 57869df40..ff69ea3d4 100644
--- a/core/src/main/java/google/registry/persistence/converter/TimedTransitionPropertyConverterBase.java
+++ b/core/src/main/java/google/registry/persistence/converter/TimedTransitionPropertyConverterBase.java
@@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableSortedMap;
import google.registry.model.common.TimedTransitionProperty;
import google.registry.model.common.TimedTransitionProperty.TimedTransition;
import google.registry.persistence.converter.StringMapDescriptor.StringMap;
+import java.io.Serializable;
import java.util.Map;
import javax.annotation.Nullable;
import javax.persistence.AttributeConverter;
@@ -30,7 +31,8 @@ import org.joda.time.DateTime;
* Base JPA converter for {@link TimedTransitionProperty} objects that are stored in a column with
* data type of hstore in the database.
*/
-public abstract class TimedTransitionPropertyConverterBase>
+public abstract class TimedTransitionPropertyConverterBase<
+ K extends Serializable, V extends TimedTransition>
implements AttributeConverter, StringMap> {
abstract Map.Entry convertToDatabaseMapEntry(Map.Entry entry);
diff --git a/core/src/test/java/google/registry/model/billing/BillingEventTest.java b/core/src/test/java/google/registry/model/billing/BillingEventTest.java
index 6337207d2..d7b284e35 100644
--- a/core/src/test/java/google/registry/model/billing/BillingEventTest.java
+++ b/core/src/test/java/google/registry/model/billing/BillingEventTest.java
@@ -25,6 +25,7 @@ import static google.registry.testing.DatabaseHelper.loadByKey;
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
+import static google.registry.util.SerializeUtils.serializeDeserialize;
import static org.joda.money.CurrencyUnit.USD;
import static org.joda.time.DateTimeZone.UTC;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -46,6 +47,7 @@ import google.registry.persistence.VKey;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
+import google.registry.testing.TestSqlOnly;
import google.registry.util.DateTimeUtils;
import org.joda.money.Money;
import org.joda.time.DateTime;
@@ -195,6 +197,20 @@ public class BillingEventTest extends EntityTestCase {
ofyTmOrDoNothing(() -> assertThat(tm().loadByEntity(modification)).isEqualTo(modification));
}
+ @TestSqlOnly
+ void testSerializable() {
+ BillingEvent persisted = loadByEntity(oneTime);
+ assertThat(serializeDeserialize(persisted)).isEqualTo(persisted);
+ persisted = loadByEntity(oneTimeSynthetic);
+ assertThat(serializeDeserialize(persisted)).isEqualTo(persisted);
+ persisted = loadByEntity(recurring);
+ assertThat(serializeDeserialize(persisted)).isEqualTo(persisted);
+ persisted = loadByEntity(cancellationOneTime);
+ assertThat(serializeDeserialize(persisted)).isEqualTo(persisted);
+ persisted = loadByEntity(cancellationRecurring);
+ assertThat(serializeDeserialize(persisted)).isEqualTo(persisted);
+ }
+
@TestOfyOnly
void testParenting() {
// Note that these are all tested separately because BillingEvent is an abstract base class that
diff --git a/core/src/test/java/google/registry/model/common/CursorTest.java b/core/src/test/java/google/registry/model/common/CursorTest.java
index 40f38472f..a61cdd663 100644
--- a/core/src/test/java/google/registry/model/common/CursorTest.java
+++ b/core/src/test/java/google/registry/model/common/CursorTest.java
@@ -30,6 +30,8 @@ import google.registry.model.domain.DomainBase;
import google.registry.model.tld.Registry;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
+import google.registry.testing.TestSqlOnly;
+import google.registry.util.SerializeUtils;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
@@ -46,6 +48,15 @@ public class CursorTest extends EntityTestCase {
fakeClock.setTo(DateTime.parse("2010-10-17TZ"));
}
+ @TestSqlOnly
+ void testSerializable() {
+ final DateTime time = DateTime.parse("2012-07-12T03:30:00.000Z");
+ tm().transact(() -> tm().put(Cursor.createGlobal(RECURRING_BILLING, time)));
+ Cursor persisted =
+ tm().transact(() -> tm().loadByKey(Cursor.createGlobalVKey(RECURRING_BILLING)));
+ assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
+ }
+
@TestOfyAndSql
void testSuccess_persistScopedCursor() {
createTld("tld");
diff --git a/core/src/test/java/google/registry/model/contact/ContactResourceTest.java b/core/src/test/java/google/registry/model/contact/ContactResourceTest.java
index ad6e59c18..1de2cc034 100644
--- a/core/src/test/java/google/registry/model/contact/ContactResourceTest.java
+++ b/core/src/test/java/google/registry/model/contact/ContactResourceTest.java
@@ -48,6 +48,7 @@ import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import google.registry.testing.TestSqlOnly;
+import google.registry.util.SerializeUtils;
import org.junit.jupiter.api.BeforeEach;
/** Unit tests for {@link ContactResource}. */
@@ -174,6 +175,14 @@ public class ContactResourceTest extends EntityTestCase {
.hasValue(contactResource);
}
+ @TestSqlOnly
+ void testSerializable() {
+ ContactResource persisted =
+ loadByForeignKey(ContactResource.class, contactResource.getForeignKey(), fakeClock.nowUtc())
+ .get();
+ assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
+ }
+
@TestOfyOnly
void testIndexing() throws Exception {
verifyDatastoreIndexing(
diff --git a/core/src/test/java/google/registry/model/domain/DomainBaseSqlTest.java b/core/src/test/java/google/registry/model/domain/DomainBaseSqlTest.java
index f5cb14e7b..578ceb508 100644
--- a/core/src/test/java/google/registry/model/domain/DomainBaseSqlTest.java
+++ b/core/src/test/java/google/registry/model/domain/DomainBaseSqlTest.java
@@ -56,6 +56,7 @@ import google.registry.testing.AppEngineExtension;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.FakeClock;
import google.registry.testing.TestSqlOnly;
+import google.registry.util.SerializeUtils;
import java.util.Arrays;
import org.joda.money.Money;
import org.joda.time.DateTime;
@@ -354,6 +355,14 @@ public class DomainBaseSqlTest {
});
}
+ @TestSqlOnly
+ void testSerializable() {
+ createTld("com");
+ insertInDb(contact, contact2, domain, host);
+ DomainBase persisted = jpaTm().transact(() -> jpaTm().loadByEntity(domain));
+ assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
+ }
+
@TestSqlOnly
void testUpdates() {
createTld("com");
diff --git a/core/src/test/java/google/registry/model/domain/token/AllocationTokenTest.java b/core/src/test/java/google/registry/model/domain/token/AllocationTokenTest.java
index 120cd8fdf..746b9d218 100644
--- a/core/src/test/java/google/registry/model/domain/token/AllocationTokenTest.java
+++ b/core/src/test/java/google/registry/model/domain/token/AllocationTokenTest.java
@@ -41,6 +41,8 @@ import google.registry.model.reporting.HistoryEntry;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
+import google.registry.testing.TestSqlOnly;
+import google.registry.util.SerializeUtils;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
@@ -93,6 +95,44 @@ public class AllocationTokenTest extends EntityTestCase {
assertThat(loadByEntity(singleUseToken)).isEqualTo(singleUseToken);
}
+ @TestSqlOnly
+ void testSerializable() {
+ AllocationToken unlimitedUseToken =
+ persistResource(
+ new AllocationToken.Builder()
+ .setToken("abc123Unlimited")
+ .setTokenType(UNLIMITED_USE)
+ .setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
+ .setAllowedTlds(ImmutableSet.of("dev", "app"))
+ .setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar, NewRegistrar"))
+ .setDiscountFraction(0.5)
+ .setDiscountPremiums(true)
+ .setDiscountYears(3)
+ .setTokenStatusTransitions(
+ ImmutableSortedMap.naturalOrder()
+ .put(START_OF_TIME, NOT_STARTED)
+ .put(DateTime.now(UTC), TokenStatus.VALID)
+ .put(DateTime.now(UTC).plusWeeks(8), TokenStatus.ENDED)
+ .build())
+ .build());
+ AllocationToken persisted = loadByEntity(unlimitedUseToken);
+ assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
+
+ DomainBase domain = persistActiveDomain("example.foo");
+ Key historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 1);
+ AllocationToken singleUseToken =
+ persistResource(
+ new AllocationToken.Builder()
+ .setToken("abc123Single")
+ .setRedemptionHistoryEntry(HistoryEntry.createVKey(historyEntryKey))
+ .setDomainName("example.foo")
+ .setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
+ .setTokenType(SINGLE_USE)
+ .build());
+ persisted = loadByEntity(singleUseToken);
+ assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
+ }
+
@TestOfyOnly
void testIndexing() throws Exception {
DomainBase domain = persistActiveDomain("blahdomain.foo");
diff --git a/core/src/test/java/google/registry/model/history/ContactHistoryTest.java b/core/src/test/java/google/registry/model/history/ContactHistoryTest.java
index 15f46c0b6..5d8d61975 100644
--- a/core/src/test/java/google/registry/model/history/ContactHistoryTest.java
+++ b/core/src/test/java/google/registry/model/history/ContactHistoryTest.java
@@ -39,6 +39,7 @@ import google.registry.persistence.VKey;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyOnly;
import google.registry.testing.TestSqlOnly;
+import google.registry.util.SerializeUtils;
/** Tests for {@link ContactHistory}. */
@DualDatabaseTest
@@ -64,6 +65,18 @@ public class ContactHistoryTest extends EntityTestCase {
});
}
+ @TestSqlOnly
+ void testSerializable() {
+ ContactResource contact = newContactResourceWithRoid("contactId", "contact1");
+ insertInDb(contact);
+ ContactResource contactFromDb = loadByEntity(contact);
+ ContactHistory contactHistory = createContactHistory(contactFromDb);
+ insertInDb(contactHistory);
+ ContactHistory fromDatabase =
+ jpaTm().transact(() -> jpaTm().loadByKey(contactHistory.createVKey()));
+ assertThat(SerializeUtils.serializeDeserialize(fromDatabase)).isEqualTo(fromDatabase);
+ }
+
@TestSqlOnly
void testLegacyPersistence_nullContactBase() {
ContactResource contact = newContactResourceWithRoid("contactId", "contact1");
diff --git a/core/src/test/java/google/registry/model/history/DomainHistoryTest.java b/core/src/test/java/google/registry/model/history/DomainHistoryTest.java
index 7bc2ce962..7b5a04a48 100644
--- a/core/src/test/java/google/registry/model/history/DomainHistoryTest.java
+++ b/core/src/test/java/google/registry/model/history/DomainHistoryTest.java
@@ -55,6 +55,7 @@ import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyOnly;
import google.registry.testing.TestSqlOnly;
+import google.registry.util.SerializeUtils;
import java.util.Optional;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
@@ -87,6 +88,16 @@ public class DomainHistoryTest extends EntityTestCase {
});
}
+ @TestSqlOnly
+ void testSerializable() {
+ DomainBase domain = addGracePeriodForSql(createDomainWithContactsAndHosts());
+ DomainHistory domainHistory = createDomainHistory(domain);
+ insertInDb(domainHistory);
+ DomainHistory fromDatabase =
+ jpaTm().transact(() -> jpaTm().loadByKey(domainHistory.createVKey()));
+ assertThat(SerializeUtils.serializeDeserialize(fromDatabase)).isEqualTo(fromDatabase);
+ }
+
@TestSqlOnly
void testLegacyPersistence_nullResource() {
DomainBase domain = addGracePeriodForSql(createDomainWithContactsAndHosts());
diff --git a/core/src/test/java/google/registry/model/history/HostHistoryTest.java b/core/src/test/java/google/registry/model/history/HostHistoryTest.java
index a45431f1f..13f9afb95 100644
--- a/core/src/test/java/google/registry/model/history/HostHistoryTest.java
+++ b/core/src/test/java/google/registry/model/history/HostHistoryTest.java
@@ -36,6 +36,7 @@ import google.registry.persistence.VKey;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyOnly;
import google.registry.testing.TestSqlOnly;
+import google.registry.util.SerializeUtils;
/** Tests for {@link HostHistory}. */
@DualDatabaseTest
@@ -61,6 +62,17 @@ public class HostHistoryTest extends EntityTestCase {
});
}
+ @TestSqlOnly
+ void testSerializable() {
+ HostResource host = newHostResourceWithRoid("ns1.example.com", "host1");
+ insertInDb(host);
+ HostResource hostFromDb = loadByEntity(host);
+ HostHistory hostHistory = createHostHistory(hostFromDb);
+ insertInDb(hostHistory);
+ HostHistory fromDatabase = jpaTm().transact(() -> jpaTm().loadByKey(hostHistory.createVKey()));
+ assertThat(SerializeUtils.serializeDeserialize(fromDatabase)).isEqualTo(fromDatabase);
+ }
+
@TestSqlOnly
void testLegacyPersistence_nullHostBase() {
HostResource host = newHostResourceWithRoid("ns1.example.com", "host1");
diff --git a/core/src/test/java/google/registry/model/host/HostResourceTest.java b/core/src/test/java/google/registry/model/host/HostResourceTest.java
index 7d82f9ad6..ec3032411 100644
--- a/core/src/test/java/google/registry/model/host/HostResourceTest.java
+++ b/core/src/test/java/google/registry/model/host/HostResourceTest.java
@@ -45,6 +45,8 @@ import google.registry.model.transfer.TransferStatus;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
+import google.registry.testing.TestSqlOnly;
+import google.registry.util.SerializeUtils;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
@@ -111,6 +113,14 @@ class HostResourceTest extends EntityTestCase {
.containsExactly(newHost);
}
+ @TestSqlOnly
+ void testSerializable() {
+ HostResource newHost = host.asBuilder().setRepoId("NEWHOST").build();
+ tm().transact(() -> tm().insert(newHost));
+ HostResource persisted = tm().transact(() -> tm().loadByEntity(newHost));
+ assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
+ }
+
@TestOfyOnly
void testLoadingByForeignKey() {
assertThat(loadByForeignKey(HostResource.class, host.getForeignKey(), fakeClock.nowUtc()))
diff --git a/core/src/test/java/google/registry/model/poll/PollMessageTest.java b/core/src/test/java/google/registry/model/poll/PollMessageTest.java
index 76beca234..db44ea26e 100644
--- a/core/src/test/java/google/registry/model/poll/PollMessageTest.java
+++ b/core/src/test/java/google/registry/model/poll/PollMessageTest.java
@@ -36,6 +36,7 @@ import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import google.registry.testing.TestSqlOnly;
+import google.registry.util.SerializeUtils;
import org.junit.jupiter.api.BeforeEach;
/** Unit tests for {@link PollMessage}. */
@@ -118,6 +119,20 @@ public class PollMessageTest extends EntityTestCase {
assertThat(tm().transact(() -> tm().loadByEntity(pollMessage))).isEqualTo(pollMessage);
}
+ @TestSqlOnly
+ void testSerializableOneTime() {
+ PollMessage.OneTime pollMessage =
+ persistResource(
+ new PollMessage.OneTime.Builder()
+ .setRegistrarId("TheRegistrar")
+ .setEventTime(fakeClock.nowUtc())
+ .setMsg("Test poll message")
+ .setParent(historyEntry)
+ .build());
+ PollMessage persisted = tm().transact(() -> tm().loadByEntity(pollMessage));
+ assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
+ }
+
@TestOfyAndSql
void testPersistenceAutorenew() {
PollMessage.Autorenew pollMessage =
@@ -133,6 +148,22 @@ public class PollMessageTest extends EntityTestCase {
assertThat(tm().transact(() -> tm().loadByEntity(pollMessage))).isEqualTo(pollMessage);
}
+ @TestSqlOnly
+ void testSerializableAutorenew() {
+ PollMessage.Autorenew pollMessage =
+ persistResource(
+ new PollMessage.Autorenew.Builder()
+ .setRegistrarId("TheRegistrar")
+ .setEventTime(fakeClock.nowUtc())
+ .setMsg("Test poll message")
+ .setParent(historyEntry)
+ .setAutorenewEndTime(fakeClock.nowUtc().plusDays(365))
+ .setTargetId("foobar.foo")
+ .build());
+ PollMessage persisted = tm().transact(() -> tm().loadByEntity(pollMessage));
+ assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
+ }
+
@TestOfyOnly
void testIndexingAutorenew() throws Exception {
PollMessage.Autorenew pollMessage =
diff --git a/core/src/test/java/google/registry/model/registrar/RegistrarTest.java b/core/src/test/java/google/registry/model/registrar/RegistrarTest.java
index 2f34418da..09cf69081 100644
--- a/core/src/test/java/google/registry/model/registrar/RegistrarTest.java
+++ b/core/src/test/java/google/registry/model/registrar/RegistrarTest.java
@@ -45,7 +45,9 @@ import google.registry.model.tld.Registries;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
+import google.registry.testing.TestSqlOnly;
import google.registry.util.CidrAddressBlock;
+import google.registry.util.SerializeUtils;
import org.joda.money.CurrencyUnit;
import org.junit.jupiter.api.BeforeEach;
@@ -135,6 +137,12 @@ class RegistrarTest extends EntityTestCase {
assertThat(tm().transact(() -> tm().loadByKey(registrar.createVKey()))).isEqualTo(registrar);
}
+ @TestSqlOnly
+ void testSerializable() {
+ Registrar persisted = tm().transact(() -> tm().loadByKey(registrar.createVKey()));
+ assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
+ }
+
@TestOfyOnly
void testIndexing() throws Exception {
verifyDatastoreIndexing(registrar, "registrarName", "ianaIdentifier");
diff --git a/core/src/test/java/google/registry/model/tld/RegistryTest.java b/core/src/test/java/google/registry/model/tld/RegistryTest.java
index a4fef56a3..4f687fe18 100644
--- a/core/src/test/java/google/registry/model/tld/RegistryTest.java
+++ b/core/src/test/java/google/registry/model/tld/RegistryTest.java
@@ -47,6 +47,8 @@ import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
+import google.registry.testing.TestSqlOnly;
+import google.registry.util.SerializeUtils;
import java.math.BigDecimal;
import java.util.Optional;
import org.joda.money.Money;
@@ -87,6 +89,16 @@ public final class RegistryTest extends EntityTestCase {
.isEqualTo(Registry.get("tld"));
}
+ @TestSqlOnly
+ void testSerializable() {
+ ReservedList rl15 = persistReservedList("tld-reserved15", "potato,FULLY_BLOCKED");
+ Registry registry = Registry.get("tld").asBuilder().setReservedLists(rl15).build();
+ tm().transact(() -> tm().put(registry));
+ Registry persisted =
+ tm().transact(() -> tm().loadByKey(Registry.createVKey(registry.tldStrId)));
+ assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
+ }
+
@TestOfyAndSql
void testFailure_registryNotFound() {
createTld("foo");
diff --git a/core/src/test/java/google/registry/schema/registrar/RegistrarContactTest.java b/core/src/test/java/google/registry/schema/registrar/RegistrarContactTest.java
index 6bbe91eb2..b90996d4f 100644
--- a/core/src/test/java/google/registry/schema/registrar/RegistrarContactTest.java
+++ b/core/src/test/java/google/registry/schema/registrar/RegistrarContactTest.java
@@ -16,6 +16,7 @@ package google.registry.schema.registrar;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.registrar.RegistrarContact.Type.WHOIS;
+import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.testing.DatabaseHelper.insertInDb;
import static google.registry.testing.DatabaseHelper.loadByEntity;
import static google.registry.testing.SqlHelper.saveRegistrar;
@@ -27,6 +28,7 @@ import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationWithCoverageExtension;
import google.registry.testing.DatastoreEntityExtension;
import google.registry.testing.TmOverrideExtension;
+import google.registry.util.SerializeUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
@@ -74,4 +76,11 @@ class RegistrarContactTest {
insertInDb(testRegistrarPoc);
assertThat(loadByEntity(testRegistrarPoc)).isEqualTo(testRegistrarPoc);
}
+
+ @Test
+ void testSerializable_succeeds() {
+ insertInDb(testRegistrarPoc);
+ RegistrarContact persisted = jpaTm().transact(() -> jpaTm().loadByEntity(testRegistrarPoc));
+ assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
+ }
}
diff --git a/util/src/main/java/google/registry/util/SerializeUtils.java b/util/src/main/java/google/registry/util/SerializeUtils.java
index efa44e107..2d53d65e8 100644
--- a/util/src/main/java/google/registry/util/SerializeUtils.java
+++ b/util/src/main/java/google/registry/util/SerializeUtils.java
@@ -65,5 +65,10 @@ public final class SerializeUtils {
}
}
+ /** Serializes an object then deserializes it. This is typically used in tests. */
+ public static Object serializeDeserialize(Object object) {
+ return deserialize(Object.class, serialize(object));
+ }
+
private SerializeUtils() {}
}