mirror of
https://github.com/google/nomulus.git
synced 2025-05-13 07:57:13 +02:00
mv com/google/domain/registry google/registry
This change renames directories in preparation for the great package rename. The repository is now in a broken state because the code itself hasn't been updated. However this should ensure that git correctly preserves history for each file.
This commit is contained in:
parent
a41677aea1
commit
5012893c1d
2396 changed files with 0 additions and 0 deletions
407
java/google/registry/model/EppResourceUtils.java
Normal file
407
java/google/registry/model/EppResourceUtils.java
Normal file
|
@ -0,0 +1,407 @@
|
|||
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.model;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.Iterables.transform;
|
||||
import static com.google.domain.registry.model.RoidSuffixes.getRoidSuffixForTld;
|
||||
import static com.google.domain.registry.model.index.ForeignKeyIndex.loadAndGetReference;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.util.DateTimeUtils.isAtOrAfter;
|
||||
import static com.google.domain.registry.util.DateTimeUtils.isBeforeOrAt;
|
||||
import static com.google.domain.registry.util.DateTimeUtils.latestOf;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.domain.registry.config.RegistryEnvironment;
|
||||
import com.google.domain.registry.model.EppResource.Builder;
|
||||
import com.google.domain.registry.model.EppResource.ForeignKeyedEppResource;
|
||||
import com.google.domain.registry.model.contact.ContactResource;
|
||||
import com.google.domain.registry.model.domain.DomainBase;
|
||||
import com.google.domain.registry.model.domain.ReferenceUnion;
|
||||
import com.google.domain.registry.model.eppcommon.StatusValue;
|
||||
import com.google.domain.registry.model.host.HostResource;
|
||||
import com.google.domain.registry.model.index.ForeignKeyIndex;
|
||||
import com.google.domain.registry.model.ofy.CommitLogManifest;
|
||||
import com.google.domain.registry.model.ofy.CommitLogMutation;
|
||||
import com.google.domain.registry.model.transfer.TransferData;
|
||||
import com.google.domain.registry.model.transfer.TransferStatus;
|
||||
import com.google.domain.registry.util.FormattingLogger;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.Ref;
|
||||
import com.googlecode.objectify.Result;
|
||||
import com.googlecode.objectify.util.ResultNow;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Interval;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Utilities for working with {@link EppResource}. */
|
||||
public final class EppResourceUtils {
|
||||
|
||||
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();
|
||||
|
||||
/** Returns the full domain repoId of the format HEX-TLD for the specified long id and tld. */
|
||||
public static String createDomainRoid(long repoId, String tld) {
|
||||
return createRoid(repoId, getRoidSuffixForTld(tld));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full contact/host repoId of the format HEX-GOOGLE for the specified long repo id.
|
||||
*/
|
||||
public static String createContactHostRoid(long repoId) {
|
||||
return createRoid(
|
||||
repoId, RegistryEnvironment.get().config().getContactAndHostRepositoryIdentifier());
|
||||
}
|
||||
|
||||
private static String createRoid(long repoId, String roidSuffix) {
|
||||
// %X is uppercase hexadecimal.
|
||||
return String.format("%X-%s", repoId, roidSuffix);
|
||||
}
|
||||
|
||||
/** Helper to call {@link EppResource#cloneProjectedAtTime} without warnings. */
|
||||
@SuppressWarnings("unchecked")
|
||||
private static final <T extends EppResource> T cloneProjectedAtTime(T resource, DateTime now) {
|
||||
return (T) resource.cloneProjectedAtTime(now);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the last created version of an {@link EppResource} from the datastore by foreign key.
|
||||
*
|
||||
* <p>Returns null if no resource with this foreign key was ever created, or if the most recently
|
||||
* created resource was deleted before time "now".
|
||||
*
|
||||
* <p>Loading an {@link EppResource} by itself is not sufficient to know its current state since
|
||||
* it may have various expirable conditions and status values that might implicitly change its
|
||||
* state as time progresses even if it has not been updated in the datastore. Rather, the
|
||||
* resource must be combined with a timestamp to view its current state. We use a global last
|
||||
* updated timestamp on the entire entity group (which is essentially free since all writes to
|
||||
* the entity group must be serialized anyways) to guarantee monotonically increasing write
|
||||
* times, so forwarding our projected time to the greater of "now", and this update timestamp
|
||||
* guarantees that we're not projecting into the past.
|
||||
*
|
||||
* @param clazz the resource type to load
|
||||
* @param foreignKey id to match
|
||||
* @param now the current logical time to project resources at
|
||||
*/
|
||||
public static <T extends EppResource> T loadByUniqueId(
|
||||
Class<T> clazz, String foreignKey, DateTime now) {
|
||||
// For regular foreign-keyed resources, get the ref by loading the FKI; for domain applications,
|
||||
// we can construct the ref directly, since the provided foreignKey is just the repoId.
|
||||
Ref<T> resourceRef = ForeignKeyedEppResource.class.isAssignableFrom(clazz)
|
||||
? loadAndGetReference(clazz, foreignKey, now)
|
||||
: Ref.create(Key.create(null, clazz, foreignKey));
|
||||
if (resourceRef == null) {
|
||||
return null;
|
||||
}
|
||||
T resource = resourceRef.get();
|
||||
if (resource == null
|
||||
// You'd think this couldn't happen, but it can. For polymorphic entities, a Ref or Key is
|
||||
// of necessity a reference to the base type (since datastore doesn't have polymorphism and
|
||||
// Objectify is faking it). In the non-foreign-key code path above where we directly create
|
||||
// a Ref, there is no way to know whether the Ref points to an instance of the desired
|
||||
// subclass without loading it. Due to type erasure, it gets stuffed into "resource" without
|
||||
// causing a ClassCastException even if it's the wrong type until you actually try to use it
|
||||
// as the wrong type, at which point it blows up somewhere else in the code. Concretely,
|
||||
// this means that without this line bad things would happen if you tried to load a
|
||||
// DomainApplication using the id of a DomainResource (but not vice versa).
|
||||
|| !clazz.isInstance(resource)
|
||||
|| isAtOrAfter(now, resource.getDeletionTime())) {
|
||||
return null;
|
||||
}
|
||||
// When setting status values based on a time, choose the greater of "now" and the resource's
|
||||
// UpdateAutoTimestamp. For non-mutating uses (info, whois, etc.), this is equivalent to rolling
|
||||
// "now" forward to at least the last update on the resource, so that a read right after a write
|
||||
// doesn't appear stale. For mutating flows, if we had to roll now forward then the flow will
|
||||
// fail when it tries to save anything via Ofy, since "now" is needed to be > the last update
|
||||
// time for writes.
|
||||
return cloneProjectedAtTime(
|
||||
resource,
|
||||
latestOf(now, resource.getUpdateAutoTimestamp().getTimestamp()));
|
||||
}
|
||||
|
||||
/** Loads returns the hosts specified by the given ReferenceUnions. */
|
||||
public static ImmutableSet<HostResource> loadReferencedNameservers(
|
||||
Set<ReferenceUnion<HostResource>> hostRefs) {
|
||||
ImmutableSet.Builder<HostResource> builder = new ImmutableSet.Builder<>();
|
||||
for (ReferenceUnion<HostResource> hostRef : hostRefs) {
|
||||
HostResource host = hostRef.getLinked().get();
|
||||
if (host != null) {
|
||||
builder.add(host);
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/** Loads and returns the contacts specified by the given ReferenceUnions. */
|
||||
public static ImmutableSet<ContactResource> loadReferencedContacts(
|
||||
Set<ReferenceUnion<ContactResource>> contactRefs) {
|
||||
ImmutableSet.Builder<ContactResource> builder = new ImmutableSet.Builder<>();
|
||||
for (ReferenceUnion<ContactResource> contactRef : contactRefs) {
|
||||
builder.add(contactRef.getLinked().get());
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks multiple {@link EppResource} objects from the datastore by unique ids.
|
||||
* <p>
|
||||
* There are currently no resources that support checks and do not use foreign keys. If we need to
|
||||
* support that case in the future, we can loosen the type to allow any {@link EppResource} and
|
||||
* add code to do the lookup by id directly.
|
||||
*
|
||||
* @param clazz the resource type to load
|
||||
* @param uniqueIds a list of ids to match
|
||||
* @param now the logical time of the check
|
||||
*/
|
||||
public static <T extends EppResource> Set<String> checkResourcesExist(
|
||||
Class<T> clazz, List<String> uniqueIds, final DateTime now) {
|
||||
return ForeignKeyIndex.load(clazz, uniqueIds, now).keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads resources that match some filter and that have {@link EppResource#deletionTime} that is
|
||||
* not before "now".
|
||||
*
|
||||
* <p>This is an eventually consistent query.
|
||||
*
|
||||
* @param clazz the resource type to load
|
||||
* @param now the logical time of the check
|
||||
* @param filterDefinition the filter to apply when loading resources
|
||||
* @param filterValue the acceptable value for the filter
|
||||
*/
|
||||
public static <T extends EppResource> Iterable<T> queryNotDeleted(
|
||||
Class<T> clazz, DateTime now, String filterDefinition, Object filterValue) {
|
||||
return transform(
|
||||
ofy().load().type(clazz)
|
||||
.filter(filterDefinition, filterValue)
|
||||
.filter("deletionTime >", now.toDate()),
|
||||
EppResourceUtils.<T>transformAtTime(now));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Function that transforms an EppResource to the given DateTime, suitable for use with
|
||||
* Iterables.transform() over a collection of EppResources.
|
||||
*/
|
||||
public static <T extends EppResource> Function<T, T> transformAtTime(final DateTime now) {
|
||||
return new Function<T, T>() {
|
||||
@Override
|
||||
public T apply(T resource) {
|
||||
return cloneProjectedAtTime(resource, now);
|
||||
}};
|
||||
}
|
||||
|
||||
/**
|
||||
* The lifetime of a resource is from its creation time, inclusive, through its deletion time,
|
||||
* exclusive, which happily maps to the behavior of Interval.
|
||||
*/
|
||||
private static Interval getLifetime(EppResource resource) {
|
||||
return new Interval(resource.getCreationTime(), resource.getDeletionTime());
|
||||
}
|
||||
|
||||
public static boolean isActive(EppResource resource, DateTime time) {
|
||||
return getLifetime(resource).contains(time);
|
||||
}
|
||||
|
||||
public static boolean isDeleted(EppResource resource, DateTime time) {
|
||||
return !isActive(resource, time);
|
||||
}
|
||||
|
||||
/** Process an automatic transfer on a resource. */
|
||||
public static void setAutomaticTransferSuccessProperties(
|
||||
Builder<?, ?> builder, TransferData transferData) {
|
||||
checkArgument(TransferStatus.PENDING.equals(transferData.getTransferStatus()));
|
||||
builder.removeStatusValue(StatusValue.PENDING_TRANSFER)
|
||||
.setTransferData(transferData.asBuilder()
|
||||
.setTransferStatus(TransferStatus.SERVER_APPROVED)
|
||||
.setServerApproveEntities(null)
|
||||
.setServerApproveBillingEvent(null)
|
||||
.setServerApproveAutorenewEvent(null)
|
||||
.setServerApproveAutorenewPollMessage(null)
|
||||
.build())
|
||||
.setLastTransferTime(transferData.getPendingTransferExpirationTime())
|
||||
.setCurrentSponsorClientId(transferData.getGainingClientId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform common operations for projecting an {@link EppResource} at a given time:
|
||||
* <ul>
|
||||
* <li>Process an automatic transfer.
|
||||
* </ul>
|
||||
*/
|
||||
public static <T extends EppResource> void projectResourceOntoBuilderAtTime(
|
||||
T resource, Builder<?, ?> builder, DateTime now) {
|
||||
TransferData transferData = resource.getTransferData();
|
||||
// If there's a pending transfer that has expired, process it.
|
||||
DateTime expirationTime = transferData.getPendingTransferExpirationTime();
|
||||
if (TransferStatus.PENDING.equals(transferData.getTransferStatus())
|
||||
&& isBeforeOrAt(expirationTime, now)) {
|
||||
setAutomaticTransferSuccessProperties(builder, transferData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewinds an {@link EppResource} object to a given point in time.
|
||||
*
|
||||
* <p>This method costs nothing if {@code resource} is already current. Otherwise it needs to
|
||||
* perform a single asynchronous key fetch operation.
|
||||
*
|
||||
* <p><b>Warning:</b> A resource can only be rolled backwards in time, not forwards; therefore
|
||||
* {@code resource} should be whatever's currently in datastore.
|
||||
*
|
||||
* <p><b>Warning:</b> Revisions are granular to 24-hour periods. It's recommended that
|
||||
* {@code timestamp} be set to midnight. Otherwise you must take into consideration that under
|
||||
* certain circumstances, a resource might be restored to a revision on the previous day, even if
|
||||
* there were revisions made earlier on the same date as {@code timestamp}; however, a resource
|
||||
* will never be restored to a revision occuring after {@code timestamp}. This behavior is due to
|
||||
* the way {@link com.google.domain.registry.model.translators.CommitLogRevisionsTranslatorFactory
|
||||
* CommitLogRevisionsTranslatorFactory} manages the {@link EppResource#revisions} field. Please
|
||||
* note however that the creation and deletion times of a resource are granular to the
|
||||
* millisecond.
|
||||
*
|
||||
* @return an asynchronous operation returning resource at {@code timestamp} or {@code null} if
|
||||
* if resource is deleted or not yet created
|
||||
*/
|
||||
public static <T extends EppResource>
|
||||
Result<T> loadAtPointInTime(final T resource, final DateTime timestamp) {
|
||||
// If we're before the resource creation time, don't try to find a "most recent revision".
|
||||
if (timestamp.isBefore(resource.getCreationTime())) {
|
||||
return new ResultNow<>(null);
|
||||
}
|
||||
// If the resource was not modified after the requested time, then use it as-is, otherwise find
|
||||
// the most recent revision asynchronously, and return an async result that wraps that revision
|
||||
// and returns it projected forward to exactly the desired timestamp, or null if the resource is
|
||||
// deleted at that timestamp.
|
||||
final Result<T> loadResult =
|
||||
(isAtOrAfter(timestamp, resource.getUpdateAutoTimestamp().getTimestamp()))
|
||||
? new ResultNow<>(resource)
|
||||
: loadMostRecentRevisionAtTime(resource, timestamp);
|
||||
return new Result<T>() {
|
||||
@Override
|
||||
public T now() {
|
||||
T loadedResource = loadResult.now();
|
||||
return loadedResource == null ? null
|
||||
: (isActive(loadedResource, timestamp)
|
||||
? cloneProjectedAtTime(loadedResource, timestamp)
|
||||
: null);
|
||||
}};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an asynchronous result holding the most recent datastore revision of a given
|
||||
* EppResource before or at the provided timestamp using the EppResource revisions map, falling
|
||||
* back to using the earliest revision or the resource as-is if there are no revisions.
|
||||
*
|
||||
* @see #loadAtPointInTime(EppResource, DateTime)
|
||||
*/
|
||||
private static <T extends EppResource> Result<T> loadMostRecentRevisionAtTime(
|
||||
final T resource, final DateTime timestamp) {
|
||||
final Key<T> resourceKey = Key.create(resource);
|
||||
final Ref<CommitLogManifest> revision = findMostRecentRevisionAtTime(resource, timestamp);
|
||||
if (revision == null) {
|
||||
logger.severefmt("No revision found for %s, falling back to resource.", resourceKey);
|
||||
return new ResultNow<>(resource);
|
||||
}
|
||||
final Result<CommitLogMutation> mutationResult =
|
||||
ofy().load().key(CommitLogMutation.createKey(revision.getKey(), resourceKey));
|
||||
return new Result<T>() {
|
||||
@Override
|
||||
public T now() {
|
||||
CommitLogMutation mutation = mutationResult.now();
|
||||
if (mutation != null) {
|
||||
return ofy().load().fromEntity(mutation.getEntity());
|
||||
}
|
||||
logger.severefmt(
|
||||
"Couldn't load mutation for revision at %s for %s, falling back to resource."
|
||||
+ " Revision: %s",
|
||||
timestamp, resourceKey, revision);
|
||||
return resource;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static <T extends EppResource> Ref<CommitLogManifest>
|
||||
findMostRecentRevisionAtTime(final T resource, final DateTime timestamp) {
|
||||
final Key<T> resourceKey = Key.create(resource);
|
||||
Entry<?, Ref<CommitLogManifest>> revision = resource.getRevisions().floorEntry(timestamp);
|
||||
if (revision != null) {
|
||||
logger.infofmt("Found revision history at %s for %s: %s", timestamp, resourceKey, revision);
|
||||
return revision.getValue();
|
||||
}
|
||||
// Fall back to the earliest revision if we don't have one before the requested timestamp.
|
||||
revision = resource.getRevisions().firstEntry();
|
||||
if (revision != null) {
|
||||
logger.severefmt("Found no revision history at %s for %s, using earliest revision: %s",
|
||||
timestamp, resourceKey, revision);
|
||||
return revision.getValue();
|
||||
}
|
||||
// Ultimate fallback: There are no revisions whatsoever, so return null.
|
||||
logger.severefmt("Found no revision history at all for %s", resourceKey);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find keys of domains or applications that reference a specified contact or host.
|
||||
*
|
||||
* <p>This is an eventually consistent query.
|
||||
*
|
||||
* @param clazz the referent type (contact or host)
|
||||
* @param ref the referent key
|
||||
* @param now the logical time of the check
|
||||
* @param limit max number of keys to return
|
||||
*/
|
||||
public static List<Key<DomainBase>> queryDomainsUsingResource(
|
||||
Class<? extends EppResource> clazz, Ref<? extends EppResource> ref, DateTime now, int limit) {
|
||||
checkArgument(ContactResource.class.equals(clazz) || HostResource.class.equals(clazz));
|
||||
return ofy().load().type(DomainBase.class)
|
||||
.filter(
|
||||
clazz.equals(ContactResource.class)
|
||||
? "allContacts.contactId.linked"
|
||||
: "nameservers.linked",
|
||||
ref)
|
||||
.filter("deletionTime >", now)
|
||||
.limit(limit)
|
||||
.keys()
|
||||
.list();
|
||||
}
|
||||
|
||||
/** Clone a contact or host with an eventually-consistent notion of LINKED. */
|
||||
public static EppResource cloneResourceWithLinkedStatus(EppResource resource, DateTime now) {
|
||||
Builder<?, ?> builder = resource.asBuilder();
|
||||
if (queryDomainsUsingResource(resource.getClass(), Ref.create(resource), now, 1).isEmpty()) {
|
||||
builder.removeStatusValue(StatusValue.LINKED);
|
||||
} else {
|
||||
builder.addStatusValue(StatusValue.LINKED);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/** Exception to throw when failing to parse a repo id. */
|
||||
public static class InvalidRepoIdException extends Exception {
|
||||
|
||||
public InvalidRepoIdException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
private EppResourceUtils() {}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue