Remove nearly all uses of ReferenceUnion

ReferenceUnion is a hack to work around the mismatch between how
we store references (by roid) and how they are represented in EPP
(by foreign key). If it ever needed to exist (not entirely clear...)
it should have remained tightly scoped within the domain commands
and resources. Instead it has leaked everywhere in the project,
causing lots of boilerplate. This CL hides all of that behind
standard Refs, and should be followed by work to remove ReferenceUnion
completely.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=122424416
This commit is contained in:
cgoldfeder 2016-05-16 08:55:54 -07:00 committed by Justine Tunney
parent 56c8bb0f2a
commit 9a2afc7a9b
59 changed files with 448 additions and 454 deletions

View file

@ -17,12 +17,12 @@ package google.registry.flows;
import static google.registry.model.eppoutput.Result.Code.SuccessWithActionPending;
import static google.registry.model.ofy.ObjectifyService.ofy;
import com.googlecode.objectify.Ref;
import com.googlecode.objectify.Work;
import google.registry.flows.EppException.AssociationProhibitsOperationException;
import google.registry.model.EppResource;
import google.registry.model.EppResource.Builder;
import google.registry.model.domain.ReferenceUnion;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand;
import google.registry.model.eppoutput.Result.Code;
@ -51,7 +51,7 @@ public abstract class ResourceAsyncDeleteFlow
// that would be hard to reason about, and there's no real gain in doing so.
return false;
}
return isLinkedForFailfast(ReferenceUnion.create(fki.getReference()));
return isLinkedForFailfast(fki.getReference());
}
});
if (isLinked) {
@ -60,7 +60,7 @@ public abstract class ResourceAsyncDeleteFlow
}
/** Subclasses must override this to check if the supplied reference has incoming links. */
protected abstract boolean isLinkedForFailfast(ReferenceUnion<R> ref);
protected abstract boolean isLinkedForFailfast(Ref<R> ref);
@Override
protected final R createOrMutateResource() {

View file

@ -16,9 +16,10 @@ package google.registry.flows.async;
import static google.registry.flows.ResourceFlowUtils.handlePendingTransferOnDelete;
import com.googlecode.objectify.Ref;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DomainBase;
import google.registry.model.domain.ReferenceUnion;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.HistoryEntry.Type;
import google.registry.request.Action;
@ -48,7 +49,7 @@ public class DeleteContactResourceAction extends DeleteEppResourceAction<Contact
@Override
protected boolean isLinked(
DomainBase domain, ReferenceUnion<ContactResource> targetResourceRef) {
DomainBase domain, Ref<ContactResource> targetResourceRef) {
return domain.getReferencedContacts().contains(targetResourceRef);
}
}

View file

@ -41,7 +41,6 @@ import google.registry.mapreduce.inputs.NullInput;
import google.registry.model.EppResource;
import google.registry.model.annotations.ExternalMessagingName;
import google.registry.model.domain.DomainBase;
import google.registry.model.domain.ReferenceUnion;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry;
@ -110,7 +109,7 @@ public abstract class DeleteEppResourceAction<T extends EppResource> implements
"Resource %s is already deleted.", resource.getForeignKey());
checkState(
resource.getStatusValues().contains(StatusValue.PENDING_DELETE),
"Resource %s is not set as PENDING_DELETE", resource.getForeignKey());
"Resource %s is not set as PENDING_DELETE", resource.getForeignKey());
mapper.setTargetResource(resourceKey);
reducer.setClient(requestingClientId, isSuperuser);
logger.infofmt("Executing Delete EPP resource mapreduce for %s", resourceKey);
@ -147,7 +146,7 @@ public abstract class DeleteEppResourceAction<T extends EppResource> implements
}
/** Determine whether the target resource is a linked resource on the domain. */
protected abstract boolean isLinked(DomainBase domain, ReferenceUnion<T> targetResourceRef);
protected abstract boolean isLinked(DomainBase domain, Ref<T> targetResourceRef);
@Override
public void map(DomainBase domain) {
@ -159,12 +158,12 @@ public abstract class DeleteEppResourceAction<T extends EppResource> implements
emit(targetEppResourceKey, false);
return;
}
// The ReferenceUnion can't be a field on the Mapper, because when a Ref<?> is serialized
// (required for each MapShardTask), it uses the DeadRef version, which contains the Ref's
// value, which isn't serializable. Thankfully, this isn't expensive.
// The Ref can't be a field on the Mapper, because when a Ref<?> is serialized (required for
// each MapShardTask), it uses the DeadRef version, which contains the Ref's value, which
// isn't serializable. Thankfully, this isn't expensive.
// See: https://github.com/objectify/objectify/blob/master/src/main/java/com/googlecode/objectify/impl/ref/DeadRef.java
if (isActive(domain, targetResourceUpdateTimestamp)
&& isLinked(domain, ReferenceUnion.create(Ref.create(targetEppResourceKey)))) {
&& isLinked(domain, Ref.create(targetEppResourceKey))) {
emit(targetEppResourceKey, true);
}
}

