Ensure VKey is actually serializable (#1235)

* Ensure VKey is actually serializable

Tighten field type so that non-serializable object cannot be set as
sqlKey.

This would make it easier to make EppResource entities Serializable in
the future.
This commit is contained in:
Weimin Yu 2021-07-08 10:54:22 -04:00 committed by GitHub
parent 2195ba90fa
commit 61d029d955
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 21 additions and 15 deletions

View file

@ -21,6 +21,7 @@ import google.registry.model.billing.BillingEvent.OneTime;
import google.registry.model.billing.BillingEvent.Recurring; import google.registry.model.billing.BillingEvent.Recurring;
import google.registry.model.domain.DomainBase; import google.registry.model.domain.DomainBase;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import java.io.Serializable;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.persistence.AttributeOverride; import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides; import javax.persistence.AttributeOverrides;
@ -46,7 +47,7 @@ public abstract class BillingVKey<K> extends EppHistoryVKey<K, DomainBase> {
} }
@Override @Override
public Object createSqlKey() { public Serializable createSqlKey() {
return billingId; return billingId;
} }

View file

@ -21,6 +21,7 @@ import google.registry.model.domain.DomainBase;
import google.registry.model.domain.DomainHistory; import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.DomainHistory.DomainHistoryId; import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import java.io.Serializable;
import javax.persistence.Embeddable; import javax.persistence.Embeddable;
/** {@link VKey} for {@link HistoryEntry} which parent is {@link DomainBase}. */ /** {@link VKey} for {@link HistoryEntry} which parent is {@link DomainBase}. */
@ -35,7 +36,7 @@ public class DomainHistoryVKey extends EppHistoryVKey<HistoryEntry, DomainBase>
} }
@Override @Override
public Object createSqlKey() { public Serializable createSqlKey() {
return new DomainHistoryId(repoId, historyRevisionId); return new DomainHistoryId(repoId, historyRevisionId);
} }

View file

@ -101,7 +101,7 @@ public abstract class EppHistoryVKey<K, E extends EppResource> extends Immutable
return VKey.create(vKeyType, createSqlKey(), createOfyKey()); return VKey.create(vKeyType, createSqlKey(), createOfyKey());
} }
public abstract Object createSqlKey(); public abstract Serializable createSqlKey();
public abstract Key<K> createOfyKey(); public abstract Key<K> createOfyKey();
} }

View file

