Remove references to Objectify (#1846)

This is not a complete removal of ofy as we still a dependency on it
(GaeUserIdConverter). But this PR removed it from a lot of places where
it's no longer needed.
This commit is contained in:
Lai Jiang 2022-11-09 11:31:00 -05:00 committed by GitHub
parent b578060c66
commit cc451bb7dc
53 changed files with 82 additions and 921 deletions

View file

@ -55,8 +55,6 @@ public class JpaDemoPipeline implements Serializable {
@ProcessElement
public void processElement() {
// AppEngineEnvironment is needed as long as JPA entity classes still depends
// on Objectify.
int result =
(Integer)
jpaTm()

View file

@ -64,7 +64,7 @@ public abstract class BillingEvent implements Serializable {
"amount",
"flags");
/** Returns the unique Objectify ID for the {@code OneTime} associated with this event. */
/** Returns the unique ID for the {@code OneTime} associated with this event. */
abstract long id();
/** Returns the UTC DateTime this event becomes billable. */

View file

@ -29,12 +29,12 @@ import google.registry.flows.EppException.SyntaxErrorException;
import google.registry.flows.EppException.UnimplementedProtocolVersionException;
import google.registry.flows.custom.EntityChanges;
import google.registry.model.EppResource;
import google.registry.model.adapters.CurrencyUnitAdapter.UnknownCurrencyException;
import google.registry.model.eppcommon.EppXmlTransformer;
import google.registry.model.eppinput.EppInput.WrongProtocolVersionException;
import google.registry.model.eppoutput.EppOutput;
import google.registry.model.host.InetAddressAdapter.IpVersionMismatchException;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.model.translators.CurrencyUnitAdapter.UnknownCurrencyException;
import google.registry.xml.XmlException;
import java.util.List;

View file

@ -19,7 +19,6 @@ import static com.google.common.base.Preconditions.checkState;
import static google.registry.model.IdService.allocateId;
import static google.registry.model.ModelUtils.getAllFields;
import com.googlecode.objectify.annotation.Id;
import google.registry.model.annotations.OfyIdAllocation;
import google.registry.util.TypeUtils.TypeInstantiator;
import java.lang.reflect.Field;
@ -56,14 +55,10 @@ public interface Buildable {
/** Build the instance. */
public S build() {
try {
// If this object has a Long or long Objectify @Id field that is not set, set it now. For
// any entity it has one and only one @Id field in its class hierarchy.
// If this object has a Long or long @OfyIdAllocation field that is not set, set it now.
Field idField =
getAllFields(instance.getClass()).values().stream()
.filter(
field ->
field.isAnnotationPresent(Id.class)
|| field.isAnnotationPresent(OfyIdAllocation.class))
.filter(field -> field.isAnnotationPresent(OfyIdAllocation.class))
.findFirst()
.orElse(null);
if (idField != null

View file

@ -21,8 +21,6 @@ import static java.util.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toList;
import com.google.common.base.Joiner;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.persistence.VKey;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
@ -76,7 +74,7 @@ public abstract class ImmutableObject implements Cloneable {
// 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;
@XmlTransient protected Integer hashCode;
private boolean equalsImmutableObject(ImmutableObject other) {
return getClass().equals(other.getClass())
@ -176,9 +174,6 @@ public abstract class ImmutableObject implements Cloneable {
/** Helper function to recursively hydrate an ImmutableObject. */
private static Object hydrate(Object value) {
if (value instanceof Key) {
return value;
}
if (value instanceof Map) {
return transformValues((Map<?, ?>) value, ImmutableObject::hydrate);
}

View file

@ -25,22 +25,13 @@ import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Streams;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.Parent;
import google.registry.persistence.VKey;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.AbstractList;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collection;
import java.util.Deque;
import java.util.LinkedHashMap;
@ -49,7 +40,6 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/** A collection of static methods that deal with reflection on model classes. */
@ -86,84 +76,6 @@ public class ModelUtils {
return ALL_FIELDS_CACHE.get(clazz);
}
/** Return a string representing the persisted schema of a type or enum. */
static String getSchema(Class<?> clazz) {
StringBuilder stringBuilder = new StringBuilder();
Stream<?> body;
if (clazz.isEnum()) {
stringBuilder.append("enum ");
body = Arrays.stream(clazz.getEnumConstants());
} else {
stringBuilder.append("class ");
body =
getAllFields(clazz)
.values()
.stream()
.filter(field -> !field.isAnnotationPresent(Ignore.class))
.map(
field -> {
String annotation =
field.isAnnotationPresent(Id.class)
? "@Id "
: field.isAnnotationPresent(Parent.class) ? "@Parent " : "";
String type =
field.getType().isArray()
? field.getType().getComponentType().getName() + "[]"
: field.getGenericType().toString().replaceFirst("class ", "");
return String.format("%s%s %s", annotation, type, field.getName());
});
}
return stringBuilder
.append(clazz.getName())
.append(" {\n ")
.append(body.map(Object::toString).sorted().collect(Collectors.joining(";\n ")))
.append(";\n}")
.toString();
}
/**
* Returns the set of Class objects of all persisted fields. This includes the parameterized
* type(s) of any fields (if any).
*/
static Set<Class<?>> getPersistedFieldTypes(Class<?> clazz) {
ImmutableSet.Builder<Class<?>> builder = new ImmutableSet.Builder<>();
for (Field field : getAllFields(clazz).values()) {
// Skip fields that aren't persisted to Datastore.
if (field.isAnnotationPresent(Ignore.class)) {
continue;
}
// If the field's type is the same as the field's class object, then it's a non-parameterized
// type, and thus we just add it directly. We also don't bother looking at the parameterized
// types of Key and VKey objects, since they are just references to other objects and don't
// actually embed themselves in the persisted object anyway.
Class<?> fieldClazz = field.getType();
Type fieldType = field.getGenericType();
if (VKey.class.equals(fieldClazz)) {
continue;
}
builder.add(fieldClazz);
if (fieldType.equals(fieldClazz) || Key.class.equals(clazz)) {
continue;
}
// If the field is a parameterized type, then also add the parameterized field.
if (fieldType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) fieldType;
for (Type actualType : parameterizedType.getActualTypeArguments()) {
if (actualType instanceof Class<?>) {
builder.add((Class<?>) actualType);
} else {
// We intentionally ignore types that are parameterized on non-concrete types. In theory
// we could have collections embedded within collections, but Objectify does not allow
// that.
}
}
}
}
return builder.build();
}
/** Retrieves a field value via reflection. */
static Object getFieldValue(Object instance, Field field) {
try {

View file

@ -29,7 +29,6 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import com.googlecode.objectify.Key;
import google.registry.config.RegistryEnvironment;
import google.registry.model.common.GaeUserIdConverter;
import google.registry.model.pricing.StaticPremiumListPricingEngine;
@ -199,9 +198,8 @@ public final class OteAccountBuilder {
*
* <p>Use this to set up registrar fields.
*
* <p>NOTE: DO NOT change anything that would affect the {@link Key#create} result on Registrars.
* If you want to make this function public, add a check that the Key.create on the registrars
* hasn't changed.
* <p>NOTE: DO NOT change anything that would affect the result of {@link Registrar#createVKey()}
* . If you want to make this function public, add a check that the value hasn't changed.
*
* @param func a function setting the requested fields on Registrar Builders. Will be applied to
* all the Registrars.

View file

@ -26,7 +26,6 @@ import com.google.common.collect.HashMultiset;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multiset;
import com.googlecode.objectify.Key;
import google.registry.model.domain.DomainCommand;
import google.registry.model.domain.fee.FeeCreateCommandExtension;
import google.registry.model.domain.launch.LaunchCreateExtension;
@ -202,7 +201,7 @@ public class OteStats {
try {
record(historyEntry);
} catch (XmlException e) {
throw new RuntimeException("Couldn't parse history entry " + Key.create(historyEntry), e);
throw new RuntimeException("Couldn't parse history entry " + historyEntry.createVKey(), e);
}
// Break out early if all tests were passed.
if (wereAllTestsPassed()) {

View file

@ -23,8 +23,8 @@ import java.io.Serializable;
* the migration. Note that only objects loaded from the SQL database need serialization support.
*
* <p>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}.
* classes we use, either directly or indirectly, including those in Java libraries, Guava, and
* Hibernate are {@code Serializable}.
*
* <p>The {@code serialVersionUID} field has also been omitted in the implementing classes, since
* they are not used for persistence.

View file

@ -14,7 +14,6 @@
package google.registry.model;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.util.PreconditionsUtils;
import javax.persistence.Access;
import javax.persistence.AccessType;
@ -39,7 +38,6 @@ public abstract class UpdateAutoTimestampEntity extends ImmutableObject
// Prevents subclasses from unexpectedly accessing as property (e.g., Host), which would
// require an unnecessary non-private setter method.
@Access(AccessType.FIELD)
@Ignore
UpdateAutoTimestamp updateTimestamp = UpdateAutoTimestamp.create(null);
/** Get the {@link UpdateAutoTimestamp} for this entity. */

View file

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.model.translators;
package google.registry.model.adapters;
import static com.google.common.base.Strings.nullToEmpty;

View file

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.model.translators;
package google.registry.model.adapters;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.adapters.XmlAdapter;

View file

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.model.translators;
package google.registry.model.adapters;
import google.registry.model.eppcommon.StatusValue;

View file

@ -1,29 +0,0 @@
// Copyright 2017 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.annotations;
import com.googlecode.objectify.annotation.Entity;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation for an Objectify {@link Entity} to indicate that it should be exported to BigQuery.
*/
@DeleteAfterMigration
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface ReportedOn {}

View file

@ -29,7 +29,6 @@ import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
import google.registry.model.annotations.OfyIdAllocation;
import google.registry.model.annotations.ReportedOn;
import google.registry.model.common.TimeOfYear;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.GracePeriod;
@ -594,7 +593,6 @@ public abstract class BillingEvent extends ImmutableObject
* <p>This is implemented as a separate event rather than a bit on BillingEvent in order to
* preserve the immutability of billing events.
*/
@ReportedOn
@Entity(name = "BillingCancellation")
@Table(
indexes = {

View file

@ -25,7 +25,6 @@ import static org.joda.time.DateTimeZone.UTC;
import com.google.common.base.Splitter;
import com.google.common.collect.ContiguousSet;
import com.google.common.collect.Range;
import com.googlecode.objectify.annotation.Index;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
import java.util.List;
@ -50,7 +49,6 @@ public class TimeOfYear extends ImmutableObject implements UnsafeSerializable {
* The time as "month day millis" with all fields left-padded with zeroes so that lexographic
* sorting will do the right thing.
*/
@Index
String timeString;
/**

View file

@ -241,8 +241,8 @@ public class ContactBase extends EppResource
* Postal info for the contact.
*
* <p>The XML marshalling expects the {@link PostalInfo} objects in a list, but we can't actually
* persist them to Datastore that way because Objectify can't handle collections of embedded
* objects that themselves contain collections, and there's a list of streets inside. This method
* persist them directly due to legacy reasons (Objectify can't handle collections of embedded
* objects that themselves contain collections, and there's a list of streets inside). This method
* transforms the persisted format to the XML format for marshalling.
*/
@XmlElement(name = "postalInfo")

View file

@ -16,7 +16,6 @@ package google.registry.model.domain;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
import google.registry.model.contact.Contact;
@ -69,7 +68,7 @@ public class DesignatedContact extends ImmutableObject implements UnsafeSerializ
Type type;
@Ignore VKey<Contact> contactVKey;
VKey<Contact> contactVKey;
public Type getType() {
return type;

View file

@ -18,7 +18,6 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.EppResource;
import google.registry.model.domain.GracePeriod.GracePeriodHistory;
import google.registry.model.domain.secdns.DomainDsData;
@ -115,7 +114,6 @@ public class DomainHistory extends HistoryEntry {
updatable = false)
})
// HashSet rather than ImmutableSet so that Hibernate can fill them out lazily on request
@Ignore
Set<DomainDsDataHistory> dsDataHistories = new HashSet<>();
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)

View file

@ -20,7 +20,7 @@
@XmlJavaTypeAdapter(CurrencyUnitAdapter.class)
package google.registry.model.domain.fee06;
import google.registry.model.translators.CurrencyUnitAdapter;
import google.registry.model.adapters.CurrencyUnitAdapter;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlNs;

View file

@ -20,7 +20,7 @@
@XmlJavaTypeAdapter(CurrencyUnitAdapter.class)
package google.registry.model.domain.fee11;
import google.registry.model.translators.CurrencyUnitAdapter;
import google.registry.model.adapters.CurrencyUnitAdapter;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlNs;

View file

@ -22,7 +22,7 @@
@XmlJavaTypeAdapter(UtcDateTimeAdapter.class)})
package google.registry.model.domain.fee12;
import google.registry.model.translators.CurrencyUnitAdapter;
import google.registry.model.adapters.CurrencyUnitAdapter;
import google.registry.xml.UtcDateTimeAdapter;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;

View file

@ -17,8 +17,8 @@ package google.registry.model.domain.rgp;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import com.google.common.collect.ImmutableMap;
import google.registry.model.translators.EnumToAttributeAdapter;
import google.registry.model.translators.EnumToAttributeAdapter.EppEnum;
import google.registry.model.adapters.EnumToAttributeAdapter;
import google.registry.model.adapters.EnumToAttributeAdapter.EppEnum;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import javax.xml.bind.annotation.XmlAttribute;

View file

@ -20,14 +20,14 @@ import static com.google.common.base.Strings.nullToEmpty;
import com.google.common.collect.ImmutableSet;
import google.registry.model.EppResource;
import google.registry.model.adapters.EnumToAttributeAdapter.EppEnum;
import google.registry.model.adapters.StatusValueAdapter;
import google.registry.model.contact.Contact;
import google.registry.model.contact.ContactBase;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainBase;
import google.registry.model.host.Host;
import google.registry.model.host.HostBase;
import google.registry.model.translators.EnumToAttributeAdapter.EppEnum;
import google.registry.model.translators.StatusValueAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
/**

View file

@ -14,26 +14,26 @@
package google.registry.model.host;
import com.googlecode.objectify.annotation.Entity;
import google.registry.model.EppResource.ForeignKeyedEppResource;
import google.registry.model.annotations.ExternalMessagingName;
import google.registry.model.annotations.ReportedOn;
import google.registry.persistence.VKey;
import google.registry.persistence.WithVKey;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.Table;
/**
* A persistable Host resource including mutable and non-mutable fields.
*
* <p>The {@link javax.persistence.Id} of the Host is the repoId.
*/
@ReportedOn
@Entity
@javax.persistence.Entity(name = "Host")
@javax.persistence.Table(
@Entity(name = "Host")
@Table(
name = "Host",
/**
/*
* A gin index defined on the inet_addresses field ({@link HostBase#inetAddresses} cannot be
* declared here because JPA/Hibernate does not support index type specification. As a result,
* the hibernate-generated schema (which is for reference only) does not have this index.
@ -45,10 +45,10 @@ import javax.persistence.AccessType;
* doing either.
*/
indexes = {
@javax.persistence.Index(columnList = "hostName"),
@javax.persistence.Index(columnList = "creationTime"),
@javax.persistence.Index(columnList = "deletionTime"),
@javax.persistence.Index(columnList = "currentSponsorRegistrarId")
@Index(columnList = "hostName"),
@Index(columnList = "creationTime"),
@Index(columnList = "deletionTime"),
@Index(columnList = "currentSponsorRegistrarId")
})
@ExternalMessagingName("host")
@WithVKey(String.class)
@ -56,7 +56,7 @@ import javax.persistence.AccessType;
public class Host extends HostBase implements ForeignKeyedEppResource {
@Override
@javax.persistence.Id
@Id
@Access(AccessType.PROPERTY) // to tell it to use the non-default property-as-ID
public String getRepoId() {
return super.getRepoId();

View file

@ -21,27 +21,17 @@ import static google.registry.util.TypeUtils.hasAnnotation;
import com.google.appengine.api.datastore.AsyncDatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceConfig;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.ObjectifyFactory;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.EntitySubclass;
import com.googlecode.objectify.impl.translate.TranslatorFactory;
import com.googlecode.objectify.impl.translate.opt.joda.MoneyStringTranslatorFactory;
import google.registry.config.RegistryEnvironment;
import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
import google.registry.model.annotations.DeleteAfterMigration;
import google.registry.model.common.GaeUserIdConverter;
import google.registry.model.translators.BloomFilterOfStringTranslatorFactory;
import google.registry.model.translators.CidrAddressBlockTranslatorFactory;
import google.registry.model.translators.CurrencyUnitTranslatorFactory;
import google.registry.model.translators.DurationTranslatorFactory;
import google.registry.model.translators.InetAddressTranslatorFactory;
import google.registry.model.translators.ReadableInstantUtcTranslatorFactory;
/**
* An instance of Ofy, obtained via {@code #auditedOfy()}, should be used to access all persistable
@ -86,11 +76,6 @@ public class ObjectifyService {
// The "false" argument means that we are not using the v5-style Objectify embedded entities.
com.googlecode.objectify.ObjectifyService.setFactory(
new ObjectifyFactory(false) {
@Override
public Objectify begin() {
return new SessionKeyExposingObjectify(this);
}
@Override
protected AsyncDatastoreService createRawAsyncDatastoreService(
DatastoreServiceConfig cfg) {
@ -103,26 +88,9 @@ public class ObjectifyService {
}
});
// Translators must be registered before any entities can be registered.
registerTranslators();
registerEntityClasses(ImmutableSet.of(GaeUserIdConverter.class));
}
/** Register translators that allow less common types to be stored directly in Datastore. */
private static void registerTranslators() {
for (TranslatorFactory<?> translatorFactory :
ImmutableList.of(
new BloomFilterOfStringTranslatorFactory(),
new CidrAddressBlockTranslatorFactory(),
new CurrencyUnitTranslatorFactory(),
new DurationTranslatorFactory(),
new InetAddressTranslatorFactory(),
new MoneyStringTranslatorFactory(),
new ReadableInstantUtcTranslatorFactory())) {
factory().getTranslators().add(translatorFactory);
}
}
/** Register classes that can be persisted via Objectify as Datastore entities. */
private static void registerEntityClasses(
ImmutableSet<Class<? extends ImmutableObject>> entityClasses) {

View file

@ -25,7 +25,6 @@ import com.google.appengine.api.datastore.DatastoreTimeoutException;
import com.google.appengine.api.taskqueue.TransientFailureException;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import com.googlecode.objectify.Key;
@ -97,16 +96,6 @@ public class Ofy {
return ofy().factory();
}
/**
* Returns keys read by Objectify during this transaction.
*
* <p>This won't include the keys of asynchronous save and delete operations that haven't been
* reaped.
*/
public ImmutableSet<Key<?>> getSessionKeys() {
return ((SessionKeyExposingObjectify) ofy()).getSessionKeys();
}
/** Clears the session cache. */
public void clearSessionCache() {
ofy().clear();
@ -272,7 +261,6 @@ public class Ofy {
});
return work.getResult();
} catch (TransientFailureException
| TimestampInversionException
| DatastoreTimeoutException
| DatastoreFailureException e) {
// TransientFailureExceptions come from task queues and always mean nothing committed.

View file

@ -1,35 +0,0 @@
// Copyright 2017 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.ofy;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.ObjectifyFactory;
import com.googlecode.objectify.impl.ObjectifyImpl;
import google.registry.model.annotations.DeleteAfterMigration;
/** Registry-specific Objectify subclass that exposes the keys used in the current session. */
@DeleteAfterMigration
public class SessionKeyExposingObjectify extends ObjectifyImpl<SessionKeyExposingObjectify> {
public SessionKeyExposingObjectify(ObjectifyFactory factory) {
super(factory);
}
/** Expose the protected method that provides the keys read, saved or deleted in a session. */
ImmutableSet<Key<?>> getSessionKeys() {
return ImmutableSet.copyOf(getSession().keys());
}
}

View file

@ -1,62 +0,0 @@
// Copyright 2017 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.ofy;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.Objectify;
import google.registry.model.UpdateAutoTimestampEntity;
import google.registry.model.annotations.DeleteAfterMigration;
import java.util.Arrays;
import java.util.Map;
import org.joda.time.DateTime;
/**
* Exception when trying to write to Datastore with a timestamp that is inconsistent with a partial
* ordering on transactions that touch the same entities.
*/
@DeleteAfterMigration
class TimestampInversionException extends RuntimeException {
static String getFileAndLine(StackTraceElement callsite) {
return callsite.getFileName() + ":" + callsite.getLineNumber();
}
TimestampInversionException(
DateTime transactionTime, Map<Key<UpdateAutoTimestampEntity>, DateTime> problematicRoots) {
this(transactionTime, "entities rooted under:\n" + problematicRoots);
}
TimestampInversionException(DateTime transactionTime, DateTime updateTimestamp) {
this(transactionTime, String.format("update timestamp (%s)", updateTimestamp));
}
private TimestampInversionException(DateTime transactionTime, String problem) {
super(
String.format(
"Timestamp inversion between transaction time (%s) and %s\n%s",
transactionTime,
problem,
getFileAndLine(
Arrays.stream(new Exception().getStackTrace())
.filter(
element ->
!element
.getClassName()
.startsWith(Objectify.class.getPackage().getName())
&& !element.getClassName().startsWith(Ofy.class.getName()))
.findFirst()
.get())));
}
}

View file

@ -29,22 +29,21 @@ import javax.xml.bind.annotation.XmlSchema;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
/*
* This package defines all entities which are managed via EPP XML and persisted to the Datastore
* via Objectify.
* This package defines all entities which are managed via EPP XML and persisted to SQL via
* Hibernate.
*
* <p>All first class entities are represented as a resource class - {@link
* google.registry.model.domain.Domain}, {@link google.registry.model.host.Host}, {@link
* google.registry.model.contact.Contact}, and {@link
* google.registry.model.registrar.Registrar}. Resource objects are written in a single shared
* entity group per TLD. All commands that operate on those entities are grouped in a "Command"
* class- {@link google.registry.model.domain.DomainCommand}, {@link
* google.registry.model.registrar.Registrar}. All commands that operate on those entities are
* grouped in a "Command" class- {@link google.registry.model.domain.DomainCommand}, {@link
* google.registry.model.host.HostCommand}, {@link google.registry.model.contact.ContactCommand}.
* The Resource does double duty as both the persisted representation and as the XML-marshallable
* object returned in respond to Info commands.
*
* <p>Command classes are never persisted, and the Objectify annotations on the Create and Update
* classes are purely for the benefit of the derived Resource classes that inherit from them.
* Whenever a command that mutates the model is executed, a HistoryEvent is stored with the affected
* Resource as its Datastore parent. All history entries have an indexed modification time field so
* that the history can be read in chronological order.
* <p>Command classes are never persisted.
* Whenever a command that mutates the model is executed, a {@link
* google.registry.reporting.HistoryEvent} is stored with the affected Resource as an embedded
* field. All history entries have an indexed modification time field so that the history can be
* read in chronological order.
*/

View file

@ -35,7 +35,6 @@ 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.ReportedOn;
import google.registry.model.registrar.RegistrarPoc.RegistrarPocId;
import google.registry.persistence.VKey;
import java.io.Serializable;
@ -57,7 +56,6 @@ import javax.persistence.Table;
* *MUST* also modify the persisted Registrar entity with {@link Registrar#contactsRequireSyncing}
* set to true.
*/
@ReportedOn
@Entity
@Table(indexes = {@Index(columnList = "gaeUserId", name = "registrarpoc_gae_user_id_idx")})
@IdClass(RegistrarPocId.class)

View file

@ -23,8 +23,6 @@ import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableMap;
import com.google.common.hash.BloomFilter;
import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
import google.registry.model.annotations.ReportedOn;
import google.registry.model.tld.Registry;
import google.registry.model.tld.label.PremiumList.PremiumEntry;
import java.io.Serializable;
@ -35,6 +33,7 @@ import java.util.Map;
import java.util.Objects;
import javax.annotation.Nullable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Index;
import javax.persistence.Table;
import javax.persistence.Transient;
@ -49,8 +48,7 @@ import org.joda.money.Money;
* succeeds, we will end up with having two exact same premium lists that differ only by revisionId.
* This is fine though, because we only use the list with the highest revisionId.
*/
@ReportedOn
@javax.persistence.Entity
@Entity
@Table(indexes = {@Index(columnList = "name", name = "premiumlist_name_idx")})
public final class PremiumList extends BaseDomainLabelList<BigDecimal, PremiumEntry> {
@ -64,7 +62,7 @@ public final class PremiumList extends BaseDomainLabelList<BigDecimal, PremiumEn
* from the immutability contract so we can modify it after construction and we have to handle the
* database processing on our own so we can detach it after load.
*/
@ImmutableObject.Insignificant @Transient ImmutableMap<String, BigDecimal> labelsToPrices;
@Insignificant @Transient ImmutableMap<String, BigDecimal> labelsToPrices;
@Column(nullable = false)
BloomFilter<String> bloomFilter;
@ -116,11 +114,11 @@ public final class PremiumList extends BaseDomainLabelList<BigDecimal, PremiumEn
* A premium list entry entity, persisted to Cloud SQL. Each instance represents the price of a
* single label on a given TLD.
*/
@javax.persistence.Entity(name = "PremiumEntry")
@Entity(name = "PremiumEntry")
public static class PremiumEntry extends DomainLabelEntry<BigDecimal, PremiumList.PremiumEntry>
implements Buildable, Serializable {
@ImmutableObject.Insignificant @javax.persistence.Id Long revisionId;
@Insignificant @javax.persistence.Id Long revisionId;
@Column(nullable = false)
BigDecimal price;

View file

@ -29,11 +29,7 @@ import org.joda.time.DateTime;
@XmlTransient
@MappedSuperclass
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
* will always be non-null.
*/
/** The status of the current or last transfer. Can be null if never transferred. */
@XmlElement(name = "trStatus")
@Enumerated(EnumType.STRING)
TransferStatus transferStatus;

View file

@ -1,61 +0,0 @@
// Copyright 2017 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.translators;
import com.googlecode.objectify.impl.Path;
import com.googlecode.objectify.impl.Property;
import com.googlecode.objectify.impl.translate.CreateContext;
import com.googlecode.objectify.impl.translate.LoadContext;
import com.googlecode.objectify.impl.translate.SaveContext;
import com.googlecode.objectify.impl.translate.ValueTranslator;
import com.googlecode.objectify.impl.translate.ValueTranslatorFactory;
import google.registry.util.TypeUtils.TypeInstantiator;
import java.lang.reflect.Type;
/** Common boilerplate for translator factories. */
public abstract class AbstractSimpleTranslatorFactory<P, D> extends ValueTranslatorFactory<P, D> {
public AbstractSimpleTranslatorFactory(Class<P> clazz) {
super(clazz);
}
@Override
protected final ValueTranslator<P, D> createSafe(
Path path, Property property, Type type, CreateContext ctx) {
return new ValueTranslator<P, D>(path, new TypeInstantiator<D>(getClass()){}.getExactType()) {
SimpleTranslator<P, D> simpleTranslator = createTranslator();
@Override
protected P loadValue(D datastoreValue, LoadContext ctx) {
return simpleTranslator.loadValue(datastoreValue);
}
@Override
protected D saveValue(P pojoValue, SaveContext ctx) {
return simpleTranslator.saveValue(pojoValue);
}
};
}
/** Translator with reduced boilerplate. */
interface SimpleTranslator<P, D> {
P loadValue(D datastoreValue);
D saveValue(P pojoValue);
}
abstract SimpleTranslator<P, D> createTranslator();
}

View file

@ -1,90 +0,0 @@
// Copyright 2017 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.translators;
import static com.google.common.hash.Funnels.unencodedCharsFunnel;
import static com.googlecode.objectify.repackaged.gentyref.GenericTypeReflector.erase;
import static com.googlecode.objectify.repackaged.gentyref.GenericTypeReflector.getTypeParameter;
import com.google.appengine.api.datastore.Blob;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnel;
import com.google.common.reflect.TypeToken;
import com.googlecode.objectify.impl.Path;
import com.googlecode.objectify.impl.Property;
import com.googlecode.objectify.impl.translate.CreateContext;
import com.googlecode.objectify.impl.translate.LoadContext;
import com.googlecode.objectify.impl.translate.SaveContext;
import com.googlecode.objectify.impl.translate.Translator;
import com.googlecode.objectify.impl.translate.TranslatorFactory;
import com.googlecode.objectify.impl.translate.ValueTranslator;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Type;
import javax.annotation.Nullable;
/** Stores CharSequence {@link BloomFilter}s as blobs. */
public class BloomFilterOfStringTranslatorFactory
implements TranslatorFactory<BloomFilter<String>> {
@Override
public Translator<BloomFilter<String>> create(
Path path, Property property, Type type, CreateContext ctx) {
if (!BloomFilter.class.equals(erase(type))) {
return null; // Skip me and try to find another matching translator
}
Type fieldBloomFilterType = getTypeParameter(type, BloomFilter.class.getTypeParameters()[0]);
if (fieldBloomFilterType == null) {
return null; // No type information is available
}
if (!TypeToken.of(String.class).getType().equals(fieldBloomFilterType)) {
return null; // We can only handle BloomFilters of CharSequences
}
return new ValueTranslator<BloomFilter<String>, Blob>(path, Blob.class) {
@Override
@Nullable
protected BloomFilter<String> loadValue(Blob value, LoadContext ctx) {
if (value == null) {
return null;
}
try {
@SuppressWarnings("unchecked")
Funnel<String> castedFunnel = (Funnel<String>) (Funnel<?>) unencodedCharsFunnel();
return BloomFilter.readFrom(new ByteArrayInputStream(value.getBytes()), castedFunnel);
} catch (IOException e) {
throw new IllegalStateException("Error loading Bloom filter data", e);
}
}
@Override
@Nullable
protected Blob saveValue(BloomFilter<String> value, SaveContext ctx) {
if (value == null) {
return null;
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
value.writeTo(bos);
} catch (IOException e) {
throw new IllegalStateException("Error saving Bloom filter data", e);
}
return new Blob(bos.toByteArray());
}
};
}
}

View file

@ -1,40 +0,0 @@
// Copyright 2017 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.translators;
import google.registry.util.CidrAddressBlock;
/** Stores {@link CidrAddressBlock} as a canonicalized string. */
public class CidrAddressBlockTranslatorFactory
extends AbstractSimpleTranslatorFactory<CidrAddressBlock, String> {
public CidrAddressBlockTranslatorFactory() {
super(CidrAddressBlock.class);
}
@Override
SimpleTranslator<CidrAddressBlock, String> createTranslator() {
return new SimpleTranslator<CidrAddressBlock, String>(){
@Override
public CidrAddressBlock loadValue(String datastoreValue) {
return CidrAddressBlock.create(datastoreValue);
}
@Override
public String saveValue(CidrAddressBlock pojoValue) {
return pojoValue.toString();
}};
}
}

View file

@ -1,40 +0,0 @@
// Copyright 2017 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.translators;
import org.joda.money.CurrencyUnit;
/** Stores {@link CurrencyUnit} as a canonicalized string. */
public class CurrencyUnitTranslatorFactory
extends AbstractSimpleTranslatorFactory<CurrencyUnit, String> {
public CurrencyUnitTranslatorFactory() {
super(CurrencyUnit.class);
}
@Override
SimpleTranslator<CurrencyUnit, String> createTranslator() {
return new SimpleTranslator<CurrencyUnit, String>(){
@Override
public CurrencyUnit loadValue(String datastoreValue) {
return CurrencyUnit.of(datastoreValue);
}
@Override
public String saveValue(CurrencyUnit pojoValue) {
return pojoValue.toString();
}};
}
}

View file

@ -1,39 +0,0 @@
// Copyright 2017 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.translators;
import org.joda.time.Duration;
/** Stores {@link Duration} as a canonicalized string. */
public class DurationTranslatorFactory extends AbstractSimpleTranslatorFactory<Duration, String> {
public DurationTranslatorFactory() {
super(Duration.class);
}
@Override
protected SimpleTranslator<Duration, String> createTranslator() {
return new SimpleTranslator<Duration, String>() {
@Override
public Duration loadValue(String datastoreValue) {
return Duration.parse(datastoreValue);
}
@Override
public String saveValue(Duration pojoValue) {
return pojoValue.toString();
}};
}
}

View file

@ -1,165 +0,0 @@
// Copyright 2017 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.translators;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.googlecode.objectify.repackaged.gentyref.GenericTypeReflector.erase;
import static com.googlecode.objectify.repackaged.gentyref.GenericTypeReflector.getTypeParameter;
import static google.registry.util.CollectionUtils.nullToEmpty;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Ordering;
import com.google.common.reflect.TypeToken;
import com.googlecode.objectify.impl.Node;
import com.googlecode.objectify.impl.Path;
import com.googlecode.objectify.impl.Property;
import com.googlecode.objectify.impl.translate.CreateContext;
import com.googlecode.objectify.impl.translate.ListNodeTranslator;
import com.googlecode.objectify.impl.translate.LoadContext;
import com.googlecode.objectify.impl.translate.SaveContext;
import com.googlecode.objectify.impl.translate.SkipException;
import com.googlecode.objectify.impl.translate.Translator;
import com.googlecode.objectify.impl.translate.TranslatorFactory;
import com.googlecode.objectify.impl.translate.TranslatorRegistry;
import java.lang.reflect.Type;
import java.util.Map;
import javax.annotation.Nullable;
/**
* Abstract Objectify translator for {@link ImmutableSortedMap} fields.
*
* <p>This class should be extended for each set of concrete key/value types you wish to support.
* This translator will only apply to {@code ImmutableSortedMap} model fields that have precicely
* the same type parameters you specified.
*
* <p>This translator serves a similar purpose to
* {@link com.googlecode.objectify.impl.translate.MapifyTranslatorFactory @Mapify}. Except this
* maintains perfect immutability of the field value. Subclasses may override the
* {@link #transformBeforeSave(ImmutableSortedMap)} methods to perform mutation on a per-concrete
* type basis. This abstraction is also more readable than {@code @Mapify} because it shifts the
* boilerplate into translator magic, rather than convoluting model data structures.
*
* <h3>Entity Data Layout</h3>
*
* <p>For example, if you had an {@code ImmutableSortedMap<String, String>} on a field named
* {@code field}, then this would look like:<pre> {@code
*
* field.key: key1 -> key2
* field.value: value1 -> value2}</pre>
*
* <p>If you had an {@code ImmutableSortedMap<String, EmbeddedClass>} on a field named
* {@code field}, where {@code EmbeddedClass} defines two {@code foo} and {@code bar} fields, then
* the embedded properties might look like:<pre> {@code
*
* field.key: key1 -> key2
* field.value.foo: foo1 -> foo2
* field.value.bar: bar1 -> bar2}</pre>
*
* @param <K> key type for sorted map which must be {@link Comparable}
* @param <V> value type for sorted map
*/
abstract class ImmutableSortedMapTranslatorFactory<K extends Comparable<? super K>, V>
implements TranslatorFactory<ImmutableSortedMap<K, V>> {
private final TypeToken<K> keyType = new TypeToken<K>(getClass()) {};
private final TypeToken<V> valueType = new TypeToken<V>(getClass()) {};
private final String keyProperty;
private final String valueProperty;
ImmutableSortedMapTranslatorFactory() {
this("key", "value");
}
/** Constructs a instance that's compatible with models migrated from {@code @Mapify}. */
ImmutableSortedMapTranslatorFactory(String keyProperty, String valueProperty) {
this.keyProperty = checkNotNull(keyProperty);
this.valueProperty = checkNotNull(valueProperty);
}
/** Allows for changing the field data structure before it's written to the raw entity object. */
ImmutableSortedMap<K, V> transformBeforeSave(ImmutableSortedMap<K, V> map) {
return map;
}
@Override
public final Translator<ImmutableSortedMap<K, V>>
create(Path path, Property property, Type type, CreateContext ctx) {
if (!ImmutableSortedMap.class.equals(erase(type))) {
return null; // skip me and try to find another matching translator
}
Type fieldKeyType = getTypeParameter(type, ImmutableSortedMap.class.getTypeParameters()[0]);
Type fieldValueType = getTypeParameter(type, ImmutableSortedMap.class.getTypeParameters()[1]);
if (fieldKeyType == null || fieldValueType == null) {
return null; // no type information is available
}
if (!keyType.isSupertypeOf(fieldKeyType) || !valueType.isSupertypeOf(fieldValueType)) {
return null; // this ImmutableSortedMap does not have the same concrete component types
}
ctx.enterCollection(path);
ctx.enterEmbed(path);
try {
// The component types can also be translated by Objectify!
TranslatorRegistry translators = ctx.getFactory().getTranslators();
final Translator<K> keyTranslator =
translators.create(path.extend(keyProperty), property, fieldKeyType, ctx);
final Translator<V> valueTranslator =
translators.create(path.extend(valueProperty), property, fieldValueType, ctx);
return new ListNodeTranslator<ImmutableSortedMap<K, V>>() {
@Override
protected ImmutableSortedMap<K, V> loadList(Node node, LoadContext ctx) {
ImmutableSortedMap.Builder<K, V> map =
new ImmutableSortedMap.Builder<>(Ordering.natural());
for (Node child : node) {
try {
map.put(keyTranslator.load(child.get(keyProperty), ctx),
valueTranslator.load(child.get(valueProperty), ctx));
} catch (SkipException e) {
// no problem, just skip that one
}
}
return map.build();
}
@Override
protected Node saveList(
@Nullable ImmutableSortedMap<K, V> mapFromPojo,
Path path,
boolean index,
SaveContext ctx) {
checkState(!index, "At path %s: Index not allowed", path);
ImmutableSortedMap<K, V> mapToSave = transformBeforeSave(
ImmutableSortedMap.copyOfSorted(nullToEmpty(mapFromPojo)));
if (mapToSave.isEmpty()) {
throw new SkipException(); // Datastore doesn't store empty lists
}
Node node = new Node(path);
for (Map.Entry<K, V> entry : mapToSave.entrySet()) {
Node item = new Node(path);
item.put(keyProperty,
keyTranslator.save(entry.getKey(), path.extend(keyProperty), index, ctx));
item.put(valueProperty,
valueTranslator.save(entry.getValue(), path.extend(valueProperty), index, ctx));
node.addToList(item);
}
return node;
}
};
} finally {
ctx.exitEmbed();
ctx.exitCollection();
}
}
}

View file

@ -1,41 +0,0 @@
// Copyright 2017 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.translators;
import com.google.common.net.InetAddresses;
import java.net.InetAddress;
/** Stores {@link InetAddress} as a canonicalized string. */
public class InetAddressTranslatorFactory
extends AbstractSimpleTranslatorFactory<InetAddress, String> {
public InetAddressTranslatorFactory() {
super(InetAddress.class);
}
@Override
SimpleTranslator<InetAddress, String> createTranslator() {
return new SimpleTranslator<InetAddress, String>() {
@Override
public InetAddress loadValue(String datastoreValue) {
return InetAddresses.forString(datastoreValue);
}
@Override
public String saveValue(InetAddress pojoValue) {
return pojoValue.getHostAddress();
}};
}
}

View file

@ -1,65 +0,0 @@
// Copyright 2017 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.translators;
import com.googlecode.objectify.impl.Path;
import com.googlecode.objectify.impl.Property;
import com.googlecode.objectify.impl.TypeUtils;
import com.googlecode.objectify.impl.translate.CreateContext;
import com.googlecode.objectify.impl.translate.LoadContext;
import com.googlecode.objectify.impl.translate.SaveContext;
import com.googlecode.objectify.impl.translate.ValueTranslator;
import com.googlecode.objectify.impl.translate.ValueTranslatorFactory;
import com.googlecode.objectify.repackaged.gentyref.GenericTypeReflector;
import java.lang.reflect.Constructor;
import java.lang.reflect.Type;
import java.util.Date;
import org.joda.time.DateTimeZone;
import org.joda.time.ReadableInstant;
/**
* Stores Joda {@link ReadableInstant} types ({@code DateTime}, etc) as a {@link java.util.Date}.
*
* <p>This is a fork of the {@code ReadableInstantTranslatorFactory} that comes bundled with
* Objectify. The original reifies a {@link ReadableInstant} using the machine's local time
* zone. This version always uses UTC.
*/
public class ReadableInstantUtcTranslatorFactory
extends ValueTranslatorFactory<ReadableInstant, Date> {
public ReadableInstantUtcTranslatorFactory() {
super(ReadableInstant.class);
}
@Override
protected ValueTranslator<ReadableInstant, Date> createSafe(
Path path, Property property, Type type, CreateContext ctx) {
final Class<?> clazz = GenericTypeReflector.erase(type);
return new ValueTranslator<ReadableInstant, Date>(path, Date.class) {
@Override
protected ReadableInstant loadValue(Date value, LoadContext ctx) {
// All the Joda instants have a constructor that will take a Date and timezone.
Constructor<?> ctor = TypeUtils.getConstructor(clazz, Object.class, DateTimeZone.class);
return (ReadableInstant) TypeUtils.newInstance(ctor, value, DateTimeZone.UTC);
}
@Override
protected Date saveValue(ReadableInstant value, SaveContext ctx) {
return value.toInstant().toDate();
}
};
}
}

View file

@ -27,7 +27,7 @@ import javax.annotation.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
/**
* Creates queries that can be used both for objectify and JPA.
* Creates queries that can be used JPA.
*
* <p>Example usage:
*

View file

@ -53,8 +53,8 @@ import javax.inject.Inject;
* <p>All commands and responses conform to the RDAP spec as defined in RFCs 7480 through 7485.
*
* <p>The RDAP specification lumps contacts and registrars together and calls them "entities", which
* is confusing for us, because "entity" means something else in Objectify. But here, when we use
* the term, it means either a contact or registrar. When searching for entities, we always start by
* is confusing for us, because "entity" means something else in SQL. But here, when we use the
* term, it means either a contact or registrar. When searching for entities, we always start by
* returning all matching contacts, and after that all matching registrars.
*
* <p>There are two ways to search for entities: by full name (for contacts, the search name, for

View file

@ -18,7 +18,6 @@ import static com.google.common.base.Verify.verify;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.flogger.FluentLogger;
import com.googlecode.objectify.Key;
import google.registry.model.ImmutableObject;
import google.registry.model.contact.Contact;
import google.registry.model.domain.Domain;
@ -164,10 +163,12 @@ public final class RdeMarshaller implements Serializable {
try {
xml = marshal(element);
} catch (MarshalException e) {
error = String.format("RDE XML schema validation failed: %s\n%s%s\n",
Key.create(resource),
e.getLinkedException(),
getMarshaller().marshalLenient(element));
error =
String.format(
"RDE XML schema validation failed: %s\n%s%s\n",
resource.createVKey(),
e.getLinkedException(),
getMarshaller().marshalLenient(element));
logger.atSevere().withCause(e).log(error);
}
return DepositFragment.create(type, xml, error);

View file

@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import google.registry.model.EppResource;
import google.registry.model.adapters.EnumToAttributeAdapter.EppEnum;
import google.registry.model.contact.Contact;
import google.registry.model.contact.ContactPhoneNumber;
import google.registry.model.contact.PostalInfo;
@ -33,7 +34,6 @@ import google.registry.model.domain.GracePeriod;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarPoc;
import google.registry.model.translators.EnumToAttributeAdapter.EppEnum;
import google.registry.persistence.VKey;
import java.util.Objects;
import java.util.Optional;

View file

@ -21,7 +21,7 @@ import google.registry.testing.FakeClock;
import org.joda.time.DateTime;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Base class of all unit tests for entities which are persisted to Datastore via Objectify. */
/** Base class of all unit tests for entities which are persisted to SQL. */
public abstract class EntityTestCase {
protected enum JpaEntityCoverageCheck {

View file

@ -17,7 +17,6 @@ package google.registry.model;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableMap;
import com.googlecode.objectify.annotation.Id;
import google.registry.testing.AppEngineExtension;
import java.lang.reflect.Field;
import java.util.Map;
@ -34,14 +33,13 @@ public class ModelUtilsTest {
/** Test class for reflection methods. */
public static class TestClass extends ImmutableObject implements Buildable {
@Id
String id;
String a;
String b;
/** Note that there is no getter for {@link #b}.*/
/** Note that there is no getter for {@link #b}. */
public String getId() {
return id;
}
@ -50,7 +48,7 @@ public class ModelUtilsTest {
return a;
}
/** Builder for {@link TestClass}. Note that there is no setter for {@link #a}.*/
/** Builder for {@link TestClass}. Note that there is no setter for {@link #a}. */
public static class Builder extends Buildable.Builder<TestClass> {
protected Builder() {}
@ -83,10 +81,11 @@ public class ModelUtilsTest {
@Test
void testGetAllFields() throws Exception {
Map<String, Field> expected = ImmutableMap.of(
"id", TestClass.class.getDeclaredField("id"),
"a", TestClass.class.getDeclaredField("a"),
"b", TestClass.class.getDeclaredField("b"));
Map<String, Field> expected =
ImmutableMap.of(
"id", TestClass.class.getDeclaredField("id"),
"a", TestClass.class.getDeclaredField("a"),
"b", TestClass.class.getDeclaredField("b"));
// More complicated version of isEqualTo() so that we check for ordering.
assertThat(ModelUtils.getAllFields(TestClass.class).entrySet())
.containsExactlyElementsIn(expected.entrySet())
@ -127,7 +126,7 @@ public class ModelUtilsTest {
original.id = "foo";
TestClass cloned = original.asBuilder().setId("bar").build();
assertThat(cloned.hashCode()).isNotEqualTo(original.hashCode());
cloned.id = "foo"; // Violates immutability contract.
cloned.id = "foo"; // Violates immutability contract.
// The hashCode is now cached and is stale (but that's the expected behavior).
assertThat(cloned.hashCode()).isNotEqualTo(original.hashCode());
}

View file

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.model.translators;
package google.registry.model.adapters;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.util.DateTimeUtils.START_OF_TIME;

View file

@ -888,9 +888,7 @@ public class DomainTest {
@Test
void testHistoryIdRestoration() {
// Verify that history ids for billing events are restored during load from datastore. History
// ids are not used by business code or persisted in datastore, but only to reconstruct
// objectify keys when loading from SQL.
// Verify that history ids for billing events are restored during load.
DateTime now = fakeClock.nowUtc();
domain =
persistResource(

View file

@ -20,15 +20,14 @@ import static google.registry.persistence.transaction.QueryComposer.Comparator;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static org.junit.Assert.assertThrows;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Index;
import google.registry.model.ImmutableObject;
import google.registry.testing.AppEngineExtension;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.FakeClock;
import java.util.Optional;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import org.junit.jupiter.api.BeforeEach;
@ -44,13 +43,11 @@ public class QueryComposerTest {
TestEntity charlie = new TestEntity("charlie", 1);
@RegisterExtension
public final AppEngineExtension appEngine =
AppEngineExtension.builder()
public final JpaUnitTestExtension jpa =
new JpaTestExtensions.Builder()
.withClock(fakeClock)
.withCloudSql()
.withOfyTestEntities(TestEntity.class)
.withJpaUnitTestEntities(TestEntity.class)
.build();
.withEntityClass(TestEntity.class)
.buildUnitTestExtension();
public QueryComposerTest() {}
@ -319,12 +316,10 @@ public class QueryComposerTest {
.isEmpty();
}
@javax.persistence.Entity
@Entity(name = "QueryComposerTestEntity")
@Entity
private static class TestEntity extends ImmutableObject {
@javax.persistence.Id @Id private String name;
@Id private String name;
@Index
// Renaming this implicitly verifies that property names work for hibernate queries.
@Column(name = "some_value")
private int val;

View file

@ -1125,7 +1125,7 @@ public final class DatabaseHelper {
.build());
}
/** Persists a single Objectify resource, without adjusting foreign resources or keys. */
/** Persists a single resource, without adjusting foreign resources or keys. */
public static <R> R persistSimpleResource(final R resource) {
return persistSimpleResources(ImmutableList.of(resource)).get(0);
}

View file

@ -81,7 +81,7 @@ public final class DiffUtils {
&& aValue == null
&& bValue instanceof Collection
&& ((Collection<?>) bValue).isEmpty()) {
// Ignore a mismatch between Objectify's use of null to store empty collections and our
// Ignore a mismatch between the use of null to store empty collections and our
// code's builder methods, which yield empty collections for the same fields. This
// prevents useless lines of the form "[null, []]" from appearing in diffs.
} else {