View file

@ -16,9 +16,10 @@ package google.registry.flows.async;
import static google.registry.model.ofy.ObjectifyService.ofy;
import com.googlecode.objectify.Ref;
import google.registry.dns.DnsQueue;
import google.registry.model.domain.DomainBase;
import google.registry.model.domain.ReferenceUnion;
import google.registry.model.host.HostResource;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.HistoryEntry.Type;
@ -48,8 +49,7 @@ public class DeleteHostResourceAction extends DeleteEppResourceAction<HostResour
private static final long serialVersionUID = 1941092742903217194L;
@Override
protected boolean isLinked(
DomainBase domain, ReferenceUnion<HostResource> targetResourceRef) {
protected boolean isLinked(DomainBase domain, Ref<HostResource> targetResourceRef) {
return domain.getNameservers().contains(targetResourceRef);
}
}

View file

@ -30,7 +30,6 @@ import google.registry.mapreduce.MapreduceAction;
import google.registry.mapreduce.MapreduceRunner;
import google.registry.mapreduce.inputs.EppResourceInputs;
import google.registry.model.domain.DomainResource;
import google.registry.model.domain.ReferenceUnion;
import google.registry.model.host.HostResource;
import google.registry.request.Action;
import google.registry.request.HttpException.BadRequestException;
@ -98,7 +97,7 @@ public class DnsRefreshForHostRenameAction implements MapreduceAction {
@Override
public final void map(DomainResource domain) {
if (isActive(domain, hostUpdateTime)
&& domain.getNameservers().contains(ReferenceUnion.create(Ref.create(targetHostKey)))) {
&& domain.getNameservers().contains(Ref.create(targetHostKey))) {
try {
dnsQueue.addDomainRefreshTask(domain.getFullyQualifiedDomainName());
logger.infofmt("Enqueued refresh for domain %s", domain.getFullyQualifiedDomainName());

View file

@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.Ref;
import google.registry.config.RegistryEnvironment;
import google.registry.flows.EppException;
@ -33,7 +34,6 @@ import google.registry.model.contact.ContactCommand.Delete;
import google.registry.model.contact.ContactResource;
import google.registry.model.contact.ContactResource.Builder;
import google.registry.model.domain.DomainBase;
import google.registry.model.domain.ReferenceUnion;
import google.registry.model.reporting.HistoryEntry;
/**
@ -50,7 +50,7 @@ public class ContactDeleteFlow extends ResourceAsyncDeleteFlow<ContactResource,
private static final int FAILFAST_CHECK_COUNT = 5;
@Override
protected boolean isLinkedForFailfast(final ReferenceUnion<ContactResource> ref) {
protected boolean isLinkedForFailfast(final Ref<ContactResource> ref) {
// Query for the first few linked domains, and if found, actually load them. The query is
// eventually consistent and so might be very stale, but the direct load will not be stale,
// just non-transactional. If we find at least one actual reference then we can reliably
@ -58,7 +58,7 @@ public class ContactDeleteFlow extends ResourceAsyncDeleteFlow<ContactResource,
return Iterables.any(
ofy().load().keys(
queryDomainsUsingResource(
ContactResource.class, ref.getLinked(), now, FAILFAST_CHECK_COUNT)).values(),
ContactResource.class, ref, now, FAILFAST_CHECK_COUNT)).values(),
new Predicate<DomainBase>() {
@Override
public boolean apply(DomainBase domain) {

View file

@ -20,7 +20,8 @@ import static google.registry.flows.domain.DomainFlowUtils.validateContactsHaveT
import static google.registry.flows.domain.DomainFlowUtils.validateDomainName;
import static google.registry.flows.domain.DomainFlowUtils.validateDomainNameWithIdnTables;
import static google.registry.flows.domain.DomainFlowUtils.validateDsData;
import static google.registry.flows.domain.DomainFlowUtils.validateNameservers;
import static google.registry.flows.domain.DomainFlowUtils.validateNameserversAllowedOnTld;
import static google.registry.flows.domain.DomainFlowUtils.validateNameserversCount;
import static google.registry.flows.domain.DomainFlowUtils.validateNoDuplicateContacts;
import static google.registry.flows.domain.DomainFlowUtils.validateRegistrantAllowedOnTld;
import static google.registry.flows.domain.DomainFlowUtils.validateRequiredContactsPresent;
@ -36,6 +37,7 @@ import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.registry.Registries.findTldForName;
import static google.registry.model.registry.label.ReservedList.matchesAnchorTenantReservation;
import static google.registry.pricing.PricingEngineProxy.getDomainCreateCost;
import static google.registry.util.CollectionUtils.nullToEmpty;
import com.google.common.base.Optional;
import com.google.common.net.InternetDomainName;
@ -67,6 +69,8 @@ import google.registry.model.tmch.ClaimsListShard;
import org.joda.money.Money;
import java.util.Set;
import javax.annotation.Nullable;
/**
@ -210,10 +214,13 @@ public abstract class BaseDomainCreateFlow<R extends DomainBase, B extends Build
command.getRegistrant(),
command.getNameservers());
validateContactsHaveTypes(command.getContacts());
validateRegistrantAllowedOnTld(tld, command.getRegistrant());
validateRegistrantAllowedOnTld(tld, command.getRegistrantContactId());
validateNoDuplicateContacts(command.getContacts());
validateRequiredContactsPresent(command.getRegistrant(), command.getContacts());
validateNameservers(tld, command.getNameservers());
Set<String> fullyQualifiedHostNames =
nullToEmpty(command.getNameserverFullyQualifiedHostNames());
validateNameserversCount(fullyQualifiedHostNames.size());
validateNameserversAllowedOnTld(tld, fullyQualifiedHostNames);
validateLaunchCreateExtension();
// If a signed mark was provided, then it must match the desired domain label.
// We do this after validating the launch create extension so that flows which don't allow any

View file

@ -20,7 +20,8 @@ import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToT
import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReferences;
import static google.registry.flows.domain.DomainFlowUtils.validateContactsHaveTypes;
import static google.registry.flows.domain.DomainFlowUtils.validateDsData;
import static google.registry.flows.domain.DomainFlowUtils.validateNameservers;
import static google.registry.flows.domain.DomainFlowUtils.validateNameserversAllowedOnTld;
import static google.registry.flows.domain.DomainFlowUtils.validateNameserversCount;
import static google.registry.flows.domain.DomainFlowUtils.validateNoDuplicateContacts;
import static google.registry.flows.domain.DomainFlowUtils.validateRegistrantAllowedOnTld;
import static google.registry.flows.domain.DomainFlowUtils.validateRequiredContactsPresent;
@ -112,6 +113,10 @@ public abstract class BaseDomainUpdateFlow<R extends DomainBase, B extends Build
command.getInnerAdd().getNameservers());
validateContactsHaveTypes(command.getInnerAdd().getContacts());
validateContactsHaveTypes(command.getInnerRemove().getContacts());
validateRegistrantAllowedOnTld(
existingResource.getTld(), command.getInnerChange().getRegistrantContactId());
validateNameserversAllowedOnTld(
existingResource.getTld(), command.getInnerAdd().getNameserverFullyQualifiedHostNames());
}
/** Subclasses can override this to do more specific verification. */
@ -123,8 +128,7 @@ public abstract class BaseDomainUpdateFlow<R extends DomainBase, B extends Build
validateNoDuplicateContacts(newResource.getContacts());
validateRequiredContactsPresent(newResource.getRegistrant(), newResource.getContacts());
validateDsData(newResource.getDsData());
validateRegistrantAllowedOnTld(newResource.getTld(), newResource.getRegistrant());
validateNameservers(newResource.getTld(), newResource.getNameservers());
validateNameserversCount(newResource.getNameservers().size());
}
/** The secDNS:all element must have value 'true' if present. */

View file

@ -91,7 +91,7 @@ import java.util.List;
* @error {@link DomainFlowUtils.LeadingDashException}
* @error {@link DomainFlowUtils.LinkedResourceDoesNotExistException}
* @error {@link DomainFlowUtils.MissingContactTypeException}
* @error {@link DomainFlowUtils.NameserverNotAllowedException}
* @error {@link DomainFlowUtils.NameserversNotAllowedException}
* @error {@link DomainFlowUtils.NoMarksFoundMatchingDomainException}
* @error {@link DomainFlowUtils.PremiumNameBlockedException}
* @error {@link DomainFlowUtils.RegistrantNotAllowedException}

View file

@ -50,7 +50,7 @@ import google.registry.model.reporting.HistoryEntry;
* @error {@link DomainFlowUtils.MissingAdminContactException}
* @error {@link DomainFlowUtils.MissingContactTypeException}
* @error {@link DomainFlowUtils.MissingTechnicalContactException}
* @error {@link DomainFlowUtils.NameserverNotAllowedException}
* @error {@link DomainFlowUtils.NameserversNotAllowedException}
* @error {@link DomainFlowUtils.RegistrantNotAllowedException}
* @error {@link DomainFlowUtils.TooManyDsRecordsException}
* @error {@link DomainFlowUtils.TooManyNameserversException}

View file

@ -81,7 +81,7 @@ import java.util.Set;
* @error {@link DomainFlowUtils.MissingContactTypeException}
* @error {@link DomainFlowUtils.MissingRegistrantException}
* @error {@link DomainFlowUtils.MissingTechnicalContactException}
* @error {@link DomainFlowUtils.NameserverNotAllowedException}
* @error {@link DomainFlowUtils.NameserversNotAllowedException}
* @error {@link DomainFlowUtils.PremiumNameBlockedException}
* @error {@link DomainFlowUtils.RegistrantNotAllowedException}
* @error {@link DomainFlowUtils.TldDoesNotExistException}

View file

@ -20,6 +20,7 @@ import static com.google.common.base.Predicates.equalTo;
import static com.google.common.base.Strings.emptyToNull;
import static com.google.common.collect.Iterables.any;
import static com.google.common.collect.Iterables.concat;
import static com.google.common.collect.Sets.difference;
import static google.registry.flows.EppXmlTransformer.unmarshal;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.registry.Registries.findTldForName;
@ -43,6 +44,7 @@ import com.google.common.collect.Sets;
import com.google.common.net.InternetDomainName;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.Ref;
import google.registry.flows.EppException;
import google.registry.flows.EppException.AuthorizationErrorException;
@ -65,7 +67,6 @@ import google.registry.model.domain.DomainCommand.CreateOrUpdate;
import google.registry.model.domain.DomainCommand.InvalidReferenceException;
import google.registry.model.domain.DomainResource;
import google.registry.model.domain.Period;
import google.registry.model.domain.ReferenceUnion;
import google.registry.model.domain.fee.BaseFeeCommand;
import google.registry.model.domain.fee.BaseFeeRequest;
import google.registry.model.domain.fee.BaseFeeResponse;
@ -249,23 +250,23 @@ public class DomainFlowUtils {
/** Verify that no linked resources have disallowed statuses. */
static void verifyNotInPendingDelete(
Set<DesignatedContact> contacts,
ReferenceUnion<ContactResource> registrant,
Set<ReferenceUnion<HostResource>> nameservers) throws EppException {
Ref<ContactResource> registrant,
Set<Ref<HostResource>> nameservers) throws EppException {
for (DesignatedContact contact : nullToEmpty(contacts)) {
verifyNotInPendingDelete(contact.getContactId());
verifyNotInPendingDelete(contact.getContactRef());
}
if (registrant != null) {
verifyNotInPendingDelete(registrant);
}
for (ReferenceUnion<HostResource> host : nullToEmpty(nameservers)) {
for (Ref<HostResource> host : nullToEmpty(nameservers)) {
verifyNotInPendingDelete(host);
}
}
private static void verifyNotInPendingDelete(
ReferenceUnion<? extends EppResource> resourceRef) throws EppException {
Ref<? extends EppResource> resourceRef) throws EppException {
EppResource resource = resourceRef.getLinked().get();
EppResource resource = resourceRef.get();
if (resource.getStatusValues().contains(StatusValue.PENDING_DELETE)) {
throw new LinkedResourceInPendingDeleteProhibitsOperationException(resource.getForeignKey());
}
@ -280,28 +281,12 @@ public class DomainFlowUtils {
}
}
/** Return a foreign key for a {@link ReferenceUnion} from memory or datastore as needed. */
private static String resolveForeignKey(ReferenceUnion<?> ref) {
return ref.getForeignKey() != null
? ref.getForeignKey()
: ref.getLinked().get().getForeignKey();
}
static void validateNameserversCount(int count) throws EppException {
if (count > MAX_NAMESERVERS_PER_DOMAIN) {
throw new TooManyNameserversException(String.format(
"Only %d nameservers are allowed per domain", MAX_NAMESERVERS_PER_DOMAIN));
}
static void validateNameservers(String tld, Set<ReferenceUnion<HostResource>> nameservers)
throws EppException {
if (nameservers != null && nameservers.size() > MAX_NAMESERVERS_PER_DOMAIN) {
throw new TooManyNameserversException(String.format(
"Only %d nameservers are allowed per domain", MAX_NAMESERVERS_PER_DOMAIN));
}
ImmutableSet<String> whitelist = Registry.get(tld).getAllowedFullyQualifiedHostNames();
if (!whitelist.isEmpty()) { // Empty whitelists are ignored.
for (ReferenceUnion<HostResource> nameserver : nullToEmpty(nameservers)) {
String foreignKey = resolveForeignKey(nameserver);
if (!whitelist.contains(foreignKey)) {
throw new NameserverNotAllowedException(foreignKey);
}
}
}
}
static void validateNoDuplicateContacts(Set<DesignatedContact> contacts)
@ -315,8 +300,8 @@ public class DomainFlowUtils {
}
static void validateRequiredContactsPresent(
ReferenceUnion<ContactResource> registrant, Set<DesignatedContact> contacts)
throws RequiredParameterMissingException {
Ref<ContactResource> registrant, Set<DesignatedContact> contacts)
throws RequiredParameterMissingException {
if (registrant == null) {
throw new MissingRegistrantException();
}
@ -333,12 +318,24 @@ public class DomainFlowUtils {
}
}
static void validateRegistrantAllowedOnTld(String tld, ReferenceUnion<ContactResource> registrant)
static void validateRegistrantAllowedOnTld(String tld, String registrantContactId)
throws RegistrantNotAllowedException {
ImmutableSet<String> whitelist = Registry.get(tld).getAllowedRegistrantContactIds();
// Empty whitelists are ignored.
if (!whitelist.isEmpty() && !whitelist.contains(resolveForeignKey(registrant))) {
throw new RegistrantNotAllowedException(resolveForeignKey(registrant));
if (!whitelist.isEmpty() && !whitelist.contains(registrantContactId)) {
throw new RegistrantNotAllowedException(registrantContactId);
}
}
static void validateNameserversAllowedOnTld(String tld, Set<String> fullyQualifiedHostNames)
throws EppException {
ImmutableSet<String> whitelist = Registry.get(tld).getAllowedFullyQualifiedHostNames();
if (whitelist.isEmpty()) { // Empty whitelists are ignored.
return;
}
Set<String> disallowedNameservers = difference(nullToEmpty(fullyQualifiedHostNames), whitelist);
if (!disallowedNameservers.isEmpty()) {
throw new NameserversNotAllowedException(disallowedNameservers);
}
}
@ -1015,10 +1012,13 @@ public class DomainFlowUtils {
}
}
/** Nameserver is not whitelisted for this TLD. */
public static class NameserverNotAllowedException extends StatusProhibitsOperationException {
public NameserverNotAllowedException(String fullyQualifiedHostName) {
super(String.format("Nameserver %s is not whitelisted for this TLD", fullyQualifiedHostName));
/** Nameserver are not whitelisted for this TLD. */
public static class NameserversNotAllowedException extends StatusProhibitsOperationException {
public NameserversNotAllowedException(Set<String> fullyQualifiedHostNames) {
super(String.format(
"Nameservers '%s' are not whitelisted for this TLD",
Joiner.on(',').join(fullyQualifiedHostNames)));
}
}
}

View file

@ -61,7 +61,7 @@ import java.util.Set;
* @error {@link DomainFlowUtils.MissingAdminContactException}
* @error {@link DomainFlowUtils.MissingContactTypeException}
* @error {@link DomainFlowUtils.MissingTechnicalContactException}
* @error {@link DomainFlowUtils.NameserverNotAllowedException}
* @error {@link DomainFlowUtils.NameserversNotAllowedException}
* @error {@link DomainFlowUtils.RegistrantNotAllowedException}
* @error {@link DomainFlowUtils.TooManyDsRecordsException}
* @error {@link DomainFlowUtils.TooManyNameserversException}

View file

@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.Ref;
import google.registry.config.RegistryEnvironment;
import google.registry.flows.EppException;
@ -30,7 +31,6 @@ import google.registry.flows.async.AsyncFlowUtils;
import google.registry.flows.async.DeleteEppResourceAction;
import google.registry.flows.async.DeleteHostResourceAction;
import google.registry.model.domain.DomainBase;
import google.registry.model.domain.ReferenceUnion;
import google.registry.model.host.HostCommand.Delete;
import google.registry.model.host.HostResource;
import google.registry.model.host.HostResource.Builder;
@ -50,7 +50,7 @@ public class HostDeleteFlow extends ResourceAsyncDeleteFlow<HostResource, Builde
private static final int FAILFAST_CHECK_COUNT = 5;
@Override
protected boolean isLinkedForFailfast(final ReferenceUnion<HostResource> ref) {
protected boolean isLinkedForFailfast(final Ref<HostResource> ref) {
// Query for the first few linked domains, and if found, actually load them. The query is
// eventually consistent and so might be very stale, but the direct load will not be stale,
// just non-transactional. If we find at least one actual reference then we can reliably
@ -58,7 +58,7 @@ public class HostDeleteFlow extends ResourceAsyncDeleteFlow<HostResource, Builde
return Iterables.any(
ofy().load().keys(
queryDomainsUsingResource(
HostResource.class, ref.getLinked(), now, FAILFAST_CHECK_COUNT)).values(),
HostResource.class, ref, now, FAILFAST_CHECK_COUNT)).values(),
new Predicate<DomainBase>() {
@Override
public boolean apply(DomainBase domain) {