@ -37,7 +37,7 @@ public class VKey<T> extends ImmutableObject implements Serializable {
private static final long serialVersionUID = -5291472863840231240L; private static final long serialVersionUID = -5291472863840231240L;
// The SQL key for the referenced entity. // The SQL key for the referenced entity.
Object sqlKey; Serializable sqlKey;
// The objectify key for the referenced entity. // The objectify key for the referenced entity.
Key<T> ofyKey; Key<T> ofyKey;
@ -46,7 +46,7 @@ public class VKey<T> extends ImmutableObject implements Serializable {
VKey() {} VKey() {}
VKey(Class<? extends T> kind, Key<T> ofyKey, Object sqlKey) { VKey(Class<? extends T> kind, Key<T> ofyKey, Serializable sqlKey) {
this.kind = kind; this.kind = kind;
this.ofyKey = ofyKey; this.ofyKey = ofyKey;
this.sqlKey = sqlKey; this.sqlKey = sqlKey;
@ -57,7 +57,7 @@ public class VKey<T> extends ImmutableObject implements Serializable {
* *
* <p>Deprecated. Create symmetric keys with create() instead. * <p>Deprecated. Create symmetric keys with create() instead.
*/ */
public static <T> VKey<T> createSql(Class<T> kind, Object sqlKey) { public static <T> VKey<T> createSql(Class<T> kind, Serializable sqlKey) {
checkArgumentNotNull(kind, "kind must not be null"); checkArgumentNotNull(kind, "kind must not be null");
checkArgumentNotNull(sqlKey, "sqlKey must not be null"); checkArgumentNotNull(sqlKey, "sqlKey must not be null");
return new VKey<T>(kind, null, sqlKey); return new VKey<T>(kind, null, sqlKey);
@ -71,7 +71,7 @@ public class VKey<T> extends ImmutableObject implements Serializable {
} }
/** Creates a {@link VKey} which only contains both sql and ofy primary key. */ /** Creates a {@link VKey} which only contains both sql and ofy primary key. */
public static <T> VKey<T> create(Class<T> kind, Object sqlKey, Key<T> ofyKey) { public static <T> VKey<T> create(Class<T> kind, Serializable sqlKey, Key<T> ofyKey) {
checkArgumentNotNull(kind, "kind must not be null"); checkArgumentNotNull(kind, "kind must not be null");
checkArgumentNotNull(sqlKey, "sqlKey must not be null"); checkArgumentNotNull(sqlKey, "sqlKey must not be null");
checkArgumentNotNull(ofyKey, "ofyKey must not be null"); checkArgumentNotNull(ofyKey, "ofyKey must not be null");
@ -84,8 +84,8 @@ public class VKey<T> extends ImmutableObject implements Serializable {
* <p>IMPORTANT USAGE NOTE: Datastore entities that are not roots of entity groups (i.e. those * <p>IMPORTANT USAGE NOTE: Datastore entities that are not roots of entity groups (i.e. those
* that do not have a null parent in their Objectify keys) require the full entity group * that do not have a null parent in their Objectify keys) require the full entity group
* inheritance chain to be specified and thus cannot use this create method. You need to use * inheritance chain to be specified and thus cannot use this create method. You need to use
* {@link #create(Class, Object, Key)} instead and pass in the full, valid parent field in the * {@link #create(Class, Serializable, Key)} instead and pass in the full, valid parent field in
* Datastore key. * the Datastore key.
*/ */
public static <T> VKey<T> create(Class<T> kind, long id) { public static <T> VKey<T> create(Class<T> kind, long id) {
checkArgument( checkArgument(
@ -102,8 +102,8 @@ public class VKey<T> extends ImmutableObject implements Serializable {
* <p>IMPORTANT USAGE NOTE: Datastore entities that are not roots of entity groups (i.e. those * <p>IMPORTANT USAGE NOTE: Datastore entities that are not roots of entity groups (i.e. those
* that do not have a null parent in their Objectify keys) require the full entity group * that do not have a null parent in their Objectify keys) require the full entity group
* inheritance chain to be specified and thus cannot use this create method. You need to use * inheritance chain to be specified and thus cannot use this create method. You need to use
* {@link #create(Class, Object, Key)} instead and pass in the full, valid parent field in the * {@link #create(Class, Serializable, Key)} instead and pass in the full, valid parent field in
* Datastore key. * the Datastore key.
*/ */
public static <T> VKey<T> create(Class<T> kind, String name) { public static <T> VKey<T> create(Class<T> kind, String name) {
checkArgument( checkArgument(
@ -172,7 +172,7 @@ public class VKey<T> extends ImmutableObject implements Serializable {
throw new IllegalArgumentException("Missing value for last key of type " + lastClass); throw new IllegalArgumentException("Missing value for last key of type " + lastClass);
} }
Object sqlKey = getSqlKey(); Serializable sqlKey = getSqlKey();
Key<T> ofyKey = Key<T> ofyKey =
sqlKey instanceof Long sqlKey instanceof Long
? Key.create(lastKey, getKind(), (Long) sqlKey) ? Key.create(lastKey, getKind(), (Long) sqlKey)
@ -197,7 +197,7 @@ public class VKey<T> extends ImmutableObject implements Serializable {
} }
/** Returns the SQL primary key. */ /** Returns the SQL primary key. */
public Object getSqlKey() { public Serializable getSqlKey() {
checkState(sqlKey != null, "Attempting obtain a null SQL key."); checkState(sqlKey != null, "Attempting obtain a null SQL key.");
return this.sqlKey; return this.sqlKey;
} }

View file

@ -16,11 +16,13 @@ package google.registry.persistence.converter;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import java.io.Serializable;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.persistence.AttributeConverter; import javax.persistence.AttributeConverter;
/** Converts VKey to a string column. */ /** Converts VKey to a string column. */
public abstract class VKeyConverter<T, C> implements AttributeConverter<VKey<? extends T>, C> { public abstract class VKeyConverter<T, C extends Serializable>
implements AttributeConverter<VKey<? extends T>, C> {
@Override @Override
@Nullable @Nullable
public C convertToDatabaseColumn(@Nullable VKey<? extends T> attribute) { public C convertToDatabaseColumn(@Nullable VKey<? extends T> attribute) {

View file

@ -46,6 +46,7 @@ import google.registry.schema.replay.SqlOnlyEntity;
import google.registry.util.Clock; import google.registry.util.Clock;
import google.registry.util.Retrier; import google.registry.util.Retrier;
import google.registry.util.SystemSleeper; import google.registry.util.SystemSleeper;
import java.io.Serializable;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.Calendar; import java.util.Calendar;
@ -489,7 +490,8 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
loadByKey( loadByKey(
VKey.createSql( VKey.createSql(
possibleChild.getClass(), possibleChild.getClass(),
emf.getPersistenceUnitUtil().getIdentifier(possibleChild))); // Casting to Serializable is safe according to JPA (JSR 338 sec. 2.4).
(Serializable) emf.getPersistenceUnitUtil().getIdentifier(possibleChild)));
return returnValue; return returnValue;
} }