From 20a2301dc59bbae91757623b4761c6d64e5c8765 Mon Sep 17 00:00:00 2001 From: Ben McIlwain Date: Fri, 27 Mar 2026 15:41:17 -0400 Subject: [PATCH 1/2] Begin migration of joda DateTime to java.time.Instant java.time has been around since Java 8 and was based on joda DateTime, so this is an overdue migration. We're migrating specifically to Instant in most places rather than ZonedDateTime because we were always using DateTimes in UTC to reference a specific instant, which is exactly what Instants are for. ZonedDateTime set to UTC may still be useful in some places that are heavy on date math (especially in tests). There is a lot more work to be done after this, but I wanted to put together a manual PR showing my overall approach for how to do the migration that I can then hopefully follow along with AI to continue making these changes throughout the codebase. The basic approach is to migrate a small number of methods at a time, marking the old methods as @Deprecated when possible (not always possible because of @InlineMe restrictions). This PR doesn't yet migrate any DateTime fields in the model classes, so that's the one remaining type of refactor to figure out after this. We won't be changing how any of the data is actually stored in the database. BUG= http://b/496985355 --- .../main/java/google/registry/util/Clock.java | 5 ++ .../google/registry/util/DateTimeUtils.java | 89 ++++++++++++++++++- .../google/registry/util/SystemClock.java | 6 ++ .../google/registry/testing/FakeClock.java | 6 ++ .../batch/BulkDomainTransferAction.java | 6 +- .../batch/DeleteExpiredDomainsAction.java | 3 +- .../registry/batch/RelockDomainAction.java | 2 +- .../ExpandBillingRecurrencesPipeline.java | 3 +- .../registry/flows/ResourceFlowUtils.java | 11 ++- .../flows/domain/DomainDeleteFlow.java | 4 +- .../flows/domain/DomainFlowUtils.java | 6 +- .../registry/flows/domain/DomainInfoFlow.java | 4 +- .../flows/domain/DomainRenewFlow.java | 7 +- .../domain/DomainRestoreRequestFlow.java | 4 +- .../domain/DomainTransferApproveFlow.java | 13 ++- .../flows/domain/DomainTransferQueryFlow.java | 11 ++- .../domain/DomainTransferRejectFlow.java | 11 ++- .../domain/DomainTransferRequestFlow.java | 15 ++-- .../flows/domain/DomainTransferUtils.java | 6 +- .../flows/domain/DomainUpdateFlow.java | 2 +- .../registry/flows/host/HostInfoFlow.java | 2 +- .../google/registry/model/EppResource.java | 20 ++++- .../registry/model/EppResourceUtils.java | 9 +- .../registry/model/ForeignKeyUtils.java | 35 +++++++- .../registry/model/ResourceTransferUtils.java | 4 +- .../model/billing/BillingCancellation.java | 2 +- .../google/registry/model/domain/Domain.java | 8 ++ .../registry/model/domain/DomainBase.java | 64 ++++++++----- .../model/domain/GracePeriodBase.java | 10 ++- .../google/registry/model/host/HostBase.java | 10 ++- .../registry/model/poll/PollMessage.java | 2 +- .../model/transfer/BaseTransferObject.java | 10 ++- .../model/transfer/DomainTransferData.java | 10 ++- .../JpaTransactionManagerImpl.java | 11 ++- .../transaction/TransactionManager.java | 5 ++ .../google/registry/rdap/RdapActionBase.java | 3 +- .../registry/rdap/RdapJsonFormatter.java | 10 ++- .../registry/rde/DomainToXjcConverter.java | 8 +- .../registry/rde/HostToXjcConverter.java | 2 +- .../tools/GenerateDnsReportCommand.java | 4 +- .../registry/tools/RenewDomainCommand.java | 2 +- .../tools/UniformRapidSuspensionCommand.java | 2 +- .../registry/tools/UnrenewDomainCommand.java | 11 +-- .../tools/UpdateRecurrenceCommand.java | 9 +- .../batch/DeleteExpiredDomainsActionTest.java | 3 +- .../batch/DeleteProberDataActionTest.java | 11 +-- .../ResaveAllEppResourcesPipelineTest.java | 7 +- .../flows/EppLifecycleDomainTest.java | 2 +- .../google/registry/flows/FlowTestCase.java | 2 +- .../flows/domain/DomainCreateFlowTest.java | 8 +- .../flows/domain/DomainDeleteFlowTest.java | 10 +-- .../flows/domain/DomainRenewFlowTest.java | 8 +- .../domain/DomainRestoreRequestFlowTest.java | 4 +- .../domain/DomainTransferApproveFlowTest.java | 25 +++--- .../domain/DomainTransferCancelFlowTest.java | 2 +- .../domain/DomainTransferQueryFlowTest.java | 5 +- .../domain/DomainTransferRejectFlowTest.java | 2 +- .../domain/DomainTransferRequestFlowTest.java | 27 +++--- .../flows/host/HostUpdateFlowTest.java | 2 +- .../registry/model/domain/DomainTest.java | 81 +++++++++-------- .../model/domain/GracePeriodTest.java | 6 +- .../testing/AbstractEppResourceSubject.java | 6 +- .../registry/testing/DomainSubject.java | 2 +- .../tools/UnrenewDomainCommandTest.java | 8 +- .../domains/ConsoleBulkDomainActionTest.java | 5 +- .../registry/util/DateTimeUtilsTest.java | 50 ++++++++++- 66 files changed, 530 insertions(+), 213 deletions(-) diff --git a/common/src/main/java/google/registry/util/Clock.java b/common/src/main/java/google/registry/util/Clock.java index 78372cba4bc..9943aaa7cec 100644 --- a/common/src/main/java/google/registry/util/Clock.java +++ b/common/src/main/java/google/registry/util/Clock.java @@ -15,6 +15,7 @@ package google.registry.util; import java.io.Serializable; +import java.time.Instant; import javax.annotation.concurrent.ThreadSafe; import org.joda.time.DateTime; @@ -30,5 +31,9 @@ public interface Clock extends Serializable { /** Returns current time in UTC timezone. */ + @Deprecated DateTime nowUtc(); + + /** Returns current Instant (which is always in UTC). */ + Instant now(); } diff --git a/common/src/main/java/google/registry/util/DateTimeUtils.java b/common/src/main/java/google/registry/util/DateTimeUtils.java index b085fbabdc8..025ca3dde81 100644 --- a/common/src/main/java/google/registry/util/DateTimeUtils.java +++ b/common/src/main/java/google/registry/util/DateTimeUtils.java @@ -20,6 +20,9 @@ import com.google.common.collect.Lists; import com.google.common.collect.Ordering; import java.sql.Date; +import java.time.Instant; +import java.time.ZoneOffset; +import javax.annotation.Nullable; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.LocalDate; @@ -28,7 +31,10 @@ public abstract class DateTimeUtils { /** The start of the epoch, in a convenient constant. */ - public static final DateTime START_OF_TIME = new DateTime(0, DateTimeZone.UTC); + @Deprecated public static final DateTime START_OF_TIME = new DateTime(0, DateTimeZone.UTC); + + /** The start of the UNIX epoch in UTC, in a convenient constant. */ + public static final Instant START_INSTANT = Instant.ofEpochMilli(0); /** * A date in the far future that we can treat as infinity. @@ -37,19 +43,40 @@ public abstract class DateTimeUtils { * but Java uses milliseconds, so this is the largest representable date that will survive a * round-trip through the database. */ + @Deprecated public static final DateTime END_OF_TIME = new DateTime(Long.MAX_VALUE / 1000, DateTimeZone.UTC); + /** + * An instant in the far future that we can treat as infinity. + * + *

This value is (2^63-1)/1000 rounded down. Postgres can store dates as 64 bit microseconds, + * but Java uses milliseconds, so this is the largest representable date that will survive a + * round-trip through the database. + */ + public static final Instant END_INSTANT = Instant.ofEpochMilli(Long.MAX_VALUE / 1000); + /** Returns the earliest of a number of given {@link DateTime} instances. */ public static DateTime earliestOf(DateTime first, DateTime... rest) { + return earliestDateTimeOf(Lists.asList(first, rest)); + } + + /** Returns the earliest of a number of given {@link Instant} instances. */ + public static Instant earliestOf(Instant first, Instant... rest) { return earliestOf(Lists.asList(first, rest)); } /** Returns the earliest element in a {@link DateTime} iterable. */ - public static DateTime earliestOf(Iterable dates) { + public static DateTime earliestDateTimeOf(Iterable dates) { checkArgument(!Iterables.isEmpty(dates)); return Ordering.natural().min(dates); } + /** Returns the earliest element in a {@link Instant} iterable. */ + public static Instant earliestOf(Iterable instants) { + checkArgument(!Iterables.isEmpty(instants)); + return Ordering.natural().min(instants); + } + /** Returns the latest of a number of given {@link DateTime} instances. */ public static DateTime latestOf(DateTime first, DateTime... rest) { return latestOf(Lists.asList(first, rest)); @@ -66,29 +93,63 @@ public static boolean isBeforeOrAt(DateTime timeToCheck, DateTime timeToCompareT return !timeToCheck.isAfter(timeToCompareTo); } + /** Returns whether the first {@link Instant} is equal to or earlier than the second. */ + public static boolean isBeforeOrAt(Instant timeToCheck, Instant timeToCompareTo) { + return !timeToCheck.isAfter(timeToCompareTo); + } + /** Returns whether the first {@link DateTime} is equal to or later than the second. */ public static boolean isAtOrAfter(DateTime timeToCheck, DateTime timeToCompareTo) { return !timeToCheck.isBefore(timeToCompareTo); } + /** Returns whether the first {@link Instant} is equal to or later than the second. */ + public static boolean isAtOrAfter(Instant timeToCheck, Instant timeToCompareTo) { + return !timeToCheck.isBefore(timeToCompareTo); + } + /** * Adds years to a date, in the {@code Duration} sense of semantic years. Use this instead of * {@link DateTime#plusYears} to ensure that we never end up on February 29. */ + @Deprecated public static DateTime leapSafeAddYears(DateTime now, int years) { checkArgument(years >= 0); return years == 0 ? now : now.plusYears(1).plusYears(years - 1); } + /** + * Adds years to a date, in the {@code Duration} sense of semantic years. Use this instead of + * {@link DateTime#plusYears} to ensure that we never end up on February 29. + */ + public static Instant leapSafeAddYears(Instant now, long years) { + checkArgument(years >= 0); + return (years == 0) + ? now + : now.atZone(ZoneOffset.UTC).plusYears(1).plusYears(years - 1).toInstant(); + } + /** * Subtracts years from a date, in the {@code Duration} sense of semantic years. Use this instead * of {@link DateTime#minusYears} to ensure that we never end up on February 29. */ + @Deprecated public static DateTime leapSafeSubtractYears(DateTime now, int years) { checkArgument(years >= 0); return years == 0 ? now : now.minusYears(1).minusYears(years - 1); } + /** + * Subtracts years from a date, in the {@code Duration} sense of semantic years. Use this instead + * of {@link DateTime#minusYears} to ensure that we never end up on February 29. + */ + public static Instant leapSafeSubtractYears(Instant now, long years) { + checkArgument(years >= 0); + return (years == 0) + ? now + : now.atZone(ZoneOffset.UTC).minusYears(1).minusYears(years - 1).toInstant(); + } + public static Date toSqlDate(LocalDate localDate) { return new Date(localDate.toDateTimeAtStartOfDay().getMillis()); } @@ -96,4 +157,28 @@ public static Date toSqlDate(LocalDate localDate) { public static LocalDate toLocalDate(Date date) { return new LocalDate(date.getTime(), DateTimeZone.UTC); } + + /** Convert a joda {@link DateTime} to a java.time {@link Instant}, null-safe. */ + @Nullable + public static Instant toInstant(@Nullable DateTime dateTime) { + return (dateTime == null) ? null : Instant.ofEpochMilli(dateTime.getMillis()); + } + + /** Convert a java.time {@link Instant} to a joda {@link DateTime}, null-safe. */ + @Nullable + public static DateTime toDateTime(@Nullable Instant instant) { + return (instant == null) ? null : new DateTime(instant.toEpochMilli(), DateTimeZone.UTC); + } + + public static Instant plusYears(Instant instant, int years) { + return instant.atZone(ZoneOffset.UTC).plusYears(years).toInstant(); + } + + public static Instant plusDays(Instant instant, int days) { + return instant.atZone(ZoneOffset.UTC).plusDays(days).toInstant(); + } + + public static Instant minusDays(Instant instant, int days) { + return instant.atZone(ZoneOffset.UTC).minusDays(days).toInstant(); + } } diff --git a/common/src/main/java/google/registry/util/SystemClock.java b/common/src/main/java/google/registry/util/SystemClock.java index eb553678d45..52ac9893f67 100644 --- a/common/src/main/java/google/registry/util/SystemClock.java +++ b/common/src/main/java/google/registry/util/SystemClock.java @@ -17,6 +17,7 @@ import static org.joda.time.DateTimeZone.UTC; import jakarta.inject.Inject; +import java.time.Instant; import javax.annotation.concurrent.ThreadSafe; import org.joda.time.DateTime; @@ -34,4 +35,9 @@ public SystemClock() {} public DateTime nowUtc() { return DateTime.now(UTC); } + + @Override + public Instant now() { + return Instant.now(); + } } diff --git a/common/src/testing/java/google/registry/testing/FakeClock.java b/common/src/testing/java/google/registry/testing/FakeClock.java index 526f943a2ea..656ec7109b9 100644 --- a/common/src/testing/java/google/registry/testing/FakeClock.java +++ b/common/src/testing/java/google/registry/testing/FakeClock.java @@ -19,6 +19,7 @@ import static org.joda.time.Duration.millis; import google.registry.util.Clock; +import java.time.Instant; import java.util.concurrent.atomic.AtomicLong; import javax.annotation.concurrent.ThreadSafe; import org.joda.time.DateTime; @@ -54,6 +55,11 @@ public DateTime nowUtc() { return new DateTime(currentTimeMillis.addAndGet(autoIncrementStepMs), UTC); } + @Override + public Instant now() { + return Instant.ofEpochMilli(currentTimeMillis.addAndGet(autoIncrementStepMs)); + } + /** * Sets the increment applied to the clock whenever it is queried. The increment is zero by * default: the clock is left unchanged when queried. diff --git a/core/src/main/java/google/registry/batch/BulkDomainTransferAction.java b/core/src/main/java/google/registry/batch/BulkDomainTransferAction.java index 990428edde9..b3bcc419d3b 100644 --- a/core/src/main/java/google/registry/batch/BulkDomainTransferAction.java +++ b/core/src/main/java/google/registry/batch/BulkDomainTransferAction.java @@ -17,6 +17,7 @@ import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8; import static google.registry.flows.FlowUtils.marshalWithLenientRetry; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; +import static google.registry.util.DateTimeUtils.END_INSTANT; import static jakarta.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; import static jakarta.servlet.http.HttpServletResponse.SC_NO_CONTENT; import static jakarta.servlet.http.HttpServletResponse.SC_OK; @@ -39,7 +40,6 @@ import google.registry.request.Response; import google.registry.request.auth.Auth; import google.registry.request.lock.LockHandler; -import google.registry.util.DateTimeUtils; import jakarta.inject.Inject; import jakarta.inject.Named; import java.util.Optional; @@ -212,7 +212,7 @@ private void runTransferFlowInTransaction(String domainName) { private boolean shouldSkipDomain(String domainName) { Optional maybeDomain = - ForeignKeyUtils.loadResource(Domain.class, domainName, tm().getTransactionTime()); + ForeignKeyUtils.loadResource(Domain.class, domainName, tm().getTxTime()); if (maybeDomain.isEmpty()) { logger.atWarning().log("Domain '%s' was already deleted", domainName); missingDomains++; @@ -232,7 +232,7 @@ private boolean shouldSkipDomain(String domainName) { return true; } if (domain.getStatusValues().contains(StatusValue.PENDING_DELETE) - || !domain.getDeletionTime().equals(DateTimeUtils.END_OF_TIME)) { + || !domain.getDeletionTime().equals(END_INSTANT)) { logger.atWarning().log("Domain '%s' is in PENDING_DELETE", domainName); pendingDelete++; return true; diff --git a/core/src/main/java/google/registry/batch/DeleteExpiredDomainsAction.java b/core/src/main/java/google/registry/batch/DeleteExpiredDomainsAction.java index 5281360342c..a23d5ce17eb 100644 --- a/core/src/main/java/google/registry/batch/DeleteExpiredDomainsAction.java +++ b/core/src/main/java/google/registry/batch/DeleteExpiredDomainsAction.java @@ -18,6 +18,7 @@ import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8; import static google.registry.flows.FlowUtils.marshalWithLenientRetry; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; +import static google.registry.util.DateTimeUtils.END_INSTANT; import static google.registry.util.DateTimeUtils.END_OF_TIME; import static google.registry.util.ResourceUtils.readResourceUtf8; import static jakarta.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; @@ -177,7 +178,7 @@ private boolean runDomainDeleteFlow(Domain domain) { "Failed to delete domain %s because of its autorenew end time: %s.", transDomain.getDomainName(), transDomain.getAutorenewEndTime()); return Optional.empty(); - } else if (domain.getDeletionTime().isBefore(END_OF_TIME)) { + } else if (domain.getDeletionTime().isBefore(END_INSTANT)) { logger.atSevere().log( "Failed to delete domain %s because it was already deleted on %s.", transDomain.getDomainName(), transDomain.getDeletionTime()); diff --git a/core/src/main/java/google/registry/batch/RelockDomainAction.java b/core/src/main/java/google/registry/batch/RelockDomainAction.java index f9a5ab5bec9..010eef40f93 100644 --- a/core/src/main/java/google/registry/batch/RelockDomainAction.java +++ b/core/src/main/java/google/registry/batch/RelockDomainAction.java @@ -188,7 +188,7 @@ private void verifyDomainAndLockState(RegistryLock oldLock, Domain domain) { "Domain %s has a pending delete.", domainName); checkArgument( - !DateTimeUtils.isAtOrAfter(tm().getTransactionTime(), domain.getDeletionTime()), + !DateTimeUtils.isAtOrAfter(tm().getTxTime(), domain.getDeletionTime()), "Domain %s has been deleted.", domainName); checkArgument( diff --git a/core/src/main/java/google/registry/beam/billing/ExpandBillingRecurrencesPipeline.java b/core/src/main/java/google/registry/beam/billing/ExpandBillingRecurrencesPipeline.java index 193103c69a9..1bfcc1c487c 100644 --- a/core/src/main/java/google/registry/beam/billing/ExpandBillingRecurrencesPipeline.java +++ b/core/src/main/java/google/registry/beam/billing/ExpandBillingRecurrencesPipeline.java @@ -24,6 +24,7 @@ import static google.registry.util.DateTimeUtils.START_OF_TIME; import static google.registry.util.DateTimeUtils.earliestOf; import static google.registry.util.DateTimeUtils.latestOf; +import static google.registry.util.DateTimeUtils.toInstant; import static org.apache.beam.sdk.values.TypeDescriptors.voids; import com.google.common.collect.ImmutableMap; @@ -372,7 +373,7 @@ private void expandOneRecurrence( // during ARGP). // // See: DomainFlowUtils#createCancellingRecords - domain.getDeletionTime().isBefore(billingTime) + domain.getDeletionTime().isBefore(toInstant(billingTime)) ? ImmutableSet.of() : ImmutableSet.of( DomainTransactionRecord.create( diff --git a/core/src/main/java/google/registry/flows/ResourceFlowUtils.java b/core/src/main/java/google/registry/flows/ResourceFlowUtils.java index 122d5ded3b0..101bafa96f0 100644 --- a/core/src/main/java/google/registry/flows/ResourceFlowUtils.java +++ b/core/src/main/java/google/registry/flows/ResourceFlowUtils.java @@ -16,6 +16,7 @@ import static com.google.common.collect.Sets.intersection; import static google.registry.model.EppResourceUtils.isLinked; +import static google.registry.util.DateTimeUtils.toInstant; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; @@ -44,6 +45,7 @@ import google.registry.model.host.Host; import google.registry.model.transfer.TransferStatus; import google.registry.persistence.VKey; +import java.time.Instant; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -89,6 +91,11 @@ public static void verifyTransferInitiator(String registrarId, Domain domain) public static R loadAndVerifyExistence( Class clazz, String targetId, DateTime now) throws ResourceDoesNotExistException { + return loadAndVerifyExistence(clazz, targetId, toInstant(now)); + } + + public static R loadAndVerifyExistence( + Class clazz, String targetId, Instant now) throws ResourceDoesNotExistException { return verifyExistence(clazz, targetId, ForeignKeyUtils.loadResource(clazz, targetId, now)); } @@ -197,8 +204,8 @@ public static void verifyAllStatusesAreClientSettable(Set statusVal * * @param domain is the domain already projected at approvalTime */ - public static DateTime computeExDateForApprovalTime( - DomainBase domain, DateTime approvalTime, Period period) { + public static Instant computeExDateForApprovalTime( + DomainBase domain, Instant approvalTime, Period period) { boolean inAutoRenew = domain.getGracePeriodStatuses().contains(GracePeriodStatus.AUTO_RENEW); // inAutoRenew is set to false if the period is zero because a zero-period transfer should not // subsume an autorenew. diff --git a/core/src/main/java/google/registry/flows/domain/DomainDeleteFlow.java b/core/src/main/java/google/registry/flows/domain/DomainDeleteFlow.java index 6414aa93b80..9cb5a7c4b66 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainDeleteFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainDeleteFlow.java @@ -236,7 +236,7 @@ public EppResponse run() throws EppException { } // Cancel any grace periods that were still active, and set the expiration time accordingly. - DateTime newExpirationTime = existingDomain.getRegistrationExpirationTime(); + DateTime newExpirationTime = existingDomain.getRegistrationExpirationDateTime(); for (GracePeriod gracePeriod : existingDomain.getGracePeriods()) { // No cancellation is written if the grace period was not for a billable event. if (gracePeriod.hasBillingEvent()) { @@ -289,7 +289,7 @@ public EppResponse run() throws EppException { flowCustomLogic.beforeResponse( BeforeResponseParameters.newBuilder() .setResultCode( - newDomain.getDeletionTime().isAfter(now) + newDomain.getDeletionDateTime().isAfter(now) ? SUCCESS_WITH_ACTION_PENDING : SUCCESS) .setResponseExtensions( diff --git a/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java b/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java index fb6b358dca1..3a2fdd196fc 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java +++ b/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java @@ -503,7 +503,7 @@ public static BillingRecurrence.Builder newAutorenewBillingEvent(Domain domain) .setFlags(ImmutableSet.of(Flag.AUTO_RENEW)) .setTargetId(domain.getDomainName()) .setRegistrarId(domain.getCurrentSponsorRegistrarId()) - .setEventTime(domain.getRegistrationExpirationTime()); + .setEventTime(domain.getRegistrationExpirationDateTime()); } /** @@ -514,7 +514,7 @@ public static Autorenew.Builder newAutorenewPollMessage(Domain domain) { return new Autorenew.Builder() .setTargetId(domain.getDomainName()) .setRegistrarId(domain.getCurrentSponsorRegistrarId()) - .setEventTime(domain.getRegistrationExpirationTime()) + .setEventTime(domain.getRegistrationExpirationDateTime()) .setMsg("Domain was auto-renewed."); } @@ -658,7 +658,7 @@ static void handleFeeRequest( // process, don't count as expired for the purposes of requiring an added year of renewal on // restore because they can't be restored in the first place. boolean isExpired = - domain.isPresent() && domain.get().getRegistrationExpirationTime().isBefore(now); + domain.isPresent() && domain.get().getRegistrationExpirationDateTime().isBefore(now); fees = pricingLogic.getRestorePrice(tld, domainNameString, now, isExpired).getFees(); } case TRANSFER -> { diff --git a/core/src/main/java/google/registry/flows/domain/DomainInfoFlow.java b/core/src/main/java/google/registry/flows/domain/DomainInfoFlow.java index 15a9b1b48c9..8815d2ca7bd 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainInfoFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainInfoFlow.java @@ -122,8 +122,8 @@ public EppResponse run() throws EppException { .setNameservers( hostsRequest.requestDelegated() ? domain.loadNameserverHostNames() : null) .setCreationTime(domain.getCreationTime()) - .setLastEppUpdateTime(domain.getLastEppUpdateTime()) - .setRegistrationExpirationTime(domain.getRegistrationExpirationTime()) + .setLastEppUpdateTime(domain.getLastEppUpdateDateTime()) + .setRegistrationExpirationTime(domain.getRegistrationExpirationDateTime()) .setLastTransferTime(domain.getLastTransferTime()); // If authInfo is non-null, then the caller is authorized to see the full information since we diff --git a/core/src/main/java/google/registry/flows/domain/DomainRenewFlow.java b/core/src/main/java/google/registry/flows/domain/DomainRenewFlow.java index ec7d982ca77..32278bf53d4 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainRenewFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainRenewFlow.java @@ -191,7 +191,7 @@ public EppResponse run() throws EppException { existingDomain = maybeApplyBulkPricingRemovalToken(existingDomain, allocationToken); DateTime newExpirationTime = - leapSafeAddYears(existingDomain.getRegistrationExpirationTime(), years); // Uncapped + leapSafeAddYears(existingDomain.getRegistrationExpirationDateTime(), years); // Uncapped validateRegistrationPeriod(now, newExpirationTime); Optional feeRenew = eppInput.getSingleExtension(FeeRenewCommandExtension.class); @@ -328,8 +328,9 @@ private void verifyRenewAllowed( // We only allow __REMOVE_BULK_PRICING__ token on bulk pricing domains for now verifyBulkTokenAllowedOnDomain(existingDomain, allocationToken); // If the date they specify doesn't match the expiration, fail. (This is an idempotence check). - if (!command.getCurrentExpirationDate().equals( - existingDomain.getRegistrationExpirationTime().toLocalDate())) { + if (!command + .getCurrentExpirationDate() + .equals(existingDomain.getRegistrationExpirationDateTime().toLocalDate())) { throw new IncorrectCurrentExpirationDateException(); } } diff --git a/core/src/main/java/google/registry/flows/domain/DomainRestoreRequestFlow.java b/core/src/main/java/google/registry/flows/domain/DomainRestoreRequestFlow.java index 29fc413d351..3ec154e7f8b 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainRestoreRequestFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainRestoreRequestFlow.java @@ -138,7 +138,7 @@ public EppResponse run() throws EppException { Update command = (Update) resourceCommand; DateTime now = tm().getTransactionTime(); Domain existingDomain = loadAndVerifyExistence(Domain.class, targetId, now); - boolean isExpired = existingDomain.getRegistrationExpirationTime().isBefore(now); + boolean isExpired = existingDomain.getRegistrationExpirationDateTime().isBefore(now); FeesAndCredits feesAndCredits = pricingLogic.getRestorePrice(Tld.get(existingDomain.getTld()), targetId, now, isExpired); Optional feeUpdate = @@ -149,7 +149,7 @@ public EppResponse run() throws EppException { ImmutableSet.Builder entitiesToInsert = new ImmutableSet.Builder<>(); DateTime newExpirationTime = - existingDomain.getRegistrationExpirationTime().plusYears(isExpired ? 1 : 0); + existingDomain.getRegistrationExpirationDateTime().plusYears(isExpired ? 1 : 0); // Restore the expiration time on the deleted domain, except if that's already passed, then add // a year and bill for it immediately, with no grace period. if (isExpired) { diff --git a/core/src/main/java/google/registry/flows/domain/DomainTransferApproveFlow.java b/core/src/main/java/google/registry/flows/domain/DomainTransferApproveFlow.java index cea6f656825..7b7eb47b328 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainTransferApproveFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainTransferApproveFlow.java @@ -33,6 +33,8 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.util.CollectionUtils.union; import static google.registry.util.DateTimeUtils.END_OF_TIME; +import static google.registry.util.DateTimeUtils.toDateTime; +import static google.registry.util.DateTimeUtils.toInstant; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @@ -193,7 +195,9 @@ public EppResponse run() throws EppException { updateAutorenewRecurrenceEndTime( existingDomain, existingBillingRecurrence, now, domainHistoryId); DateTime newExpirationTime = - computeExDateForApprovalTime(existingDomain, now, transferData.getTransferPeriod()); + toDateTime( + computeExDateForApprovalTime( + existingDomain, toInstant(now), transferData.getTransferPeriod())); // Create a new autorenew event starting at the expiration time. BillingRecurrence autorenewEvent = new BillingRecurrence.Builder() @@ -268,8 +272,11 @@ public EppResponse run() throws EppException { // been implicitly server approved. tm().delete(existingDomain.getTransferData().getServerApproveEntities()); return responseBuilder - .setResData(createTransferResponse( - targetId, newDomain.getTransferData(), newDomain.getRegistrationExpirationTime())) + .setResData( + createTransferResponse( + targetId, + newDomain.getTransferData(), + newDomain.getRegistrationExpirationDateTime())) .build(); } diff --git a/core/src/main/java/google/registry/flows/domain/DomainTransferQueryFlow.java b/core/src/main/java/google/registry/flows/domain/DomainTransferQueryFlow.java index b562422b6b5..3e002f60c13 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainTransferQueryFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainTransferQueryFlow.java @@ -15,15 +15,17 @@ package google.registry.flows.domain; import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn; +import static google.registry.flows.ResourceFlowUtils.computeExDateForApprovalTime; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfo; import static google.registry.flows.domain.DomainTransferUtils.createTransferResponse; +import static google.registry.util.DateTimeUtils.toDateTime; +import static google.registry.util.DateTimeUtils.toInstant; import google.registry.flows.EppException; import google.registry.flows.ExtensionManager; import google.registry.flows.FlowModule.RegistrarId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.ResourceFlowUtils; import google.registry.flows.TransactionalFlow; import google.registry.flows.annotations.ReportingSpec; import google.registry.flows.exceptions.NoTransferHistoryToQueryException; @@ -88,11 +90,12 @@ public EppResponse run() throws EppException { } DateTime newExpirationTime = null; if (transferData.getTransferStatus().isApproved()) { - newExpirationTime = transferData.getTransferredRegistrationExpirationTime(); + newExpirationTime = transferData.getTransferredRegistrationExpirationDateTime(); } else if (transferData.getTransferStatus().equals(TransferStatus.PENDING)) { newExpirationTime = - ResourceFlowUtils.computeExDateForApprovalTime( - domain, now, domain.getTransferData().getTransferPeriod()); + toDateTime( + computeExDateForApprovalTime( + domain, toInstant(now), domain.getTransferData().getTransferPeriod())); } return responseBuilder .setResData(createTransferResponse(targetId, transferData, newExpirationTime)) diff --git a/core/src/main/java/google/registry/flows/domain/DomainTransferRejectFlow.java b/core/src/main/java/google/registry/flows/domain/DomainTransferRejectFlow.java index 351ee5e18fd..39b25d8d551 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainTransferRejectFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainTransferRejectFlow.java @@ -32,6 +32,7 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.util.CollectionUtils.union; import static google.registry.util.DateTimeUtils.END_OF_TIME; +import static google.registry.util.DateTimeUtils.toDateTime; import com.google.common.collect.ImmutableSet; import google.registry.flows.EppException; @@ -53,6 +54,7 @@ import google.registry.model.tld.Tld; import google.registry.model.transfer.TransferStatus; import jakarta.inject.Inject; +import java.time.Instant; import java.util.Optional; import org.joda.time.DateTime; @@ -92,7 +94,7 @@ public EppResponse run() throws EppException { extensionManager.register(MetadataExtension.class); validateRegistrarIsLoggedIn(registrarId); extensionManager.validate(); - DateTime now = tm().getTransactionTime(); + Instant now = tm().getTxTime(); Domain existingDomain = loadAndVerifyExistence(Domain.class, targetId, now); Tld tld = Tld.get(existingDomain.getTld()); HistoryEntryId domainHistoryId = createHistoryEntryId(existingDomain); @@ -107,13 +109,14 @@ public EppResponse run() throws EppException { checkAllowedAccessToTld(registrarId, existingDomain.getTld()); } Domain newDomain = - denyPendingTransfer(existingDomain, TransferStatus.CLIENT_REJECTED, now, registrarId); - DomainHistory domainHistory = buildDomainHistory(newDomain, tld, now); + denyPendingTransfer( + existingDomain, TransferStatus.CLIENT_REJECTED, toDateTime(now), registrarId); + DomainHistory domainHistory = buildDomainHistory(newDomain, tld, toDateTime(now)); tm().update(newDomain); tm().insertAll( domainHistory, createGainingTransferPollMessage( - targetId, newDomain.getTransferData(), null, now, domainHistoryId)); + targetId, newDomain.getTransferData(), null, toDateTime(now), domainHistoryId)); // Reopen the autorenew event and poll message that we closed for the implicit transfer. This // may end up recreating the poll message if it was deleted upon the transfer request. BillingRecurrence existingBillingRecurrence = diff --git a/core/src/main/java/google/registry/flows/domain/DomainTransferRequestFlow.java b/core/src/main/java/google/registry/flows/domain/DomainTransferRequestFlow.java index db7ce17bfe5..ad711a9b722 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainTransferRequestFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainTransferRequestFlow.java @@ -35,6 +35,8 @@ import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_ACTION_PENDING; import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; +import static google.registry.util.DateTimeUtils.toDateTime; +import static google.registry.util.DateTimeUtils.toInstant; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @@ -80,6 +82,7 @@ import google.registry.model.transfer.TransferResponse.DomainTransferResponse; import google.registry.model.transfer.TransferStatus; import jakarta.inject.Inject; +import java.time.Instant; import java.util.Optional; import org.joda.time.DateTime; @@ -230,7 +233,9 @@ public EppResponse run() throws EppException { Domain domainAtTransferTime = existingDomain.cloneProjectedAtTime(automaticTransferTime); // The new expiration time if there is a server approval. DateTime serverApproveNewExpirationTime = - computeExDateForApprovalTime(domainAtTransferTime, automaticTransferTime, period); + toDateTime( + computeExDateForApprovalTime( + domainAtTransferTime, toInstant(automaticTransferTime), period)); // Create speculative entities in anticipation of an automatic server approval. ImmutableSet serverApproveEntities = createTransferServerApproveEntities( @@ -287,7 +292,7 @@ public EppResponse run() throws EppException { tm().insertAll(domainHistory, requestPollMessage); return responseBuilder .setResultFromCode(SUCCESS_WITH_ACTION_PENDING) - .setResData(createResponse(period, existingDomain, newDomain, now)) + .setResData(createResponse(period, existingDomain, newDomain, toInstant(now))) .setExtensions(createResponseExtensions(feesAndCredits, feeTransfer)) .build(); } @@ -375,14 +380,14 @@ private DomainHistory buildDomainHistory(Domain newDomain, Tld tld, DateTime now } private DomainTransferResponse createResponse( - Period period, Domain existingDomain, Domain newDomain, DateTime now) { + Period period, Domain existingDomain, Domain newDomain, Instant now) { // If the registration were approved this instant, this is what the new expiration would be, // because we cap at 10 years from the moment of approval. This is different from the server // approval new expiration time, which is capped at 10 years from the server approve time. - DateTime approveNowExtendedRegistrationTime = + Instant approveNowExtendedRegistrationTime = computeExDateForApprovalTime(existingDomain, now, period); return createTransferResponse( - targetId, newDomain.getTransferData(), approveNowExtendedRegistrationTime); + targetId, newDomain.getTransferData(), toDateTime(approveNowExtendedRegistrationTime)); } private static ImmutableList createResponseExtensions( diff --git a/core/src/main/java/google/registry/flows/domain/DomainTransferUtils.java b/core/src/main/java/google/registry/flows/domain/DomainTransferUtils.java index 60000b224d5..f9c0ab16372 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainTransferUtils.java +++ b/core/src/main/java/google/registry/flows/domain/DomainTransferUtils.java @@ -184,7 +184,7 @@ public static PollMessage createGainingTransferPollMessage( HistoryEntryId domainHistoryId) { return new PollMessage.OneTime.Builder() .setRegistrarId(transferData.getGainingRegistrarId()) - .setEventTime(transferData.getPendingTransferExpirationTime()) + .setEventTime(transferData.getPendingTransferExpirationDateTime()) .setMsg(transferData.getTransferStatus().getMessage()) .setResponseData( ImmutableList.of( @@ -206,7 +206,7 @@ public static PollMessage createLosingTransferPollMessage( HistoryEntryId domainHistoryId) { return new PollMessage.OneTime.Builder() .setRegistrarId(transferData.getLosingRegistrarId()) - .setEventTime(transferData.getPendingTransferExpirationTime()) + .setEventTime(transferData.getPendingTransferExpirationDateTime()) .setMsg(transferData.getTransferStatus().getMessage()) .setResponseData( ImmutableList.of( @@ -224,7 +224,7 @@ static DomainTransferResponse createTransferResponse( .setDomainName(targetId) .setGainingRegistrarId(transferData.getGainingRegistrarId()) .setLosingRegistrarId(transferData.getLosingRegistrarId()) - .setPendingTransferExpirationTime(transferData.getPendingTransferExpirationTime()) + .setPendingTransferExpirationTime(transferData.getPendingTransferExpirationDateTime()) .setTransferRequestTime(transferData.getTransferRequestTime()) .setTransferStatus(transferData.getTransferStatus()) .setExtendedRegistrationExpirationTime(extendedRegistrationExpirationTime) diff --git a/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java b/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java index 16f89e57b82..752c194ebbf 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java @@ -281,7 +281,7 @@ private Domain performUpdate(Update command, Domain domain, DateTime now) throws if (superuserExt.get().getAutorenews().isPresent()) { boolean autorenews = superuserExt.get().getAutorenews().get(); domainBuilder.setAutorenewEndTime( - Optional.ofNullable(autorenews ? null : domain.getRegistrationExpirationTime())); + Optional.ofNullable(autorenews ? null : domain.getRegistrationExpirationDateTime())); } } return domainBuilder.build(); diff --git a/core/src/main/java/google/registry/flows/host/HostInfoFlow.java b/core/src/main/java/google/registry/flows/host/HostInfoFlow.java index af5a5b25851..4647ade8963 100644 --- a/core/src/main/java/google/registry/flows/host/HostInfoFlow.java +++ b/core/src/main/java/google/registry/flows/host/HostInfoFlow.java @@ -99,7 +99,7 @@ public EppResponse run() throws EppException { .setCreationRegistrarId(host.getCreationRegistrarId()) .setCreationTime(host.getCreationTime()) .setLastEppUpdateRegistrarId(host.getLastEppUpdateRegistrarId()) - .setLastEppUpdateTime(host.getLastEppUpdateTime()) + .setLastEppUpdateTime(host.getLastEppUpdateDateTime()) .build()) .build(); } diff --git a/core/src/main/java/google/registry/model/EppResource.java b/core/src/main/java/google/registry/model/EppResource.java index ee5c376ef63..f8007038f2d 100644 --- a/core/src/main/java/google/registry/model/EppResource.java +++ b/core/src/main/java/google/registry/model/EppResource.java @@ -25,6 +25,7 @@ import static google.registry.util.CollectionUtils.nullToEmpty; import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import static google.registry.util.DateTimeUtils.END_OF_TIME; +import static google.registry.util.DateTimeUtils.toInstant; import com.github.benmanes.caffeine.cache.CacheLoader; import com.github.benmanes.caffeine.cache.LoadingCache; @@ -45,6 +46,7 @@ import jakarta.persistence.MappedSuperclass; import jakarta.persistence.Transient; import java.time.Duration; +import java.time.Instant; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -163,10 +165,15 @@ public String getCreationRegistrarId() { return creationRegistrarId; } - public DateTime getLastEppUpdateTime() { + @Deprecated + public DateTime getLastEppUpdateDateTime() { return lastEppUpdateTime; } + public Instant getLastEppUpdateTime() { + return toInstant(lastEppUpdateTime); + } + public String getLastEppUpdateRegistrarId() { return lastEppUpdateRegistrarId; } @@ -185,13 +192,22 @@ public final ImmutableSet getStatusValues() { return nullToEmptyImmutableCopy(statuses); } - public DateTime getDeletionTime() { + @Deprecated + public DateTime getDeletionDateTime() { return deletionTime; } + public Instant getDeletionTime() { + return toInstant(deletionTime); + } + /** Return a clone of the resource with timed status values modified using the given time. */ + @Deprecated public abstract EppResource cloneProjectedAtTime(DateTime now); + /** Return a clone of the resource with timed status values modified using the given time. */ + public abstract EppResource cloneProjectedAtInstant(Instant now); + /** Get the foreign key string for this resource. */ public abstract String getForeignKey(); diff --git a/core/src/main/java/google/registry/model/EppResourceUtils.java b/core/src/main/java/google/registry/model/EppResourceUtils.java index 4af8c6ccf5c..c64591ce426 100644 --- a/core/src/main/java/google/registry/model/EppResourceUtils.java +++ b/core/src/main/java/google/registry/model/EppResourceUtils.java @@ -34,6 +34,7 @@ import google.registry.model.transfer.TransferStatus; import google.registry.persistence.VKey; import jakarta.persistence.Query; +import java.time.Instant; import java.util.Comparator; import java.util.function.Function; import javax.annotation.Nullable; @@ -83,7 +84,7 @@ public static Function transformAtTime(final DateT * exclusive, which happily maps to the behavior of Interval. */ private static Interval getLifetime(EppResource resource) { - return new Interval(resource.getCreationTime(), resource.getDeletionTime()); + return new Interval(resource.getCreationTime(), resource.getDeletionDateTime()); } public static boolean isActive(EppResource resource, DateTime time) { @@ -108,7 +109,7 @@ public static void setAutomaticTransferSuccessProperties( builder .removeStatusValue(StatusValue.PENDING_TRANSFER) .setTransferData(transferDataBuilder.build()) - .setLastTransferTime(transferData.getPendingTransferExpirationTime()) + .setLastTransferTime(transferData.getPendingTransferExpirationDateTime()) .setPersistedCurrentSponsorRegistrarId(transferData.getGainingRegistrarId()); } @@ -120,10 +121,10 @@ public static void setAutomaticTransferSuccessProperties( * */ public static void projectResourceOntoBuilderAtTime( - DomainBase domain, DomainBase.Builder builder, DateTime now) { + DomainBase domain, DomainBase.Builder builder, Instant now) { DomainTransferData transferData = domain.getTransferData(); // If there's a pending transfer that has expired, process it. - DateTime expirationTime = transferData.getPendingTransferExpirationTime(); + Instant expirationTime = transferData.getPendingTransferExpirationTime(); if (TransferStatus.PENDING.equals(transferData.getTransferStatus()) && isBeforeOrAt(expirationTime, now)) { setAutomaticTransferSuccessProperties(builder, transferData); diff --git a/core/src/main/java/google/registry/model/ForeignKeyUtils.java b/core/src/main/java/google/registry/model/ForeignKeyUtils.java index 9a0b08006e0..ba1ab670b79 100644 --- a/core/src/main/java/google/registry/model/ForeignKeyUtils.java +++ b/core/src/main/java/google/registry/model/ForeignKeyUtils.java @@ -35,6 +35,7 @@ import google.registry.persistence.transaction.JpaTransactionManager; import google.registry.util.NonFinalForTesting; import java.time.Duration; +import java.time.Instant; import java.util.Collection; import java.util.Map; import java.util.Map.Entry; @@ -81,6 +82,7 @@ public static Optional> loadKey( *

Returns null if no resource with this foreign key was ever created or if the most recently * created resource was deleted before time "now". */ + @Deprecated public static Optional loadResource( Class clazz, String foreignKey, DateTime now) { // Note: no need to project to "now" because loadResources already does @@ -88,6 +90,19 @@ public static Optional loadResource( loadResources(clazz, ImmutableList.of(foreignKey), now).get(foreignKey)); } + /** + * Loads an {@link EppResource} from the database by foreign key. + * + *

Returns null if no resource with this foreign key was ever created or if the most recently + * created resource was deleted before time "now". + */ + public static Optional loadResource( + Class clazz, String foreignKey, Instant now) { + // Note: no need to project to "now" because loadResources already does + return Optional.ofNullable( + loadResources(clazz, ImmutableList.of(foreignKey), now).get(foreignKey)); + } + /** * Load a map of {@link String} foreign keys to {@link VKey}s to {@link EppResource} that are * active at or after the specified moment in time. @@ -110,13 +125,29 @@ public static ImmutableMap> loadKeys( * or has been soft-deleted. */ @SuppressWarnings("unchecked") + @Deprecated public static ImmutableMap loadResources( Class clazz, Collection foreignKeys, DateTime now) { return loadMostRecentResourceObjects(clazz, foreignKeys, false).entrySet().stream() - .filter(e -> now.isBefore(e.getValue().getDeletionTime())) + .filter(e -> now.isBefore(e.getValue().getDeletionDateTime())) .collect(toImmutableMap(Entry::getKey, e -> (E) e.getValue().cloneProjectedAtTime(now))); } + /** + * Load a map of {@link String} foreign keys to the {@link EppResource} that are active at or + * after the specified moment in time. + * + *

The returned map will omit any foreign keys for which the {@link EppResource} doesn't exist + * or has been soft-deleted. + */ + @SuppressWarnings("unchecked") + public static ImmutableMap loadResources( + Class clazz, Collection foreignKeys, Instant now) { + return loadMostRecentResourceObjects(clazz, foreignKeys, false).entrySet().stream() + .filter(e -> now.isBefore(e.getValue().getDeletionTime())) + .collect(toImmutableMap(Entry::getKey, e -> (E) e.getValue().cloneProjectedAtInstant(now))); + } + /** * Helper method to load {@link VKey}s to all the most recent {@link EppResource}s for the given * foreign keys, regardless of whether they have been soft-deleted. @@ -397,7 +428,7 @@ public static Optional loadResourceByCache( return (Optional) foreignKeyToResourceCache .get(VKey.create(clazz, foreignKey)) - .filter(e -> now.isBefore(e.getDeletionTime())) + .filter(e -> now.isBefore(e.getDeletionDateTime())) .map(e -> e.cloneProjectedAtTime(now)); } } diff --git a/core/src/main/java/google/registry/model/ResourceTransferUtils.java b/core/src/main/java/google/registry/model/ResourceTransferUtils.java index c6a70026622..7d4bc12c628 100644 --- a/core/src/main/java/google/registry/model/ResourceTransferUtils.java +++ b/core/src/main/java/google/registry/model/ResourceTransferUtils.java @@ -50,11 +50,11 @@ public static TransferResponse createTransferResponse( .setDomainName(domain.getForeignKey()) .setExtendedRegistrationExpirationTime( ADD_EXDATE_STATUSES.contains(transferData.getTransferStatus()) - ? transferData.getTransferredRegistrationExpirationTime() + ? transferData.getTransferredRegistrationExpirationDateTime() : null) .setGainingRegistrarId(transferData.getGainingRegistrarId()) .setLosingRegistrarId(transferData.getLosingRegistrarId()) - .setPendingTransferExpirationTime(transferData.getPendingTransferExpirationTime()) + .setPendingTransferExpirationTime(transferData.getPendingTransferExpirationDateTime()) .setTransferRequestTime(transferData.getTransferRequestTime()) .setTransferStatus(transferData.getTransferStatus()) .build(); diff --git a/core/src/main/java/google/registry/model/billing/BillingCancellation.java b/core/src/main/java/google/registry/model/billing/BillingCancellation.java index 1adc3a8f163..045e87b956a 100644 --- a/core/src/main/java/google/registry/model/billing/BillingCancellation.java +++ b/core/src/main/java/google/registry/model/billing/BillingCancellation.java @@ -104,7 +104,7 @@ public static google.registry.model.billing.BillingCancellation forGracePeriod( .setRegistrarId(gracePeriod.getRegistrarId()) .setEventTime(eventTime) // The charge being cancelled will take place at the grace period's expiration time. - .setBillingTime(gracePeriod.getExpirationTime()) + .setBillingTime(gracePeriod.getExpirationDateTime()) .setDomainHistoryId(domainHistoryId); // Set the grace period's billing event using the appropriate Cancellation builder method. if (gracePeriod.getBillingEvent() != null) { diff --git a/core/src/main/java/google/registry/model/domain/Domain.java b/core/src/main/java/google/registry/model/domain/Domain.java index 9a9715243a7..cff00f4db31 100644 --- a/core/src/main/java/google/registry/model/domain/Domain.java +++ b/core/src/main/java/google/registry/model/domain/Domain.java @@ -14,6 +14,8 @@ package google.registry.model.domain; +import static google.registry.util.DateTimeUtils.toInstant; + import google.registry.model.EppResource; import google.registry.model.EppResource.ForeignKeyedEppResource; import google.registry.model.annotations.ExternalMessagingName; @@ -37,6 +39,7 @@ import jakarta.persistence.JoinTable; import jakarta.persistence.OneToMany; import jakarta.persistence.Table; +import java.time.Instant; import java.util.Set; import org.hibernate.Hibernate; import org.joda.time.DateTime; @@ -153,6 +156,11 @@ public VKey createVKey() { @Override public Domain cloneProjectedAtTime(final DateTime now) { + return cloneDomainProjectedAtTime(this, toInstant(now)); + } + + @Override + public Domain cloneProjectedAtInstant(final Instant now) { return cloneDomainProjectedAtTime(this, now); } diff --git a/core/src/main/java/google/registry/model/domain/DomainBase.java b/core/src/main/java/google/registry/model/domain/DomainBase.java index 573f5d213ce..1f051b567ee 100644 --- a/core/src/main/java/google/registry/model/domain/DomainBase.java +++ b/core/src/main/java/google/registry/model/domain/DomainBase.java @@ -32,9 +32,14 @@ import static google.registry.util.DateTimeUtils.earliestOf; import static google.registry.util.DateTimeUtils.isBeforeOrAt; import static google.registry.util.DateTimeUtils.leapSafeAddYears; +import static google.registry.util.DateTimeUtils.plusYears; +import static google.registry.util.DateTimeUtils.toDateTime; +import static google.registry.util.DateTimeUtils.toInstant; import static google.registry.util.DomainNameUtils.canonicalizeHostname; import static google.registry.util.DomainNameUtils.getTldFromDomainName; import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; +import static java.time.ZoneOffset.UTC; +import static java.time.temporal.ChronoUnit.YEARS; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; @@ -79,13 +84,13 @@ import jakarta.persistence.Id; import jakarta.persistence.MappedSuperclass; import jakarta.persistence.Transient; +import java.time.Instant; import java.util.HashSet; import java.util.Optional; import java.util.Set; import javax.annotation.Nullable; import org.hibernate.collection.spi.PersistentSet; import org.joda.time.DateTime; -import org.joda.time.Interval; /** * A persistable domain resource including mutable and non-mutable fields. @@ -285,10 +290,15 @@ public ImmutableSet getSubordinateHosts() { return nullToEmptyImmutableCopy(subordinateHosts); } - public DateTime getRegistrationExpirationTime() { + @Deprecated + public DateTime getRegistrationExpirationDateTime() { return registrationExpirationTime; } + public Instant getRegistrationExpirationTime() { + return toInstant(registrationExpirationTime); + } + public VKey getDeletePollMessage() { return deletePollMessage; } @@ -436,6 +446,11 @@ public ImmutableSet getGracePeriodsOfType(GracePeriodStatus gracePe @Override public DomainBase cloneProjectedAtTime(final DateTime now) { + return cloneDomainProjectedAtTime(this, toInstant(now)); + } + + @Override + public DomainBase cloneProjectedAtInstant(final Instant now) { return cloneDomainProjectedAtTime(this, now); } @@ -444,9 +459,9 @@ public DomainBase cloneProjectedAtTime(final DateTime now) { * parallels the logic in {@code DomainTransferApproveFlow} which handles explicit client * approvals. */ - static T cloneDomainProjectedAtTime(T domain, DateTime now) { + static T cloneDomainProjectedAtTime(T domain, Instant now) { DomainTransferData transferData = domain.getTransferData(); - DateTime transferExpirationTime = transferData.getPendingTransferExpirationTime(); + Instant transferExpirationTime = transferData.getPendingTransferExpirationTime(); // If there's a pending transfer that has expired, handle it. if (TransferStatus.PENDING.equals(transferData.getTransferStatus()) @@ -459,7 +474,7 @@ && isBeforeOrAt(transferExpirationTime, now)) { T domainAtTransferTime = cloneDomainProjectedAtTime(domain, transferExpirationTime.minusMillis(1)); - DateTime expirationDate = transferData.getTransferredRegistrationExpirationTime(); + Instant expirationDate = transferData.getTransferredRegistrationExpirationTime(); if (expirationDate == null) { // Extend the registration by the correct number of years from the expiration time // that was current on the domain right before the transfer, capped at 10 years from @@ -478,7 +493,7 @@ && isBeforeOrAt(transferExpirationTime, now)) { Builder builder = domainAtTransferTime .asBuilder() - .setRegistrationExpirationTime(expirationDate) + .setRegistrationExpirationTime(toDateTime(expirationDate)) // Set the speculatively-written new autorenew events as the domain's autorenew // events. .setAutorenewBillingEvent(transferData.getServerApproveAutorenewEvent()) @@ -492,8 +507,8 @@ && isBeforeOrAt(transferExpirationTime, now)) { GracePeriod.create( GracePeriodStatus.TRANSFER, domain.getRepoId(), - transferExpirationTime.plus( - Tld.get(domain.getTld()).getTransferGracePeriodLength()), + toDateTime(transferExpirationTime) + .plus(Tld.get(domain.getTld()).getTransferGracePeriodLength()), transferData.getGainingRegistrarId(), transferData.getServerApproveBillingEvent()))); } else { @@ -503,32 +518,33 @@ && isBeforeOrAt(transferExpirationTime, now)) { // Set all remaining transfer properties. setAutomaticTransferSuccessProperties(builder, transferData); builder - .setLastEppUpdateTime(transferExpirationTime) + .setLastEppUpdateTime(toDateTime(transferExpirationTime)) .setLastEppUpdateRegistrarId(transferData.getGainingRegistrarId()); // Finish projecting to now. - return (T) builder.build().cloneProjectedAtTime(now); + return (T) builder.build().cloneProjectedAtInstant(now); } - Optional newLastEppUpdateTime = Optional.empty(); + Optional newLastEppUpdateTime = Optional.empty(); // There is no transfer. Do any necessary autorenews for active domains. Builder builder = domain.asBuilder(); if (isBeforeOrAt(domain.getRegistrationExpirationTime(), now) - && END_OF_TIME.equals(domain.getDeletionTime())) { + && END_OF_TIME.equals(domain.getDeletionDateTime())) { // Autorenew by the number of years between the old expiration time and now. - DateTime lastAutorenewTime = + Instant lastAutorenewTime = leapSafeAddYears( domain.getRegistrationExpirationTime(), - new Interval(domain.getRegistrationExpirationTime(), now).toPeriod().getYears()); - DateTime newExpirationTime = lastAutorenewTime.plusYears(1); + YEARS.between(domain.getRegistrationExpirationTime().atZone(UTC), now.atZone(UTC))); + Instant newExpirationTime = plusYears(lastAutorenewTime, 1); builder - .setRegistrationExpirationTime(newExpirationTime) + .setRegistrationExpirationTime(toDateTime(newExpirationTime)) .addGracePeriod( GracePeriod.createForRecurrence( GracePeriodStatus.AUTO_RENEW, domain.getRepoId(), - lastAutorenewTime.plus(Tld.get(domain.getTld()).getAutoRenewGracePeriodLength()), + toDateTime(lastAutorenewTime) + .plus(Tld.get(domain.getTld()).getAutoRenewGracePeriodLength()), domain.getCurrentSponsorRegistrarId(), domain.getAutorenewBillingEvent())); newLastEppUpdateTime = Optional.of(lastAutorenewTime); @@ -551,10 +567,10 @@ && isBeforeOrAt(transferExpirationTime, now)) { // id, so we have to do the comparison instead of having one variable just storing the most // recent time. if (newLastEppUpdateTime.isPresent()) { - if (domain.getLastEppUpdateTime() == null + if (domain.getLastEppUpdateDateTime() == null || newLastEppUpdateTime.get().isAfter(domain.getLastEppUpdateTime())) { builder - .setLastEppUpdateTime(newLastEppUpdateTime.get()) + .setLastEppUpdateTime(toDateTime(newLastEppUpdateTime.get())) .setLastEppUpdateRegistrarId(domain.getCurrentSponsorRegistrarId()); } } @@ -567,8 +583,8 @@ && isBeforeOrAt(transferExpirationTime, now)) { } /** Return what the expiration time would be if the given number of years were added to it. */ - public static DateTime extendRegistrationWithCap( - DateTime now, DateTime currentExpirationTime, @Nullable Integer extendedRegistrationYears) { + public static Instant extendRegistrationWithCap( + Instant now, Instant currentExpirationTime, @Nullable Integer extendedRegistrationYears) { // We must cap registration at the max years (aka 10), even if that truncates the last year. return earliestOf( leapSafeAddYears( @@ -826,16 +842,16 @@ public B copyFrom(DomainBase domainBase) { .setDomainName(domainBase.getDomainName()) .setDeletePollMessage(domainBase.getDeletePollMessage()) .setDsData(domainBase.getDsData()) - .setDeletionTime(domainBase.getDeletionTime()) + .setDeletionTime(domainBase.getDeletionDateTime()) .setGracePeriods(domainBase.getGracePeriods()) .setIdnTableName(domainBase.getIdnTableName()) .setLastTransferTime(domainBase.getLastTransferTime()) .setLaunchNotice(domainBase.getLaunchNotice()) .setLastEppUpdateRegistrarId(domainBase.getLastEppUpdateRegistrarId()) - .setLastEppUpdateTime(domainBase.getLastEppUpdateTime()) + .setLastEppUpdateTime(domainBase.getLastEppUpdateDateTime()) .setNameservers(domainBase.getNameservers()) .setPersistedCurrentSponsorRegistrarId(domainBase.getPersistedCurrentSponsorRegistrarId()) - .setRegistrationExpirationTime(domainBase.getRegistrationExpirationTime()) + .setRegistrationExpirationTime(domainBase.getRegistrationExpirationDateTime()) .setRepoId(domainBase.getRepoId()) .setSmdId(domainBase.getSmdId()) .setSubordinateHosts(domainBase.getSubordinateHosts()) diff --git a/core/src/main/java/google/registry/model/domain/GracePeriodBase.java b/core/src/main/java/google/registry/model/domain/GracePeriodBase.java index a53abe45790..cca7742ed57 100644 --- a/core/src/main/java/google/registry/model/domain/GracePeriodBase.java +++ b/core/src/main/java/google/registry/model/domain/GracePeriodBase.java @@ -14,6 +14,8 @@ package google.registry.model.domain; +import static google.registry.util.DateTimeUtils.toInstant; + import google.registry.model.ImmutableObject; import google.registry.model.UnsafeSerializable; import google.registry.model.billing.BillingEvent; @@ -30,6 +32,7 @@ import jakarta.persistence.Enumerated; import jakarta.persistence.MappedSuperclass; import jakarta.persistence.Transient; +import java.time.Instant; import org.joda.time.DateTime; /** Base class containing common fields and methods for {@link GracePeriod}. */ @@ -90,10 +93,15 @@ public String getDomainRepoId() { return domainRepoId; } - public DateTime getExpirationTime() { + @Deprecated + public DateTime getExpirationDateTime() { return expirationTime; } + public Instant getExpirationTime() { + return toInstant(expirationTime); + } + public String getRegistrarId() { return clientId; } diff --git a/core/src/main/java/google/registry/model/host/HostBase.java b/core/src/main/java/google/registry/model/host/HostBase.java index 8b70e49fc41..66480452b89 100644 --- a/core/src/main/java/google/registry/model/host/HostBase.java +++ b/core/src/main/java/google/registry/model/host/HostBase.java @@ -33,6 +33,7 @@ import jakarta.persistence.Convert; import jakarta.persistence.MappedSuperclass; import java.net.InetAddress; +import java.time.Instant; import java.util.Optional; import java.util.Set; import javax.annotation.Nullable; @@ -132,6 +133,11 @@ public HostBase cloneProjectedAtTime(DateTime now) { return this; } + @Override + public EppResource cloneProjectedAtInstant(Instant now) { + return this; + } + @Override public Builder asBuilder() { return new Builder<>(clone(this)); @@ -232,13 +238,13 @@ public B setLastTransferTime(DateTime lastTransferTime) { public B copyFrom(HostBase hostBase) { return setCreationRegistrarId(hostBase.getCreationRegistrarId()) .setCreationTime(hostBase.getCreationTime()) - .setDeletionTime(hostBase.getDeletionTime()) + .setDeletionTime(hostBase.getDeletionDateTime()) .setHostName(hostBase.getHostName()) .setInetAddresses(hostBase.getInetAddresses()) .setLastTransferTime(hostBase.getLastTransferTime()) .setLastSuperordinateChange(hostBase.getLastSuperordinateChange()) .setLastEppUpdateRegistrarId(hostBase.getLastEppUpdateRegistrarId()) - .setLastEppUpdateTime(hostBase.getLastEppUpdateTime()) + .setLastEppUpdateTime(hostBase.getLastEppUpdateDateTime()) .setPersistedCurrentSponsorRegistrarId(hostBase.getPersistedCurrentSponsorRegistrarId()) .setRepoId(hostBase.getRepoId()) .setSuperordinateDomain(hostBase.getSuperordinateDomain()) diff --git a/core/src/main/java/google/registry/model/poll/PollMessage.java b/core/src/main/java/google/registry/model/poll/PollMessage.java index 2e73951e3f3..16172266164 100644 --- a/core/src/main/java/google/registry/model/poll/PollMessage.java +++ b/core/src/main/java/google/registry/model/poll/PollMessage.java @@ -437,7 +437,7 @@ void postLoad() { .setTransferStatus(transferResponse.getTransferStatus()) .setTransferRequestTime(transferResponse.getTransferRequestTime()) .setPendingTransferExpirationTime( - transferResponse.getPendingTransferExpirationTime()) + transferResponse.getPendingTransferExpirationDateTime()) .setExtendedRegistrationExpirationTime(extendedRegistrationExpirationTime) .build(); } diff --git a/core/src/main/java/google/registry/model/transfer/BaseTransferObject.java b/core/src/main/java/google/registry/model/transfer/BaseTransferObject.java index 9c55b52a1a9..e1a0a04c1bf 100644 --- a/core/src/main/java/google/registry/model/transfer/BaseTransferObject.java +++ b/core/src/main/java/google/registry/model/transfer/BaseTransferObject.java @@ -14,6 +14,8 @@ package google.registry.model.transfer; +import static google.registry.util.DateTimeUtils.toInstant; + import google.registry.model.Buildable.GenericBuilder; import google.registry.model.ImmutableObject; import google.registry.model.UnsafeSerializable; @@ -23,6 +25,7 @@ import jakarta.persistence.MappedSuperclass; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlTransient; +import java.time.Instant; import org.joda.time.DateTime; /** Fields common to {@link DomainTransferData} and {@link TransferResponse}. */ @@ -73,10 +76,15 @@ public String getLosingRegistrarId() { return losingClientId; } - public DateTime getPendingTransferExpirationTime() { + @Deprecated + public DateTime getPendingTransferExpirationDateTime() { return pendingTransferExpirationTime; } + public Instant getPendingTransferExpirationTime() { + return toInstant(pendingTransferExpirationTime); + } + /** Base class for builders of {@link BaseTransferObject} subclasses. */ public abstract static class Builder> extends GenericBuilder { diff --git a/core/src/main/java/google/registry/model/transfer/DomainTransferData.java b/core/src/main/java/google/registry/model/transfer/DomainTransferData.java index 7ecb17e2698..a56c3889d54 100644 --- a/core/src/main/java/google/registry/model/transfer/DomainTransferData.java +++ b/core/src/main/java/google/registry/model/transfer/DomainTransferData.java @@ -18,6 +18,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList; import static google.registry.util.CollectionUtils.isNullOrEmpty; import static google.registry.util.CollectionUtils.nullToEmpty; +import static google.registry.util.DateTimeUtils.toInstant; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @@ -41,6 +42,7 @@ import jakarta.persistence.Convert; import jakarta.persistence.Embeddable; import jakarta.persistence.Embedded; +import java.time.Instant; import java.util.Set; import javax.annotation.Nullable; import org.joda.time.DateTime; @@ -168,10 +170,16 @@ public Trid getTransferRequestTrid() { } @Nullable - public DateTime getTransferredRegistrationExpirationTime() { + @Deprecated + public DateTime getTransferredRegistrationExpirationDateTime() { return transferredRegistrationExpirationTime; } + @Nullable + public Instant getTransferredRegistrationExpirationTime() { + return toInstant(transferredRegistrationExpirationTime); + } + @Nullable public VKey getServerApproveBillingEvent() { return serverApproveBillingEvent; diff --git a/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManagerImpl.java b/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManagerImpl.java index f1633b7c189..4502a7d39e7 100644 --- a/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManagerImpl.java +++ b/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManagerImpl.java @@ -21,6 +21,7 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet; import static google.registry.config.RegistryConfig.getHibernateAllowNestedTransactions; import static google.registry.persistence.transaction.DatabaseException.throwIfSqlException; +import static google.registry.util.DateTimeUtils.toDateTime; import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; import static java.util.AbstractMap.SimpleEntry; import static java.util.stream.Collectors.joining; @@ -61,6 +62,7 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.time.Instant; import java.util.Calendar; import java.util.Collections; import java.util.Date; @@ -340,6 +342,11 @@ public TransactionIsolationLevel getCurrentTransactionIsolationLevel() { @Override public DateTime getTransactionTime() { + return toDateTime(getTxTime()); + } + + @Override + public Instant getTxTime() { assertInTransaction(); TransactionInfo txnInfo = transactionInfo.get(); if (txnInfo.transactionTime == null) { @@ -746,7 +753,7 @@ private T detach(@Nullable T entity) { private static class TransactionInfo { EntityManager entityManager; boolean inTransaction = false; - DateTime transactionTime; + Instant transactionTime; Supplier idProvider; // The set of entity objects that have been either persisted (via insert()) or merged (via @@ -759,7 +766,7 @@ private static class TransactionInfo { private void start(Clock clock, Supplier idProvider) { checkArgumentNotNull(clock); inTransaction = true; - transactionTime = clock.nowUtc(); + transactionTime = clock.now(); this.idProvider = idProvider; } diff --git a/core/src/main/java/google/registry/persistence/transaction/TransactionManager.java b/core/src/main/java/google/registry/persistence/transaction/TransactionManager.java index 8cd63befe80..aa49bb9b784 100644 --- a/core/src/main/java/google/registry/persistence/transaction/TransactionManager.java +++ b/core/src/main/java/google/registry/persistence/transaction/TransactionManager.java @@ -20,6 +20,7 @@ import google.registry.model.ImmutableObject; import google.registry.persistence.PersistenceModule.TransactionIsolationLevel; import google.registry.persistence.VKey; +import java.time.Instant; import java.util.NoSuchElementException; import java.util.Optional; import java.util.concurrent.Callable; @@ -129,8 +130,12 @@ public interface TransactionManager { void reTransact(ThrowingRunnable work); /** Returns the time associated with the start of this particular transaction attempt. */ + @Deprecated DateTime getTransactionTime(); + /** Returns the Instant associated with the start of this particular transaction attempt. */ + Instant getTxTime(); + /** Persists a new entity in the database, throws exception if the entity already exists. */ void insert(Object entity); diff --git a/core/src/main/java/google/registry/rdap/RdapActionBase.java b/core/src/main/java/google/registry/rdap/RdapActionBase.java index 2b23be75044..94804bf0a17 100644 --- a/core/src/main/java/google/registry/rdap/RdapActionBase.java +++ b/core/src/main/java/google/registry/rdap/RdapActionBase.java @@ -17,6 +17,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.net.HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN; import static google.registry.request.Actions.getPathForAction; +import static google.registry.util.DateTimeUtils.toInstant; import static google.registry.util.DomainNameUtils.canonicalizeHostname; import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST; import static jakarta.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; @@ -241,7 +242,7 @@ DeletedItemHandling getDeletedItemHandling() { * is authorized to do so. */ boolean isAuthorized(EppResource eppResource) { - return getRequestTime().isBefore(eppResource.getDeletionTime()) + return toInstant(getRequestTime()).isBefore(eppResource.getDeletionTime()) || (shouldIncludeDeleted() && rdapAuthorization.isAuthorizedForRegistrar( eppResource.getPersistedCurrentSponsorRegistrarId())); diff --git a/core/src/main/java/google/registry/rdap/RdapJsonFormatter.java b/core/src/main/java/google/registry/rdap/RdapJsonFormatter.java index 131c22f2f54..30047c3bda6 100644 --- a/core/src/main/java/google/registry/rdap/RdapJsonFormatter.java +++ b/core/src/main/java/google/registry/rdap/RdapJsonFormatter.java @@ -317,7 +317,7 @@ RdapDomain createRdapDomain(Domain domain, OutputDataType outputDataType) { .build(), Event.builder() .setEventAction(EventAction.EXPIRATION) - .setEventDate(domain.getRegistrationExpirationTime()) + .setEventDate(domain.getRegistrationExpirationDateTime()) .build(), // RDAP response profile section 1.5: // The topmost object in the RDAP response MUST contain an event of "eventAction" type @@ -358,7 +358,7 @@ RdapDomain createRdapDomain(Domain domain, OutputDataType outputDataType) { makeStatusValueList( allStatusValues, false, // isRedacted - domain.getDeletionTime().isBefore(getRequestTime())); + domain.getDeletionDateTime().isBefore(getRequestTime())); builder.statusBuilder().addAll(status); if (status.isEmpty()) { logger.atWarning().log( @@ -438,7 +438,7 @@ && replicaTm() makeStatusValueList( statuses.build(), false, // isRedacted - host.getDeletionTime().isBefore(getRequestTime()))); + host.getDeletionDateTime().isBefore(getRequestTime()))); } // For query responses - we MUST have all the ip addresses: RDAP Response Profile 4.2. @@ -752,7 +752,9 @@ private ImmutableList makeOptionalEvents(EppResource resource) { ImmutableList.Builder eventsBuilder = new ImmutableList.Builder<>(); DateTime creationTime = resource.getCreationTime(); DateTime lastChangeTime = - resource.getLastEppUpdateTime() == null ? creationTime : resource.getLastEppUpdateTime(); + resource.getLastEppUpdateDateTime() == null + ? creationTime + : resource.getLastEppUpdateDateTime(); // The order of the elements is stable - it's the order in which the enum elements are defined // in EventAction for (EventAction rdapEventAction : EventAction.values()) { diff --git a/core/src/main/java/google/registry/rde/DomainToXjcConverter.java b/core/src/main/java/google/registry/rde/DomainToXjcConverter.java index fe671a28def..89f4be1a01d 100644 --- a/core/src/main/java/google/registry/rde/DomainToXjcConverter.java +++ b/core/src/main/java/google/registry/rde/DomainToXjcConverter.java @@ -94,13 +94,13 @@ static XjcRdeDomain convertDomain(Domain model, RdeMode mode) { // identifying the end (expiration) of the domain name object's // registration period. This element MUST be present if the domain // name has been allocated. - bean.setExDate(model.getRegistrationExpirationTime()); + bean.setExDate(model.getRegistrationExpirationDateTime()); // o An OPTIONAL element that contains the date and time of // the most recent domain-name-object modification. This element // MUST NOT be present if the domain name object has never been // modified. - bean.setUpDate(model.getLastEppUpdateTime()); + bean.setUpDate(model.getLastEppUpdateDateTime()); // o An OPTIONAL element that contains the identifier of the // registrar that last updated the domain name object. This element @@ -228,8 +228,8 @@ private static XjcRdeDomainTransferDataType convertTransferData(DomainTransferDa bean.setReRr(RdeUtils.makeXjcRdeRrType(model.getGainingRegistrarId())); bean.setAcRr(RdeUtils.makeXjcRdeRrType(model.getLosingRegistrarId())); bean.setReDate(model.getTransferRequestTime()); - bean.setAcDate(model.getPendingTransferExpirationTime()); - bean.setExDate(model.getTransferredRegistrationExpirationTime()); + bean.setAcDate(model.getPendingTransferExpirationDateTime()); + bean.setExDate(model.getTransferredRegistrationExpirationDateTime()); return bean; } diff --git a/core/src/main/java/google/registry/rde/HostToXjcConverter.java b/core/src/main/java/google/registry/rde/HostToXjcConverter.java index 3f75703ee55..f822871aa2c 100644 --- a/core/src/main/java/google/registry/rde/HostToXjcConverter.java +++ b/core/src/main/java/google/registry/rde/HostToXjcConverter.java @@ -70,7 +70,7 @@ private static XjcRdeHost convertHostCommon( bean.setName(model.getHostName()); bean.setRoid(model.getRepoId()); bean.setCrDate(model.getCreationTime()); - bean.setUpDate(model.getLastEppUpdateTime()); + bean.setUpDate(model.getLastEppUpdateDateTime()); bean.setCrRr(RdeAdapter.convertRr(model.getCreationRegistrarId(), null)); bean.setUpRr(RdeAdapter.convertRr(model.getLastEppUpdateRegistrarId(), null)); bean.setCrRr(RdeAdapter.convertRr(model.getCreationRegistrarId(), null)); diff --git a/core/src/main/java/google/registry/tools/GenerateDnsReportCommand.java b/core/src/main/java/google/registry/tools/GenerateDnsReportCommand.java index f4aad1d544a..88e0a367901 100644 --- a/core/src/main/java/google/registry/tools/GenerateDnsReportCommand.java +++ b/core/src/main/java/google/registry/tools/GenerateDnsReportCommand.java @@ -81,7 +81,7 @@ String generate() { .list()); for (Domain domain : domains) { // Skip deleted domains and domains that don't get published to DNS. - if (isBeforeOrAt(domain.getDeletionTime(), now) || !domain.shouldPublishToDns()) { + if (isBeforeOrAt(domain.getDeletionDateTime(), now) || !domain.shouldPublishToDns()) { continue; } write(domain); @@ -90,7 +90,7 @@ String generate() { Iterable nameservers = tm().transact(() -> tm().loadAllOf(Host.class)); for (Host nameserver : nameservers) { // Skip deleted hosts and external hosts. - if (isBeforeOrAt(nameserver.getDeletionTime(), now) + if (isBeforeOrAt(nameserver.getDeletionDateTime(), now) || nameserver.getInetAddresses().isEmpty()) { continue; } diff --git a/core/src/main/java/google/registry/tools/RenewDomainCommand.java b/core/src/main/java/google/registry/tools/RenewDomainCommand.java index 81f26263454..838db13ce5d 100644 --- a/core/src/main/java/google/registry/tools/RenewDomainCommand.java +++ b/core/src/main/java/google/registry/tools/RenewDomainCommand.java @@ -81,7 +81,7 @@ protected void initMutatingEppToolCommand() SoyMapData soyMapData = new SoyMapData( "domainName", domain.getDomainName(), - "expirationDate", domain.getRegistrationExpirationTime().toString(DATE_FORMATTER), + "expirationDate", domain.getRegistrationExpirationDateTime().toString(DATE_FORMATTER), "period", String.valueOf(period)); if (requestedByRegistrar != null) { diff --git a/core/src/main/java/google/registry/tools/UniformRapidSuspensionCommand.java b/core/src/main/java/google/registry/tools/UniformRapidSuspensionCommand.java index 1f3368ef2c4..77290a85292 100644 --- a/core/src/main/java/google/registry/tools/UniformRapidSuspensionCommand.java +++ b/core/src/main/java/google/registry/tools/UniformRapidSuspensionCommand.java @@ -163,7 +163,7 @@ protected void initMutatingEppToolCommand() domain.getDomainName(), "expirationDate", domain - .getRegistrationExpirationTime() + .getRegistrationExpirationDateTime() .toString(DateTimeFormat.forPattern("YYYY-MM-dd")), // period is the number of years to renew the registration for "period", diff --git a/core/src/main/java/google/registry/tools/UnrenewDomainCommand.java b/core/src/main/java/google/registry/tools/UnrenewDomainCommand.java index 29613a8ecaa..06866ebca3e 100644 --- a/core/src/main/java/google/registry/tools/UnrenewDomainCommand.java +++ b/core/src/main/java/google/registry/tools/UnrenewDomainCommand.java @@ -99,8 +99,9 @@ protected void init() throws UnsupportedEncodingException { domainsWithDisallowedStatusesBuilder.putAll( domainName, Sets.intersection(domain.get().getStatusValues(), DISALLOWED_STATUSES)); if (isBeforeOrAt( - leapSafeSubtractYears(domain.get().getRegistrationExpirationTime(), period), now)) { - domainsExpiringTooSoonBuilder.put(domainName, domain.get().getRegistrationExpirationTime()); + leapSafeSubtractYears(domain.get().getRegistrationExpirationDateTime(), period), now)) { + domainsExpiringTooSoonBuilder.put( + domainName, domain.get().getRegistrationExpirationDateTime()); } } @@ -143,7 +144,7 @@ protected String prompt() { DateTime now = clock.nowUtc(); for (String domainName : mainParameters) { Domain domain = ForeignKeyUtils.loadResource(Domain.class, domainName, now).get(); - DateTime previousTime = domain.getRegistrationExpirationTime(); + DateTime previousTime = domain.getRegistrationExpirationDateTime(); DateTime newTime = leapSafeSubtractYears(previousTime, period); resultBuilder.append( String.format( @@ -179,12 +180,12 @@ private void unrenewDomain(String domainName) { "Domain %s has prohibited status values", domainName); checkState( - leapSafeSubtractYears(domain.getRegistrationExpirationTime(), period).isAfter(now), + leapSafeSubtractYears(domain.getRegistrationExpirationDateTime(), period).isAfter(now), "Domain %s expires too soon", domainName); DateTime newExpirationTime = - leapSafeSubtractYears(domain.getRegistrationExpirationTime(), period); + leapSafeSubtractYears(domain.getRegistrationExpirationDateTime(), period); DomainHistory domainHistory = new DomainHistory.Builder() .setDomain(domain) diff --git a/core/src/main/java/google/registry/tools/UpdateRecurrenceCommand.java b/core/src/main/java/google/registry/tools/UpdateRecurrenceCommand.java index 87120148275..73a300ae885 100644 --- a/core/src/main/java/google/registry/tools/UpdateRecurrenceCommand.java +++ b/core/src/main/java/google/registry/tools/UpdateRecurrenceCommand.java @@ -16,7 +16,9 @@ import static com.google.common.base.Preconditions.checkArgument; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; +import static google.registry.util.DateTimeUtils.END_INSTANT; import static google.registry.util.DateTimeUtils.END_OF_TIME; +import static google.registry.util.DateTimeUtils.toDateTime; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; @@ -31,6 +33,7 @@ import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry.HistoryEntryId; import google.registry.model.transfer.TransferStatus; +import java.time.Instant; import java.util.List; import java.util.Optional; import javax.annotation.Nullable; @@ -161,7 +164,7 @@ private ImmutableList internalExecute() { private ImmutableMap loadDomainsAndRecurrences() { ImmutableMap.Builder result = new ImmutableMap.Builder<>(); - DateTime now = tm().getTransactionTime(); + Instant now = tm().getTxTime(); for (String domainName : mainParameters) { Domain domain = ForeignKeyUtils.loadResource(Domain.class, domainName, now) @@ -171,7 +174,7 @@ private ImmutableMap loadDomainsAndRecurrences() { String.format( "Domain %s does not exist or has been deleted", domainName))); checkArgument( - domain.getDeletionTime().equals(END_OF_TIME), + domain.getDeletionTime().equals(END_INSTANT), "Domain %s has already had a deletion time set", domainName); checkArgument( @@ -184,7 +187,7 @@ private ImmutableMap loadDomainsAndRecurrences() { domainAutorenewEndTime.ifPresent( endTime -> checkArgument( - endTime.isAfter(now), + endTime.isAfter(toDateTime(now)), "Domain %s autorenew ended prior to now at %s", domainName, endTime)); diff --git a/core/src/test/java/google/registry/batch/DeleteExpiredDomainsActionTest.java b/core/src/test/java/google/registry/batch/DeleteExpiredDomainsActionTest.java index 6fe8694964c..0315dedd67f 100644 --- a/core/src/test/java/google/registry/batch/DeleteExpiredDomainsActionTest.java +++ b/core/src/test/java/google/registry/batch/DeleteExpiredDomainsActionTest.java @@ -24,6 +24,7 @@ import static google.registry.testing.DatabaseHelper.persistActiveDomain; import static google.registry.testing.DatabaseHelper.persistResource; import static google.registry.util.DateTimeUtils.END_OF_TIME; +import static google.registry.util.DateTimeUtils.plusDays; import com.google.common.collect.ImmutableSet; import google.registry.flows.DaggerEppTestComponent; @@ -117,7 +118,7 @@ void test_deletesOnlyExpiredDomain() { assertThat(loadByEntity(notYetExpiredDomain)).isEqualTo(notYetExpiredDomain); Domain reloadedExpiredDomain = loadByEntity(pendingExpirationDomain); assertThat(reloadedExpiredDomain.getStatusValues()).contains(PENDING_DELETE); - assertThat(reloadedExpiredDomain.getDeletionTime()).isEqualTo(clock.nowUtc().plusDays(35)); + assertThat(reloadedExpiredDomain.getDeletionTime()).isEqualTo(plusDays(clock.now(), 35)); } @Test diff --git a/core/src/test/java/google/registry/batch/DeleteProberDataActionTest.java b/core/src/test/java/google/registry/batch/DeleteProberDataActionTest.java index 9c11baa01d4..96c2d0c98e0 100644 --- a/core/src/test/java/google/registry/batch/DeleteProberDataActionTest.java +++ b/core/src/test/java/google/registry/batch/DeleteProberDataActionTest.java @@ -26,7 +26,7 @@ import static google.registry.testing.DatabaseHelper.persistDeletedDomain; import static google.registry.testing.DatabaseHelper.persistDomainAsDeleted; import static google.registry.testing.DatabaseHelper.persistResource; -import static google.registry.util.DateTimeUtils.END_OF_TIME; +import static google.registry.util.DateTimeUtils.END_INSTANT; import static org.joda.time.DateTimeZone.UTC; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -47,6 +47,7 @@ import google.registry.testing.DatabaseHelper; import google.registry.testing.SystemPropertyExtension; import google.registry.util.RegistryEnvironment; +import java.time.Instant; import java.util.Optional; import java.util.Set; import org.joda.money.Money; @@ -201,7 +202,7 @@ void testSuccess_activeDomain_isSoftDeleted() throws Exception { .setCreationTimeForTest(DateTime.now(UTC).minusYears(1)) .build()); action.run(); - DateTime timeAfterDeletion = DateTime.now(UTC); + Instant timeAfterDeletion = Instant.now(); assertThat(ForeignKeyUtils.loadResource(Domain.class, "blah.ib-any.test", timeAfterDeletion)) .isEmpty(); assertThat(loadByEntity(domain).getDeletionTime()).isLessThan(timeAfterDeletion); @@ -217,7 +218,7 @@ void testSuccess_activeDomain_doubleMapSoftDeletes() throws Exception { .setCreationTimeForTest(DateTime.now(UTC).minusYears(1)) .build()); action.run(); - DateTime timeAfterDeletion = DateTime.now(UTC); + Instant timeAfterDeletion = Instant.now(); resetAction(); action.run(); assertThat(ForeignKeyUtils.loadResource(Domain.class, "blah.ib-any.test", timeAfterDeletion)) @@ -237,7 +238,7 @@ void test_recentlyCreatedDomain_isntDeletedYet() throws Exception { Optional domain = ForeignKeyUtils.loadResource(Domain.class, "blah.ib-any.test", DateTime.now(UTC)); assertThat(domain).isPresent(); - assertThat(domain.get().getDeletionTime()).isEqualTo(END_OF_TIME); + assertThat(domain.get().getDeletionTime()).isEqualTo(END_INSTANT); } @Test @@ -250,7 +251,7 @@ void testDryRun_doesntSoftDeleteData() throws Exception { .build()); action.isDryRun = true; action.run(); - assertThat(loadByEntity(domain).getDeletionTime()).isEqualTo(END_OF_TIME); + assertThat(loadByEntity(domain).getDeletionTime()).isEqualTo(END_INSTANT); } @Test diff --git a/core/src/test/java/google/registry/beam/resave/ResaveAllEppResourcesPipelineTest.java b/core/src/test/java/google/registry/beam/resave/ResaveAllEppResourcesPipelineTest.java index c87216be024..b9d8f59a547 100644 --- a/core/src/test/java/google/registry/beam/resave/ResaveAllEppResourcesPipelineTest.java +++ b/core/src/test/java/google/registry/beam/resave/ResaveAllEppResourcesPipelineTest.java @@ -25,6 +25,8 @@ import static google.registry.testing.DatabaseHelper.persistDomainWithDependentResources; import static google.registry.testing.DatabaseHelper.persistDomainWithPendingTransfer; import static google.registry.testing.DatabaseHelper.persistNewRegistrars; +import static google.registry.util.DateTimeUtils.plusYears; +import static google.registry.util.DateTimeUtils.toInstant; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -113,11 +115,12 @@ void testPipeline_autorenewedDomain() { DateTime now = fakeClock.nowUtc(); Domain domain = persistDomainWithDependentResources("domain", "tld", now, now, now.plusYears(1)); - assertThat(domain.getRegistrationExpirationTime()).isEqualTo(now.plusYears(1)); + assertThat(domain.getRegistrationExpirationTime()).isEqualTo(plusYears(toInstant(now), 1)); fakeClock.advanceBy(Duration.standardDays(500)); runPipeline(); Domain postPipeline = loadByEntity(domain); - assertThat(postPipeline.getRegistrationExpirationTime()).isEqualTo(now.plusYears(2)); + assertThat(postPipeline.getRegistrationExpirationTime()) + .isEqualTo(plusYears(toInstant(now), 2)); } @Test diff --git a/core/src/test/java/google/registry/flows/EppLifecycleDomainTest.java b/core/src/test/java/google/registry/flows/EppLifecycleDomainTest.java index fb45f347354..3d3540ab885 100644 --- a/core/src/test/java/google/registry/flows/EppLifecycleDomainTest.java +++ b/core/src/test/java/google/registry/flows/EppLifecycleDomainTest.java @@ -488,7 +488,7 @@ void testDomainDeletion_outsideAddGracePeriod_showsRedemptionPeriod() throws Exc // Make sure that in the future, the domain expiration is unchanged after deletion Domain clonedDomain = domain.cloneProjectedAtTime(deleteTime.plusYears(5)); - assertThat(clonedDomain.getRegistrationExpirationTime()).isEqualTo(createTime.plusYears(2)); + assertThat(clonedDomain.getRegistrationExpirationDateTime()).isEqualTo(createTime.plusYears(2)); } @Test diff --git a/core/src/test/java/google/registry/flows/FlowTestCase.java b/core/src/test/java/google/registry/flows/FlowTestCase.java index ba2b31f61af..4daf414b2d1 100644 --- a/core/src/test/java/google/registry/flows/FlowTestCase.java +++ b/core/src/test/java/google/registry/flows/FlowTestCase.java @@ -182,7 +182,7 @@ private static ImmutableMap canonicalizeGracePeriods( GracePeriod.create( entry.getKey().getType(), entry.getKey().getDomainRepoId(), - entry.getKey().getExpirationTime(), + entry.getKey().getExpirationDateTime(), entry.getKey().getRegistrarId(), null, 1L), diff --git a/core/src/test/java/google/registry/flows/domain/DomainCreateFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainCreateFlowTest.java index f1ae4f4855f..6c402def637 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainCreateFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainCreateFlowTest.java @@ -361,7 +361,7 @@ private void assertSuccessfulCreate( .setFlags(ImmutableSet.of(Flag.AUTO_RENEW)) .setTargetId(getUniqueIdFromCommand()) .setRegistrarId("TheRegistrar") - .setEventTime(domain.getRegistrationExpirationTime()) + .setEventTime(domain.getRegistrationExpirationDateTime()) .setRecurrenceEndTime(END_OF_TIME) .setDomainHistory(historyEntry) .setRenewalPriceBehavior(expectedRenewalPriceBehavior) @@ -397,7 +397,7 @@ private void assertSuccessfulCreate( new PollMessage.Autorenew.Builder() .setTargetId(domain.getDomainName()) .setRegistrarId("TheRegistrar") - .setEventTime(domain.getRegistrationExpirationTime()) + .setEventTime(domain.getRegistrationExpirationDateTime()) .setMsg("Domain was auto-renewed.") .setHistoryEntry(historyEntry) .build()); @@ -1720,7 +1720,7 @@ private void assertPollMessagesWithCollisionOneTime(Domain domain) { new PollMessage.Autorenew.Builder() .setTargetId(domain.getDomainName()) .setRegistrarId("TheRegistrar") - .setEventTime(domain.getRegistrationExpirationTime()) + .setEventTime(domain.getRegistrationExpirationDateTime()) .setMsg("Domain was auto-renewed.") .setHistoryEntry(historyEntry) .build(), @@ -1853,7 +1853,7 @@ void testSuccess_customLogicIsCalled_andSavesExtraEntity() throws Exception { new PollMessage.Autorenew.Builder() .setTargetId(domain.getDomainName()) .setRegistrarId("TheRegistrar") - .setEventTime(domain.getRegistrationExpirationTime()) + .setEventTime(domain.getRegistrationExpirationDateTime()) .setMsg("Domain was auto-renewed.") .setHistoryEntry(historyEntry) .build(), diff --git a/core/src/test/java/google/registry/flows/domain/DomainDeleteFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainDeleteFlowTest.java index 223293b1f5f..9b3b2060b58 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainDeleteFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainDeleteFlowTest.java @@ -376,7 +376,7 @@ void testSuccess_updatedEppUpdateTimeAfterPendingRedemption() throws Exception { runFlowAssertResponse(loadFile("domain_delete_response_pending.xml")); Domain domain = reloadResourceByForeignKey(); - DateTime redemptionEndTime = domain.getLastEppUpdateTime().plusDays(3); + DateTime redemptionEndTime = domain.getLastEppUpdateDateTime().plusDays(3); Domain domainAtRedemptionTime = domain.cloneProjectedAtTime(redemptionEndTime); assertAboutDomains() .that(domainAtRedemptionTime) @@ -418,7 +418,7 @@ private void doSuccessfulTest_noAddGracePeriod( null)); // We should see exactly one poll message, which is for the autorenew 1 month in the future. assertPollMessages(createAutorenewPollMessage("TheRegistrar").build()); - DateTime expectedExpirationTime = domain.getRegistrationExpirationTime().minusYears(2); + DateTime expectedExpirationTime = domain.getRegistrationExpirationDateTime().minusYears(2); clock.advanceOneMilli(); runFlowAssertResponse(loadFile(responseFilename, substitutions)); Domain resource = reloadResourceByForeignKey(); @@ -462,7 +462,7 @@ private void assertDeletionPollMessageFor(Domain domain, String expectedMessage) // There should be a future poll message at the deletion time. The previous autorenew poll // message should now be deleted. assertAboutDomains().that(domain).hasDeletePollMessage(); - DateTime deletionTime = domain.getDeletionTime(); + DateTime deletionTime = domain.getDeletionDateTime(); assertThat(getPollMessages("TheRegistrar", deletionTime.minusMinutes(1))).isEmpty(); assertThat(getPollMessages("TheRegistrar", deletionTime)).hasSize(1); assertThat(domain.getDeletePollMessage()) @@ -496,7 +496,7 @@ void testSuccess_autorenewPollMessageIsNotDeleted() throws Exception { runFlowAssertResponse(loadFile("domain_delete_response_pending.xml")); // There should now be two poll messages; one for the delete of the domain (in the future), and // another for the unacked autorenew messages. - DateTime deletionTime = reloadResourceByForeignKey().getDeletionTime(); + DateTime deletionTime = reloadResourceByForeignKey().getDeletionDateTime(); assertThat(getPollMessages("TheRegistrar", deletionTime.minusMinutes(1))).hasSize(1); assertThat(getPollMessages("TheRegistrar", deletionTime)).hasSize(2); } @@ -613,7 +613,7 @@ void testSuccess_pendingTransfer() throws Exception { .isEqualTo(Trid.create("transferClient-trid", "transferServer-trid")); assertThat(panData.getActionResult()).isFalse(); // There should be a future poll message to the losing registrar at the deletion time. - DateTime deletionTime = domain.getDeletionTime(); + DateTime deletionTime = domain.getDeletionDateTime(); assertThat(getPollMessages("TheRegistrar", deletionTime.minusMinutes(1))).isEmpty(); assertThat(getPollMessages("TheRegistrar", deletionTime)).hasSize(1); assertOnlyBillingEventIsClosedAutorenew("TheRegistrar"); diff --git a/core/src/test/java/google/registry/flows/domain/DomainRenewFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainRenewFlowTest.java index 6694e221d74..cdbb9e478fd 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainRenewFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainRenewFlowTest.java @@ -247,7 +247,7 @@ private void doSuccessfulTest( @Nullable Money renewalPrice) throws Exception { assertMutatingFlow(true); - DateTime currentExpiration = reloadResourceByForeignKey().getRegistrationExpirationTime(); + DateTime currentExpiration = reloadResourceByForeignKey().getRegistrationExpirationDateTime(); DateTime newExpiration = currentExpiration.plusYears(renewalYears); runFlowAssertResponse( CommitMode.LIVE, userPrivileges, loadFile(responseFilename, substitutions)); @@ -303,7 +303,7 @@ private void doSuccessfulTest( .setFlags(ImmutableSet.of(Flag.AUTO_RENEW)) .setTargetId(getUniqueIdFromCommand()) .setRegistrarId("TheRegistrar") - .setEventTime(domain.getRegistrationExpirationTime()) + .setEventTime(domain.getRegistrationExpirationDateTime()) .setRecurrenceEndTime(END_OF_TIME) .setDomainHistory(historyEntryDomainRenew) .build()); @@ -313,7 +313,7 @@ private void doSuccessfulTest( new PollMessage.Autorenew.Builder() .setTargetId(getUniqueIdFromCommand()) .setRegistrarId("TheRegistrar") - .setEventTime(domain.getRegistrationExpirationTime()) + .setEventTime(domain.getRegistrationExpirationDateTime()) .setAutorenewEndTime(END_OF_TIME) .setMsg("Domain was auto-renewed.") .setHistoryEntry(historyEntryDomainRenew) @@ -817,7 +817,7 @@ void testSuccess_autorenewPollMessageIsNotDeleted() throws Exception { new PollMessage.Autorenew.Builder() .setTargetId(getUniqueIdFromCommand()) .setRegistrarId("TheRegistrar") - .setEventTime(reloadResourceByForeignKey().getRegistrationExpirationTime()) + .setEventTime(reloadResourceByForeignKey().getRegistrationExpirationDateTime()) .setAutorenewEndTime(END_OF_TIME) .setMsg("Domain was auto-renewed.") .setHistoryEntry(historyEntryDomainRenew) diff --git a/core/src/test/java/google/registry/flows/domain/DomainRestoreRequestFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainRestoreRequestFlowTest.java index 69c28ee896a..35eeb139945 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainRestoreRequestFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainRestoreRequestFlowTest.java @@ -201,7 +201,7 @@ void testSuccess_expiryStillInFuture_notExtended() throws Exception { new PollMessage.Autorenew.Builder() .setTargetId("example.tld") .setRegistrarId("TheRegistrar") - .setEventTime(domain.getRegistrationExpirationTime()) + .setEventTime(domain.getRegistrationExpirationDateTime()) .setAutorenewEndTime(END_OF_TIME) .setMsg("Domain was auto-renewed.") .setHistoryEntry(historyEntryDomainRestore) @@ -269,7 +269,7 @@ void testSuccess_expiryInPast_extendedByOneYear() throws Exception { new PollMessage.Autorenew.Builder() .setTargetId("example.tld") .setRegistrarId("TheRegistrar") - .setEventTime(domain.getRegistrationExpirationTime()) + .setEventTime(domain.getRegistrationExpirationDateTime()) .setAutorenewEndTime(END_OF_TIME) .setMsg("Domain was auto-renewed.") .setHistoryEntry(historyEntryDomainRestore) diff --git a/core/src/test/java/google/registry/flows/domain/DomainTransferApproveFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainTransferApproveFlowTest.java index aafa9387236..30a9339e3a2 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainTransferApproveFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainTransferApproveFlowTest.java @@ -148,7 +148,8 @@ private void assertTransferApproved(Domain domain, DomainTransferData oldTransfe .copyConstantFieldsToBuilder() .setTransferStatus(TransferStatus.CLIENT_APPROVED) .setPendingTransferExpirationTime(clock.nowUtc()) - .setTransferredRegistrationExpirationTime(domain.getRegistrationExpirationTime()) + .setTransferredRegistrationExpirationTime( + domain.getRegistrationExpirationDateTime()) .build()); } @@ -216,7 +217,7 @@ private void runSuccessfulFlowWithAssertions( // should be one at the current time to the gaining registrar, as well as one at the domain's // autorenew time. assertThat(getPollMessages(domain, "NewRegistrar", clock.nowUtc().plusMonths(1))).hasSize(1); - assertThat(getPollMessages(domain, "NewRegistrar", domain.getRegistrationExpirationTime())) + assertThat(getPollMessages(domain, "NewRegistrar", domain.getRegistrationExpirationDateTime())) .hasSize(2); PollMessage gainingTransferPollMessage = @@ -225,11 +226,11 @@ private void runSuccessfulFlowWithAssertions( getOnlyPollMessage( domain, "NewRegistrar", - domain.getRegistrationExpirationTime(), + domain.getRegistrationExpirationDateTime(), PollMessage.Autorenew.class); assertThat(gainingTransferPollMessage.getEventTime()).isEqualTo(clock.nowUtc()); assertThat(gainingAutorenewPollMessage.getEventTime()) - .isEqualTo(domain.getRegistrationExpirationTime()); + .isEqualTo(domain.getRegistrationExpirationDateTime()); DomainTransferResponse transferResponse = gainingTransferPollMessage .getResponseData() @@ -239,7 +240,7 @@ private void runSuccessfulFlowWithAssertions( .collect(onlyElement()); assertThat(transferResponse.getTransferStatus()).isEqualTo(TransferStatus.CLIENT_APPROVED); assertThat(transferResponse.getExtendedRegistrationExpirationTime()) - .isEqualTo(domain.getRegistrationExpirationTime()); + .isEqualTo(domain.getRegistrationExpirationDateTime()); PendingActionNotificationResponse panData = gainingTransferPollMessage .getResponseData() @@ -294,9 +295,9 @@ private void assertHistoryEntriesContainBillingEventsAndGracePeriods( .build(), getGainingClientAutorenewEvent() .asBuilder() - .setEventTime(domain.getRegistrationExpirationTime()) + .setEventTime(domain.getRegistrationExpirationDateTime()) .setRecurrenceLastExpansion( - domain.getRegistrationExpirationTime().minusYears(1)) + domain.getRegistrationExpirationDateTime().minusYears(1)) .setDomainHistory(historyEntryTransferApproved) .build())) .toArray(BillingBase[]::new)); @@ -332,9 +333,9 @@ private void assertHistoryEntriesDoNotContainTransferBillingEventsOrGracePeriods .build(), getGainingClientAutorenewEvent() .asBuilder() - .setEventTime(domain.getRegistrationExpirationTime()) + .setEventTime(domain.getRegistrationExpirationDateTime()) .setRecurrenceLastExpansion( - domain.getRegistrationExpirationTime().minusYears(1)) + domain.getRegistrationExpirationDateTime().minusYears(1)) .setDomainHistory(historyEntryTransferApproved) .build())) .toArray(BillingBase[]::new)); @@ -349,7 +350,7 @@ private void doSuccessfulTest(String tld, String commandFilename, String expecte tld, commandFilename, expectedXmlFilename, - domain.getRegistrationExpirationTime().plusYears(1), + domain.getRegistrationExpirationDateTime().plusYears(1), 1); } @@ -821,7 +822,7 @@ void testSuccess_superuserExtension_transferPeriodZero() throws Exception { "tld", "domain_transfer_approve.xml", "domain_transfer_approve_response_zero_period.xml", - domain.getRegistrationExpirationTime()); + domain.getRegistrationExpirationDateTime()); assertHistoryEntriesDoNotContainTransferBillingEventsOrGracePeriods(); } @@ -854,7 +855,7 @@ void testSuccess_superuserExtension_transferPeriodZero_autorenewGraceActive() th "tld", "domain_transfer_approve.xml", "domain_transfer_approve_response_zero_period_autorenew_grace.xml", - domain.getRegistrationExpirationTime()); + domain.getRegistrationExpirationDateTime()); assertHistoryEntriesDoNotContainTransferBillingEventsOrGracePeriods(); } diff --git a/core/src/test/java/google/registry/flows/domain/DomainTransferCancelFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainTransferCancelFlowTest.java index c7b62116bb8..ea5ff9e16c3 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainTransferCancelFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainTransferCancelFlowTest.java @@ -114,7 +114,7 @@ private void doSuccessfulTest(String commandFilename) throws Exception { // Setup done; run the test. assertMutatingFlow(true); - DateTime originalExpirationTime = domain.getRegistrationExpirationTime(); + DateTime originalExpirationTime = domain.getRegistrationExpirationDateTime(); ImmutableSet originalGracePeriods = domain.getGracePeriods(); DomainTransferData originalTransferData = domain.getTransferData(); runFlowAssertResponse(loadFile("domain_transfer_cancel_response.xml")); diff --git a/core/src/test/java/google/registry/flows/domain/DomainTransferQueryFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainTransferQueryFlowTest.java index 523120f4ade..b96a4f41223 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainTransferQueryFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainTransferQueryFlowTest.java @@ -143,7 +143,8 @@ void testSuccess_tenYears() throws Exception { persistResource( domain .asBuilder() - .setRegistrationExpirationTime(domain.getRegistrationExpirationTime().plusYears(9)) + .setRegistrationExpirationTime( + domain.getRegistrationExpirationDateTime().plusYears(9)) .build()); doSuccessfulTest("domain_transfer_query.xml", "domain_transfer_query_response_10_years.xml", 1); } @@ -233,7 +234,7 @@ void testSuccess_serverApproved_afterAutorenews() throws Exception { // Set the clock to just past the extended registration time. We'd expect the domain to have // auto-renewed once, but the transfer query response should be the same. clock.setTo(EXTENDED_REGISTRATION_EXPIRATION_TIME.plusMillis(1)); - assertThat(domain.cloneProjectedAtTime(clock.nowUtc()).getRegistrationExpirationTime()) + assertThat(domain.cloneProjectedAtTime(clock.nowUtc()).getRegistrationExpirationDateTime()) .isEqualTo(EXTENDED_REGISTRATION_EXPIRATION_TIME.plusYears(1)); doSuccessfulTest( "domain_transfer_query.xml", "domain_transfer_query_response_server_approved.xml", 2); diff --git a/core/src/test/java/google/registry/flows/domain/DomainTransferRejectFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainTransferRejectFlowTest.java index f26b460d497..e3bac621677 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainTransferRejectFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainTransferRejectFlowTest.java @@ -88,7 +88,7 @@ private void doSuccessfulTest(String commandFilename, String expectedXmlFilename assertThat(getPollMessages("TheRegistrar", clock.nowUtc().plusMonths(1))).hasSize(1); // Setup done; run the test. assertMutatingFlow(true); - DateTime originalExpirationTime = domain.getRegistrationExpirationTime(); + DateTime originalExpirationTime = domain.getRegistrationExpirationDateTime(); ImmutableSet originalGracePeriods = domain.getGracePeriods(); DomainTransferData originalTransferData = domain.getTransferData(); runFlowAssertResponse(loadFile(expectedXmlFilename)); diff --git a/core/src/test/java/google/registry/flows/domain/DomainTransferRequestFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainTransferRequestFlowTest.java index 70ad4517e37..7afa97cd697 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainTransferRequestFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainTransferRequestFlowTest.java @@ -244,7 +244,8 @@ private void assertTransferApproved( .setTransferPeriod(expectedPeriod) .setTransferStatus(TransferStatus.SERVER_APPROVED) .setPendingTransferExpirationTime(automaticTransferTime) - .setTransferredRegistrationExpirationTime(domain.getRegistrationExpirationTime()) + .setTransferredRegistrationExpirationTime( + domain.getRegistrationExpirationDateTime()) // Server-approve entity fields should all be nulled out. .build()); } @@ -542,7 +543,7 @@ private void doSuccessfulTest( doSuccessfulTest( commandFilename, expectedXmlFilename, - domain.getRegistrationExpirationTime().plusYears(1), + domain.getRegistrationExpirationDateTime().plusYears(1), substitutions, Optional.empty()); } @@ -551,7 +552,9 @@ private void doSuccessfulTest(String commandFilename, String expectedXmlFilename throws Exception { clock.advanceOneMilli(); doSuccessfulTest( - commandFilename, expectedXmlFilename, domain.getRegistrationExpirationTime().plusYears(1)); + commandFilename, + expectedXmlFilename, + domain.getRegistrationExpirationDateTime().plusYears(1)); } private void doSuccessfulSuperuserExtensionTest( @@ -812,7 +815,7 @@ void testSuccess_superuserExtension_zeroPeriod_nonZeroAutomaticTransferLength() doSuccessfulSuperuserExtensionTest( "domain_transfer_request_superuser_extension.xml", "domain_transfer_request_response_su_ext_zero_period_nonzero_transfer_length.xml", - domain.getRegistrationExpirationTime().plusYears(0), + domain.getRegistrationExpirationDateTime().plusYears(0), ImmutableMap.of("PERIOD", "0", "AUTOMATIC_TRANSFER_LENGTH", "5"), Optional.empty(), Period.create(0, Unit.YEARS), @@ -826,7 +829,7 @@ void testSuccess_superuserExtension_zeroPeriod_zeroAutomaticTransferLength() thr doSuccessfulSuperuserExtensionTest( "domain_transfer_request_superuser_extension.xml", "domain_transfer_request_response_su_ext_zero_period_zero_transfer_length.xml", - domain.getRegistrationExpirationTime().plusYears(0), + domain.getRegistrationExpirationDateTime().plusYears(0), ImmutableMap.of("PERIOD", "0", "AUTOMATIC_TRANSFER_LENGTH", "0"), Optional.empty(), Period.create(0, Unit.YEARS), @@ -841,7 +844,7 @@ void testSuccess_superuserExtension_nonZeroPeriod_nonZeroAutomaticTransferLength doSuccessfulSuperuserExtensionTest( "domain_transfer_request_superuser_extension.xml", "domain_transfer_request_response_su_ext_one_year_period_nonzero_transfer_length.xml", - domain.getRegistrationExpirationTime().plusYears(1), + domain.getRegistrationExpirationDateTime().plusYears(1), ImmutableMap.of("PERIOD", "1", "AUTOMATIC_TRANSFER_LENGTH", "5"), Optional.empty(), Period.create(1, Unit.YEARS), @@ -873,7 +876,7 @@ void testSuccess_superuserExtension_zeroPeriod_autorenewGraceActive() throws Exc doSuccessfulSuperuserExtensionTest( "domain_transfer_request_superuser_extension.xml", "domain_transfer_request_response_su_ext_zero_period_autorenew_grace.xml", - domain.getRegistrationExpirationTime(), + domain.getRegistrationExpirationDateTime(), ImmutableMap.of("PERIOD", "0", "AUTOMATIC_TRANSFER_LENGTH", "0"), Optional.empty(), Period.create(0, Unit.YEARS), @@ -922,7 +925,7 @@ void testSuccess_superuserExtension_clientTransferProhibited() throws Exception doSuccessfulSuperuserExtensionTest( "domain_transfer_request_superuser_extension.xml", "domain_transfer_request_response_su_ext_zero_period_zero_transfer_length.xml", - domain.getRegistrationExpirationTime().plusYears(0), + domain.getRegistrationExpirationDateTime().plusYears(0), ImmutableMap.of("PERIOD", "0", "AUTOMATIC_TRANSFER_LENGTH", "0"), Optional.empty(), Period.create(0, Unit.YEARS), @@ -939,7 +942,7 @@ void testSuccess_superuserExtension_serverTransferProhibited() throws Exception doSuccessfulSuperuserExtensionTest( "domain_transfer_request_superuser_extension.xml", "domain_transfer_request_response_su_ext_zero_period_zero_transfer_length.xml", - domain.getRegistrationExpirationTime().plusYears(0), + domain.getRegistrationExpirationDateTime().plusYears(0), ImmutableMap.of("PERIOD", "0", "AUTOMATIC_TRANSFER_LENGTH", "0"), Optional.empty(), Period.create(0, Unit.YEARS), @@ -977,7 +980,7 @@ void testSuccess_customLogicFee_std_v1() throws Exception { doSuccessfulTest( "domain_transfer_request_separate_fees.xml", "domain_transfer_request_response_fees.xml", - domain.getRegistrationExpirationTime().plusYears(1), + domain.getRegistrationExpirationDateTime().plusYears(1), new ImmutableMap.Builder() .put("DOMAIN", "expensive-domain.foo") .put("YEARS", "1") @@ -1409,7 +1412,7 @@ void testSuccess_bulkPricingName_zeroPeriod() throws Exception { doSuccessfulSuperuserExtensionTest( "domain_transfer_request_superuser_extension.xml", "domain_transfer_request_response_su_ext_zero_period_zero_transfer_length.xml", - domain.getRegistrationExpirationTime().plusYears(0), + domain.getRegistrationExpirationDateTime().plusYears(0), ImmutableMap.of("PERIOD", "0", "AUTOMATIC_TRANSFER_LENGTH", "0"), Optional.empty(), Period.create(0, Unit.YEARS), @@ -2024,7 +2027,7 @@ void testSuccess_customLogicFee_v06() throws Exception { doSuccessfulTest( "domain_transfer_request_separate_fees.xml", "domain_transfer_request_response_fees.xml", - domain.getRegistrationExpirationTime().plusYears(1), + domain.getRegistrationExpirationDateTime().plusYears(1), new ImmutableMap.Builder() .put("DOMAIN", "expensive-domain.foo") .put("YEARS", "1") diff --git a/core/src/test/java/google/registry/flows/host/HostUpdateFlowTest.java b/core/src/test/java/google/registry/flows/host/HostUpdateFlowTest.java index 084cd41924b..2103b06d441 100644 --- a/core/src/test/java/google/registry/flows/host/HostUpdateFlowTest.java +++ b/core/src/test/java/google/registry/flows/host/HostUpdateFlowTest.java @@ -256,7 +256,7 @@ void testSuccess_nameUnchanged_superordinateDomainWasTransferred() throws Except .and() .hasPersistedCurrentSponsorRegistrarId("NewRegistrar") .and() - .hasLastTransferTime(domain.getTransferData().getPendingTransferExpirationTime()) + .hasLastTransferTime(domain.getTransferData().getPendingTransferExpirationDateTime()) .and() .hasOnlyOneHistoryEntryWhich() .hasType(HistoryEntry.Type.HOST_UPDATE); diff --git a/core/src/test/java/google/registry/model/domain/DomainTest.java b/core/src/test/java/google/registry/model/domain/DomainTest.java index e6e81d9a1df..ad04eb1d088 100644 --- a/core/src/test/java/google/registry/model/domain/DomainTest.java +++ b/core/src/test/java/google/registry/model/domain/DomainTest.java @@ -31,6 +31,10 @@ import static google.registry.testing.SqlHelper.saveRegistrar; import static google.registry.util.DateTimeUtils.END_OF_TIME; import static google.registry.util.DateTimeUtils.START_OF_TIME; +import static google.registry.util.DateTimeUtils.minusDays; +import static google.registry.util.DateTimeUtils.plusDays; +import static google.registry.util.DateTimeUtils.plusYears; +import static google.registry.util.DateTimeUtils.toDateTime; import static org.joda.money.CurrencyUnit.USD; import static org.joda.time.DateTimeZone.UTC; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -67,6 +71,8 @@ import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationWithCoverageExtension; import google.registry.testing.DatabaseHelper; import google.registry.testing.FakeClock; +import java.time.Instant; +import java.time.ZoneOffset; import java.util.Optional; import org.joda.money.Money; import org.joda.time.DateTime; @@ -386,7 +392,7 @@ private void assertTransferred( .isEqualTo(TransferStatus.SERVER_APPROVED); assertThat(domain.getCurrentSponsorRegistrarId()).isEqualTo("TheRegistrar"); assertThat(domain.getLastTransferTime()).isEqualTo(fakeClock.nowUtc().plusDays(1)); - assertThat(domain.getRegistrationExpirationTime()).isEqualTo(newExpirationTime); + assertThat(domain.getRegistrationExpirationDateTime()).isEqualTo(newExpirationTime); assertThat(domain.getAutorenewBillingEvent()).isEqualTo(newAutorenewEvent); } @@ -479,56 +485,57 @@ void testExpiredTransfer_autoRenewBeforeTransfer() { } private void setupPendingTransferDomain( - DateTime oldExpirationTime, DateTime transferRequestTime, DateTime transferSuccessTime) { + Instant oldExpirationTime, Instant transferRequestTime, Instant transferSuccessTime) { domain = domain .asBuilder() - .setRegistrationExpirationTime(oldExpirationTime) + .setRegistrationExpirationTime(toDateTime(oldExpirationTime)) .setTransferData( domain .getTransferData() .asBuilder() .setTransferStatus(TransferStatus.PENDING) - .setTransferRequestTime(transferRequestTime) - .setPendingTransferExpirationTime(transferSuccessTime) + .setTransferRequestTime(toDateTime(transferRequestTime)) + .setPendingTransferExpirationTime(toDateTime(transferSuccessTime)) .build()) - .setLastEppUpdateTime(transferRequestTime) + .setLastEppUpdateTime(toDateTime(transferRequestTime)) .setLastEppUpdateRegistrarId(domain.getTransferData().getGainingRegistrarId()) .build(); } @Test void testEppLastUpdateTimeAndClientId_autoRenewBeforeTransferSuccess() { - DateTime now = fakeClock.nowUtc(); - DateTime transferRequestDateTime = now.plusDays(1); - DateTime autorenewDateTime = now.plusDays(3); - DateTime transferSuccessDateTime = now.plusDays(5); + Instant now = fakeClock.now(); + Instant transferRequestDateTime = plusDays(now, 1); + Instant autorenewDateTime = plusDays(now, 3); + Instant transferSuccessDateTime = plusDays(now, 5); setupPendingTransferDomain(autorenewDateTime, transferRequestDateTime, transferSuccessDateTime); - Domain beforeAutoRenew = domain.cloneProjectedAtTime(autorenewDateTime.minusDays(1)); + Domain beforeAutoRenew = domain.cloneProjectedAtInstant(minusDays(autorenewDateTime, 1)); assertThat(beforeAutoRenew.getLastEppUpdateTime()).isEqualTo(transferRequestDateTime); assertThat(beforeAutoRenew.getLastEppUpdateRegistrarId()).isEqualTo("TheRegistrar"); // If autorenew happens before transfer succeeds(before transfer grace period starts as well), // lastEppUpdateRegistrarId should still be the current sponsor client id - Domain afterAutoRenew = domain.cloneProjectedAtTime(autorenewDateTime.plusDays(1)); + Domain afterAutoRenew = domain.cloneProjectedAtInstant(plusDays(autorenewDateTime, 1)); assertThat(afterAutoRenew.getLastEppUpdateTime()).isEqualTo(autorenewDateTime); assertThat(afterAutoRenew.getLastEppUpdateRegistrarId()).isEqualTo("NewRegistrar"); } @Test void testEppLastUpdateTimeAndClientId_autoRenewAfterTransferSuccess() { - DateTime now = fakeClock.nowUtc(); - DateTime transferRequestDateTime = now.plusDays(1); - DateTime autorenewDateTime = now.plusDays(3); - DateTime transferSuccessDateTime = now.plusDays(5); + Instant now = fakeClock.now(); + Instant transferRequestDateTime = plusDays(now, 1); + Instant autorenewDateTime = plusDays(now, 3); + Instant transferSuccessDateTime = plusDays(now, 5); setupPendingTransferDomain(autorenewDateTime, transferRequestDateTime, transferSuccessDateTime); - Domain beforeAutoRenew = domain.cloneProjectedAtTime(autorenewDateTime.minusDays(1)); + Domain beforeAutoRenew = domain.cloneProjectedAtInstant(minusDays(autorenewDateTime, 1)); assertThat(beforeAutoRenew.getLastEppUpdateTime()).isEqualTo(transferRequestDateTime); assertThat(beforeAutoRenew.getLastEppUpdateRegistrarId()).isEqualTo("TheRegistrar"); - Domain afterTransferSuccess = domain.cloneProjectedAtTime(transferSuccessDateTime.plusDays(1)); + Domain afterTransferSuccess = + domain.cloneProjectedAtInstant(plusDays(transferSuccessDateTime, 1)); assertThat(afterTransferSuccess.getLastEppUpdateTime()).isEqualTo(transferSuccessDateTime); assertThat(afterTransferSuccess.getLastEppUpdateRegistrarId()).isEqualTo("TheRegistrar"); } @@ -552,11 +559,11 @@ void testEppLastUpdateTimeAndClientId_isSetCorrectlyWithNullPreviousValue() { setupUnmodifiedDomain(autorenewDateTime); Domain beforeAutoRenew = domain.cloneProjectedAtTime(autorenewDateTime.minusDays(1)); - assertThat(beforeAutoRenew.getLastEppUpdateTime()).isEqualTo(null); + assertThat(beforeAutoRenew.getLastEppUpdateDateTime()).isEqualTo(null); assertThat(beforeAutoRenew.getLastEppUpdateRegistrarId()).isEqualTo(null); Domain afterAutoRenew = domain.cloneProjectedAtTime(autorenewDateTime.plusDays(1)); - assertThat(afterAutoRenew.getLastEppUpdateTime()).isEqualTo(autorenewDateTime); + assertThat(afterAutoRenew.getLastEppUpdateDateTime()).isEqualTo(autorenewDateTime); assertThat(afterAutoRenew.getLastEppUpdateRegistrarId()).isEqualTo("NewRegistrar"); } @@ -633,9 +640,9 @@ void testGracePeriodsByType() { @Test void testRenewalsHappenAtExpiration() { - Domain renewed = domain.cloneProjectedAtTime(domain.getRegistrationExpirationTime()); + Domain renewed = domain.cloneProjectedAtInstant(domain.getRegistrationExpirationTime()); assertThat(renewed.getRegistrationExpirationTime()) - .isEqualTo(domain.getRegistrationExpirationTime().plusYears(1)); + .isEqualTo(plusYears(domain.getRegistrationExpirationTime(), 1)); assertThat(renewed.getLastEppUpdateTime()).isEqualTo(domain.getRegistrationExpirationTime()); assertThat(getOnlyElement(renewed.getGracePeriods()).getType()) .isEqualTo(GracePeriodStatus.AUTO_RENEW); @@ -656,15 +663,16 @@ void testRenewalsDontHappenOnFebruary29() { .setRegistrationExpirationTime(DateTime.parse("2004-02-29T22:00:00.0Z")) .build(); Domain renewed = - domain.cloneProjectedAtTime(domain.getRegistrationExpirationTime().plusYears(4)); - assertThat(renewed.getRegistrationExpirationTime().getDayOfMonth()).isEqualTo(28); + domain.cloneProjectedAtInstant(plusYears(domain.getRegistrationExpirationTime(), 4)); + assertThat(renewed.getRegistrationExpirationTime().atZone(ZoneOffset.UTC).getDayOfMonth()) + .isEqualTo(28); } @Test void testMultipleAutoRenews() { // Change the registry so that renewal costs change every year to make sure we are using the // autorenew time as the lookup time for the cost. - DateTime oldExpirationTime = domain.getRegistrationExpirationTime(); + DateTime oldExpirationTime = domain.getRegistrationExpirationDateTime(); persistResource( Tld.get("com") .asBuilder() @@ -680,9 +688,10 @@ void testMultipleAutoRenews() { .build()) .build()); Domain renewedThreeTimes = domain.cloneProjectedAtTime(oldExpirationTime.plusYears(2)); - assertThat(renewedThreeTimes.getRegistrationExpirationTime()) + assertThat(renewedThreeTimes.getRegistrationExpirationDateTime()) .isEqualTo(oldExpirationTime.plusYears(3)); - assertThat(renewedThreeTimes.getLastEppUpdateTime()).isEqualTo(oldExpirationTime.plusYears(2)); + assertThat(renewedThreeTimes.getLastEppUpdateDateTime()) + .isEqualTo(oldExpirationTime.plusYears(2)); assertThat(renewedThreeTimes.getGracePeriods()) .containsExactly( GracePeriod.createForRecurrence( @@ -730,7 +739,7 @@ void testClone_doNotExtendExpirationOnDeletedDomain() { .setDeletionTime(now.minusDays(10)) .setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE, StatusValue.INACTIVE)) .build()); - assertThat(domain.cloneProjectedAtTime(now).getRegistrationExpirationTime()) + assertThat(domain.cloneProjectedAtTime(now).getRegistrationExpirationDateTime()) .isEqualTo(now.minusDays(1)); } @@ -746,7 +755,7 @@ void testClone_doNotExtendExpirationOnFutureDeletedDomain() { .setDeletionTime(now.plusDays(20)) .setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE, StatusValue.INACTIVE)) .build()); - assertThat(domain.cloneProjectedAtTime(now).getRegistrationExpirationTime()) + assertThat(domain.cloneProjectedAtTime(now).getRegistrationExpirationDateTime()) .isEqualTo(now.plusDays(1)); } @@ -773,7 +782,7 @@ void testClone_extendsExpirationForExpiredTransferredDomain() { .setTransferData(transferData) .build()); - assertThat(domain.cloneProjectedAtTime(now).getRegistrationExpirationTime()) + assertThat(domain.cloneProjectedAtTime(now).getRegistrationExpirationDateTime()) .isEqualTo(newExpiration); } @@ -801,7 +810,7 @@ void testClone_extendsExpirationForNonExpiredTransferredDomain() { .setTransferData(transferData) .build()); - assertThat(domain.cloneProjectedAtTime(now).getRegistrationExpirationTime()) + assertThat(domain.cloneProjectedAtTime(now).getRegistrationExpirationDateTime()) .isEqualTo(newExpiration); } @@ -843,7 +852,7 @@ void testClone_removesBulkTokenFromTransferredDomain() { assertThat(domain.getCurrentBulkToken()).isPresent(); Domain clonedDomain = domain.cloneProjectedAtTime(now); - assertThat(clonedDomain.getRegistrationExpirationTime()).isEqualTo(newExpiration); + assertThat(clonedDomain.getRegistrationExpirationDateTime()).isEqualTo(newExpiration); assertThat(clonedDomain.getCurrentBulkToken()).isEmpty(); } @@ -868,7 +877,7 @@ void testClone_doesNotExtendExpirationForPendingTransfer() { .setTransferData(transferData) .build()); - assertThat(domain.cloneProjectedAtTime(now).getRegistrationExpirationTime()) + assertThat(domain.cloneProjectedAtTime(now).getRegistrationExpirationDateTime()) .isEqualTo(previousExpiration); } @@ -906,7 +915,7 @@ void testClone_doesNotRemoveBulkTokenForPendingTransfer() { .build()); Domain clonedDomain = domain.cloneProjectedAtTime(now); - assertThat(clonedDomain.getRegistrationExpirationTime()).isEqualTo(previousExpiration); + assertThat(clonedDomain.getRegistrationExpirationDateTime()).isEqualTo(previousExpiration); assertThat(clonedDomain.getCurrentBulkToken().get()).isEqualTo(allocationToken.createVKey()); } @@ -943,8 +952,8 @@ void testClone_transferDuringAutorenew() { .setAutorenewBillingEvent(recurrenceBillKey) .build()); Domain clone = domain.cloneProjectedAtTime(now); - assertThat(clone.getRegistrationExpirationTime()) - .isEqualTo(domain.getRegistrationExpirationTime().plusYears(1)); + assertThat(clone.getRegistrationExpirationDateTime()) + .isEqualTo(domain.getRegistrationExpirationDateTime().plusYears(1)); // Transferring removes the AUTORENEW grace period and adds a TRANSFER grace period assertThat(getOnlyElement(clone.getGracePeriods()).getType()) .isEqualTo(GracePeriodStatus.TRANSFER); diff --git a/core/src/test/java/google/registry/model/domain/GracePeriodTest.java b/core/src/test/java/google/registry/model/domain/GracePeriodTest.java index bc79c043adc..8453d28ddc2 100644 --- a/core/src/test/java/google/registry/model/domain/GracePeriodTest.java +++ b/core/src/test/java/google/registry/model/domain/GracePeriodTest.java @@ -68,7 +68,7 @@ void testSuccess_forBillingEvent() { assertThat(gracePeriod.getBillingEvent()).isEqualTo(onetime.createVKey()); assertThat(gracePeriod.getBillingRecurrence()).isNull(); assertThat(gracePeriod.getRegistrarId()).isEqualTo("TheRegistrar"); - assertThat(gracePeriod.getExpirationTime()).isEqualTo(now.plusDays(1)); + assertThat(gracePeriod.getExpirationDateTime()).isEqualTo(now.plusDays(1)); assertThat(gracePeriod.hasBillingEvent()).isTrue(); } @@ -82,7 +82,7 @@ void testSuccess_forRecurrence() { assertThat(gracePeriod.getBillingEvent()).isNull(); assertThat(gracePeriod.getBillingRecurrence()).isEqualTo(recurrenceKey); assertThat(gracePeriod.getRegistrarId()).isEqualTo("TheRegistrar"); - assertThat(gracePeriod.getExpirationTime()).isEqualTo(now.plusDays(1)); + assertThat(gracePeriod.getExpirationDateTime()).isEqualTo(now.plusDays(1)); assertThat(gracePeriod.hasBillingEvent()).isTrue(); } @@ -96,7 +96,7 @@ void testSuccess_createWithoutBillingEvent() { assertThat(gracePeriod.getBillingEvent()).isNull(); assertThat(gracePeriod.getBillingRecurrence()).isNull(); assertThat(gracePeriod.getRegistrarId()).isEqualTo("TheRegistrar"); - assertThat(gracePeriod.getExpirationTime()).isEqualTo(now); + assertThat(gracePeriod.getExpirationDateTime()).isEqualTo(now); assertThat(gracePeriod.hasBillingEvent()).isFalse(); } diff --git a/core/src/test/java/google/registry/testing/AbstractEppResourceSubject.java b/core/src/test/java/google/registry/testing/AbstractEppResourceSubject.java index baebec91be9..a8a039c4df3 100644 --- a/core/src/test/java/google/registry/testing/AbstractEppResourceSubject.java +++ b/core/src/test/java/google/registry/testing/AbstractEppResourceSubject.java @@ -148,15 +148,15 @@ public And hasExactlyStatusValues(StatusValue... statusValues) { } public And hasDeletionTime(DateTime deletionTime) { - return hasValue(deletionTime, actual.getDeletionTime(), "getDeletionTime()"); + return hasValue(deletionTime, actual.getDeletionDateTime(), "getDeletionTime()"); } public And hasLastEppUpdateTime(DateTime lastUpdateTime) { - return hasValue(lastUpdateTime, actual.getLastEppUpdateTime(), "has lastEppUpdateTime"); + return hasValue(lastUpdateTime, actual.getLastEppUpdateDateTime(), "has lastEppUpdateTime"); } public And hasLastEppUpdateTimeAtLeast(DateTime before) { - DateTime lastEppUpdateTime = actual.getLastEppUpdateTime(); + DateTime lastEppUpdateTime = actual.getLastEppUpdateDateTime(); check("getLastEppUpdateTime()").that(lastEppUpdateTime).isAtLeast(before); return andChainer(); } diff --git a/core/src/test/java/google/registry/testing/DomainSubject.java b/core/src/test/java/google/registry/testing/DomainSubject.java index 0036528baee..507a548c9c1 100644 --- a/core/src/test/java/google/registry/testing/DomainSubject.java +++ b/core/src/test/java/google/registry/testing/DomainSubject.java @@ -78,7 +78,7 @@ public And hasCurrentSponsorRegistrarId(String registrarId) { public And hasRegistrationExpirationTime(DateTime expiration) { return hasValue( - expiration, actual.getRegistrationExpirationTime(), "getRegistrationExpirationTime()"); + expiration, actual.getRegistrationExpirationDateTime(), "getRegistrationExpirationTime()"); } public And hasLastTransferTime(DateTime lastTransferTime) { diff --git a/core/src/test/java/google/registry/tools/UnrenewDomainCommandTest.java b/core/src/test/java/google/registry/tools/UnrenewDomainCommandTest.java index 9f4d161c882..378231a46af 100644 --- a/core/src/test/java/google/registry/tools/UnrenewDomainCommandTest.java +++ b/core/src/test/java/google/registry/tools/UnrenewDomainCommandTest.java @@ -78,12 +78,12 @@ void test_unrenewTwoDomains_worksSuccessfully() throws Exception { assertThat( ForeignKeyUtils.loadResource(Domain.class, "foo.tld", fakeClock.nowUtc()) .get() - .getRegistrationExpirationTime()) + .getRegistrationExpirationDateTime()) .isEqualTo(DateTime.parse("2019-12-06T13:55:01.001Z")); assertThat( ForeignKeyUtils.loadResource(Domain.class, "bar.tld", fakeClock.nowUtc()) .get() - .getRegistrationExpirationTime()) + .getRegistrationExpirationDateTime()) .isEqualTo(DateTime.parse("2018-12-06T13:55:01.002Z")); assertInStdout("Successfully unrenewed all domains."); } @@ -149,8 +149,8 @@ void test_unrenewDomain_savesDependentEntitiesCorrectly() throws Exception { .build())); // Check that fields on domain were updated correctly. - assertThat(domain.getRegistrationExpirationTime()).isEqualTo(newExpirationTime); - assertThat(domain.getLastEppUpdateTime()).isEqualTo(unrenewTime); + assertThat(domain.getRegistrationExpirationDateTime()).isEqualTo(newExpirationTime); + assertThat(domain.getLastEppUpdateDateTime()).isEqualTo(unrenewTime); assertThat(domain.getLastEppUpdateRegistrarId()).isEqualTo("TheRegistrar"); } diff --git a/core/src/test/java/google/registry/ui/server/console/domains/ConsoleBulkDomainActionTest.java b/core/src/test/java/google/registry/ui/server/console/domains/ConsoleBulkDomainActionTest.java index 7f5338f5e45..da0162a688e 100644 --- a/core/src/test/java/google/registry/ui/server/console/domains/ConsoleBulkDomainActionTest.java +++ b/core/src/test/java/google/registry/ui/server/console/domains/ConsoleBulkDomainActionTest.java @@ -19,6 +19,7 @@ import static google.registry.testing.DatabaseHelper.loadSingleton; import static google.registry.testing.DatabaseHelper.persistDomainWithDependentResources; import static google.registry.testing.DatabaseHelper.persistResource; +import static google.registry.util.DateTimeUtils.plusDays; import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST; import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN; import static jakarta.servlet.http.HttpServletResponse.SC_OK; @@ -92,7 +93,7 @@ void testSuccess_delete() { {"example.tld":{"message":"Command completed successfully; action pending",\ "responseCode":1001}}\ """); - assertThat(loadByEntity(domain).getDeletionTime()).isEqualTo(clock.nowUtc().plusDays(35)); + assertThat(loadByEntity(domain).getDeletionTime()).isEqualTo(plusDays(clock.now(), 35)); ConsoleUpdateHistory history = loadSingleton(ConsoleUpdateHistory.class).get(); assertThat(history.getType()).isEqualTo(ConsoleUpdateHistory.Type.DOMAIN_DELETE); assertThat(history.getDescription()).hasValue("example.tld"); @@ -162,7 +163,7 @@ void testHalfSuccess_halfNonexistent() throws Exception { "nonexistent.tld":{"message":"The domain with given ID (nonexistent.tld) doesn\\u0027t exist.",\ "responseCode":2303}}\ """); - assertThat(loadByEntity(domain).getDeletionTime()).isEqualTo(clock.nowUtc().plusDays(35)); + assertThat(loadByEntity(domain).getDeletionTime()).isEqualTo(plusDays(clock.now(), 35)); ConsoleUpdateHistory history = loadSingleton(ConsoleUpdateHistory.class).get(); assertThat(history.getType()).isEqualTo(ConsoleUpdateHistory.Type.DOMAIN_DELETE); assertThat(history.getDescription()).hasValue("example.tld"); diff --git a/util/src/test/java/google/registry/util/DateTimeUtilsTest.java b/util/src/test/java/google/registry/util/DateTimeUtilsTest.java index 4dc92aa1f14..eaacd8308af 100644 --- a/util/src/test/java/google/registry/util/DateTimeUtilsTest.java +++ b/util/src/test/java/google/registry/util/DateTimeUtilsTest.java @@ -15,7 +15,9 @@ package google.registry.util; import static com.google.common.truth.Truth.assertThat; +import static google.registry.util.DateTimeUtils.END_INSTANT; import static google.registry.util.DateTimeUtils.END_OF_TIME; +import static google.registry.util.DateTimeUtils.START_INSTANT; import static google.registry.util.DateTimeUtils.START_OF_TIME; import static google.registry.util.DateTimeUtils.earliestOf; import static google.registry.util.DateTimeUtils.isAtOrAfter; @@ -23,12 +25,15 @@ import static google.registry.util.DateTimeUtils.latestOf; import static google.registry.util.DateTimeUtils.leapSafeAddYears; import static google.registry.util.DateTimeUtils.leapSafeSubtractYears; +import static google.registry.util.DateTimeUtils.toDateTime; +import static google.registry.util.DateTimeUtils.toInstant; import static google.registry.util.DateTimeUtils.toLocalDate; import static google.registry.util.DateTimeUtils.toSqlDate; import static org.junit.jupiter.api.Assertions.assertThrows; import com.google.common.collect.ImmutableList; import java.sql.Date; +import java.time.Instant; import org.joda.time.DateTime; import org.joda.time.LocalDate; import org.junit.jupiter.api.Test; @@ -42,7 +47,7 @@ class DateTimeUtilsTest { @Test void testSuccess_earliestOf() { assertThat(earliestOf(START_OF_TIME, END_OF_TIME)).isEqualTo(START_OF_TIME); - assertThat(earliestOf(sampleDates)).isEqualTo(START_OF_TIME); + assertThat(DateTimeUtils.earliestDateTimeOf(sampleDates)).isEqualTo(START_OF_TIME); } @Test @@ -72,6 +77,12 @@ void testSuccess_leapSafeAddYears() { assertThat(leapSafeAddYears(startDate, 4)).isEqualTo(DateTime.parse("2016-02-28T00:00:00Z")); } + @Test + void test_leapSafeAddYears_worksWithInstants() { + Instant startDate = Instant.parse("2012-02-29T00:00:00Z"); + assertThat(leapSafeAddYears(startDate, 4)).isEqualTo(Instant.parse("2016-02-28T00:00:00Z")); + } + @Test void testSuccess_leapSafeSubtractYears() { DateTime startDate = DateTime.parse("2012-02-29T00:00:00Z"); @@ -80,6 +91,13 @@ void testSuccess_leapSafeSubtractYears() { .isEqualTo(DateTime.parse("2008-02-28T00:00:00Z")); } + @Test + void test_leapSafeSubtractYears_worksWithInstants() { + Instant startDate = Instant.parse("2012-02-29T00:00:00Z"); + assertThat(leapSafeSubtractYears(startDate, 4)) + .isEqualTo(Instant.parse("2008-02-28T00:00:00Z")); + } + @Test void testSuccess_leapSafeSubtractYears_zeroYears() { DateTime leapDay = DateTime.parse("2012-02-29T00:00:00Z"); @@ -93,7 +111,7 @@ void testFailure_earliestOfEmpty() { @Test void testFailure_latestOfEmpty() { - assertThrows(IllegalArgumentException.class, () -> earliestOf(ImmutableList.of())); + assertThrows(IllegalArgumentException.class, () -> latestOf(ImmutableList.of())); } @Test @@ -107,4 +125,32 @@ void testSuccess_toLocalDate() { Date date = Date.valueOf("2020-02-29"); assertThat(toLocalDate(date)).isEqualTo(LocalDate.parse("2020-02-29")); } + + @Test + void test_startOfTimeConstants_areTheSame() { + assertThat(toInstant(START_OF_TIME)).isEqualTo(START_INSTANT); + assertThat(toDateTime(START_INSTANT)).isEqualTo(START_OF_TIME); + assertThat(toInstant(toDateTime(START_INSTANT))).isEqualTo(START_INSTANT); + assertThat(toDateTime(toInstant(START_OF_TIME))).isEqualTo(START_OF_TIME); + } + + @Test + void test_endOfTimeConstants_areTheSame() { + assertThat(toInstant(END_OF_TIME)).isEqualTo(END_INSTANT); + assertThat(toDateTime(END_INSTANT)).isEqualTo(END_OF_TIME); + assertThat(toInstant(toDateTime(END_INSTANT))).isEqualTo(END_INSTANT); + assertThat(toDateTime(toInstant(END_OF_TIME))).isEqualTo(END_OF_TIME); + } + + @Test + void test_instantConversionMethods_workCorrectly() { + assertThat(toInstant(DateTime.parse("2024-03-27T10:15:30.105Z"))) + .isEqualTo(Instant.parse("2024-03-27T10:15:30.105Z")); + assertThat(toDateTime(Instant.parse("2024-03-27T10:15:30.105Z"))) + .isEqualTo(DateTime.parse("2024-03-27T10:15:30.105Z")); + assertThat(toInstant(toDateTime(Instant.parse("2024-03-27T10:15:30.105Z")))) + .isEqualTo(Instant.parse("2024-03-27T10:15:30.105Z")); + assertThat(toDateTime(toInstant(DateTime.parse("2024-03-27T10:15:30.105Z")))) + .isEqualTo(DateTime.parse("2024-03-27T10:15:30.105Z")); + } } From 2286a46773890572bffd7d058003c55d983d996b Mon Sep 17 00:00:00 2001 From: Ben McIlwain Date: Tue, 31 Mar 2026 14:32:24 -0400 Subject: [PATCH 2/2] Set clock precision to milliseconds for Datetime->Instant migration Our existing precision is milliseconds so we want to stick with that for Instants. If we want to increase the precision globally after that we can do so all in one go post-migration, but for now, it would be a bad thing to have mixed precision going on just depending on whether a class happens to be migrated yet or not. This PR also migrates all existing DateTime.nowUtc() calls to use the Clock interface, so that when they are migrated they will get the benefit of this precision-setting as well. BUG= http://b/496985355 --- .../google/registry/util/SystemClock.java | 6 +++- .../batch/DeleteProberDataAction.java | 12 +++++--- ...ingCertificateNotificationEmailAction.java | 9 ++++-- .../registry/bigquery/BigqueryConnection.java | 24 ++++++++++----- .../model/tld/label/PremiumListDao.java | 6 +++- .../model/tld/label/PremiumListUtils.java | 6 ++-- .../model/tld/label/ReservedList.java | 7 ++--- .../tools/CreateAnchorTenantCommand.java | 7 +++-- .../registry/tools/CreateDomainCommand.java | 7 +++-- .../tools/CreateOrUpdateRegistrarCommand.java | 6 ++-- .../tools/CreateReservedListCommand.java | 7 +++-- .../tools/GenerateZoneFilesCommand.java | 10 +++++-- .../tools/GetHistoryEntriesCommand.java | 7 +++-- .../tools/UpdatePremiumListCommand.java | 7 ++++- .../console/ConsoleDumDownloadAction.java | 4 +-- .../batch/DeleteProberDataActionTest.java | 12 ++++++-- ...ertificateNotificationEmailActionTest.java | 3 +- .../model/tld/label/PremiumListUtilsTest.java | 12 ++++++-- .../icann/IcannReportingStagerTest.java | 6 +++- .../registry/tools/CommandTestCase.java | 12 ++++++++ .../tools/CreatePremiumListCommandTest.java | 6 ---- .../tools/CreateReservedListCommandTest.java | 2 -- .../tools/UpdatePremiumListCommandTest.java | 12 -------- .../tools/UpdateReservedListCommandTest.java | 2 -- .../module/CertificateSupplierModule.java | 5 ++-- .../handler/SslClientInitializerTest.java | 15 +++++----- .../handler/SslServerInitializerTest.java | 30 ++++++++++--------- .../module/CertificateSupplierModuleTest.java | 9 +++++- .../registry/proxy/EppProtocolModuleTest.java | 3 +- .../proxy/handler/EppServiceHandlerTest.java | 5 ++-- .../google/registry/util/PosixTarHeader.java | 5 +++- .../util/SelfSignedCaCertificate.java | 14 ++++----- .../registry/util/PosixTarHeaderTest.java | 8 ++++- 33 files changed, 178 insertions(+), 108 deletions(-) diff --git a/common/src/main/java/google/registry/util/SystemClock.java b/common/src/main/java/google/registry/util/SystemClock.java index 52ac9893f67..7e4e7551c1c 100644 --- a/common/src/main/java/google/registry/util/SystemClock.java +++ b/common/src/main/java/google/registry/util/SystemClock.java @@ -38,6 +38,10 @@ public DateTime nowUtc() { @Override public Instant now() { - return Instant.now(); + // Truncate to milliseconds to match the precision of Joda DateTime and our database schema + // (which uses millisecond precision via DateTimeConverter). This prevents subtle comparison + // bugs where a high-precision Instant would be considered "after" a truncated database + // timestamp. + return Instant.now().truncatedTo(java.time.temporal.ChronoUnit.MILLIS); } } diff --git a/core/src/main/java/google/registry/batch/DeleteProberDataAction.java b/core/src/main/java/google/registry/batch/DeleteProberDataAction.java index c34718b4875..f442f308a42 100644 --- a/core/src/main/java/google/registry/batch/DeleteProberDataAction.java +++ b/core/src/main/java/google/registry/batch/DeleteProberDataAction.java @@ -28,7 +28,6 @@ import static google.registry.request.RequestParameters.PARAM_DRY_RUN; import static google.registry.request.RequestParameters.PARAM_TLDS; import static google.registry.util.RegistryEnvironment.PRODUCTION; -import static org.joda.time.DateTimeZone.UTC; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; @@ -44,6 +43,7 @@ import google.registry.request.Action; import google.registry.request.Parameter; import google.registry.request.auth.Auth; +import google.registry.util.Clock; import google.registry.util.RegistryEnvironment; import jakarta.inject.Inject; import jakarta.persistence.TypedQuery; @@ -111,16 +111,20 @@ public class DeleteProberDataAction implements Runnable { String registryAdminRegistrarId; + private final Clock clock; + @Inject DeleteProberDataAction( @Parameter(PARAM_DRY_RUN) boolean isDryRun, @Parameter(PARAM_TLDS) ImmutableSet tlds, @Parameter(PARAM_BATCH_SIZE) Optional batchSize, - @Config("registryAdminClientId") String registryAdminRegistrarId) { + @Config("registryAdminClientId") String registryAdminRegistrarId, + Clock clock) { this.isDryRun = isDryRun; this.tlds = tlds; this.batchSize = batchSize.orElse(DEFAULT_BATCH_SIZE); this.registryAdminRegistrarId = registryAdminRegistrarId; + this.clock = clock; } @Override @@ -145,7 +149,7 @@ public void run() { AtomicInteger softDeletedDomains = new AtomicInteger(); AtomicInteger hardDeletedDomains = new AtomicInteger(); AtomicReference> domainsBatch = new AtomicReference<>(); - DateTime startTime = DateTime.now(UTC); + DateTime startTime = clock.nowUtc(); do { tm().transact( TRANSACTION_REPEATABLE_READ, @@ -164,7 +168,7 @@ public void run() { hardDeletedDomains.get(), batchSize); // Automatically kill the job if it is running for over 20 hours - } while (DateTime.now(UTC).isBefore(startTime.plusHours(20)) + } while (clock.nowUtc().isBefore(startTime.plusHours(20)) && domainsBatch.get().size() == batchSize); logger.atInfo().log( "%s %d domains.", diff --git a/core/src/main/java/google/registry/batch/SendExpiringCertificateNotificationEmailAction.java b/core/src/main/java/google/registry/batch/SendExpiringCertificateNotificationEmailAction.java index 4ee917951ad..02cd0dc6fb8 100644 --- a/core/src/main/java/google/registry/batch/SendExpiringCertificateNotificationEmailAction.java +++ b/core/src/main/java/google/registry/batch/SendExpiringCertificateNotificationEmailAction.java @@ -19,7 +19,6 @@ import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; import static org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR; import static org.apache.http.HttpStatus.SC_OK; -import static org.joda.time.DateTimeZone.UTC; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; @@ -37,6 +36,7 @@ import google.registry.request.Action; import google.registry.request.Response; import google.registry.request.auth.Auth; +import google.registry.util.Clock; import google.registry.util.EmailMessage; import jakarta.inject.Inject; import jakarta.mail.internet.AddressException; @@ -73,6 +73,7 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable private final GmailClient gmailClient; private final String expirationWarningEmailSubjectText; private final Response response; + private final Clock clock; @Inject public SendExpiringCertificateNotificationEmailAction( @@ -80,12 +81,14 @@ public SendExpiringCertificateNotificationEmailAction( @Config("expirationWarningEmailSubjectText") String expirationWarningEmailSubjectText, GmailClient gmailClient, CertificateChecker certificateChecker, - Response response) { + Response response, + Clock clock) { this.certificateChecker = certificateChecker; this.expirationWarningEmailSubjectText = expirationWarningEmailSubjectText; this.gmailClient = gmailClient; this.expirationWarningEmailBodyText = expirationWarningEmailBodyText; this.response = response; + this.clock = clock; } @Override @@ -186,7 +189,7 @@ boolean sendNotificationEmail( */ updateLastNotificationSentDate( registrar, - DateTime.now(UTC).minusMinutes((int) UPDATE_TIME_OFFSET.getStandardMinutes()), + clock.nowUtc().minusMinutes((int) UPDATE_TIME_OFFSET.getStandardMinutes()), certificateType); return true; } catch (Exception e) { diff --git a/core/src/main/java/google/registry/bigquery/BigqueryConnection.java b/core/src/main/java/google/registry/bigquery/BigqueryConnection.java index 93bf6fae9d2..d001645f8a3 100644 --- a/core/src/main/java/google/registry/bigquery/BigqueryConnection.java +++ b/core/src/main/java/google/registry/bigquery/BigqueryConnection.java @@ -24,7 +24,6 @@ import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static google.registry.bigquery.BigqueryUtils.toJobReferenceString; import static google.registry.config.RegistryConfig.getProjectId; -import static org.joda.time.DateTimeZone.UTC; import com.google.api.client.googleapis.json.GoogleJsonResponseException; import com.google.api.client.http.AbstractInputStreamContent; @@ -58,6 +57,7 @@ import google.registry.bigquery.BigqueryUtils.DestinationFormat; import google.registry.bigquery.BigqueryUtils.TableType; import google.registry.bigquery.BigqueryUtils.WriteDisposition; +import google.registry.util.Clock; import google.registry.util.NonFinalForTesting; import google.registry.util.Sleeper; import google.registry.util.SqlTemplate; @@ -69,7 +69,6 @@ import java.util.Random; import java.util.concurrent.ExecutorService; import javax.annotation.Nullable; -import org.joda.time.DateTime; import org.joda.time.Duration; /** Class encapsulating parameters and state for accessing the Bigquery API. */ @@ -94,6 +93,9 @@ public class BigqueryConnection implements AutoCloseable { /** Bigquery client instance wrapped by this class. */ private final Bigquery bigquery; + /** Clock instance for this connection. */ + private final Clock clock; + /** Executor service for bigquery jobs. */ private ListeningExecutorService service; @@ -109,8 +111,9 @@ public class BigqueryConnection implements AutoCloseable { /** Duration to wait between polls for job status. */ private Duration pollInterval = Duration.millis(1000); - BigqueryConnection(Bigquery bigquery) { + BigqueryConnection(Bigquery bigquery, Clock clock) { this.bigquery = bigquery; + this.clock = clock; } /** Builder for a {@link BigqueryConnection}, since the latter is immutable once created. */ @@ -118,8 +121,8 @@ public static class Builder { private BigqueryConnection instance; @Inject - Builder(Bigquery bigquery) { - instance = new BigqueryConnection(bigquery); + Builder(Bigquery bigquery, Clock clock) { + instance = new BigqueryConnection(bigquery, clock); } /** @@ -195,6 +198,11 @@ public static final class Builder { private final TableReference tableRef = new TableReference(); private TableType type = TableType.TABLE; private WriteDisposition writeDisposition = WriteDisposition.WRITE_EMPTY; + private final Clock clock; + + public Builder(Clock clock) { + this.clock = clock; + } public Builder datasetId(String datasetId) { tableRef.setDatasetId(datasetId); @@ -217,7 +225,7 @@ public Builder type(TableType type) { } public Builder timeToLive(Duration duration) { - this.table.setExpirationTime(DateTime.now(UTC).plus(duration).getMillis()); + this.table.setExpirationTime(clock.nowUtc().plus(duration).getMillis()); return this; } @@ -302,7 +310,7 @@ public void close() { /** Returns a partially built DestinationTable with the default dataset and overwrite behavior. */ public DestinationTable.Builder buildDestinationTable(String tableName) { - return new DestinationTable.Builder() + return new DestinationTable.Builder(clock) .datasetId(datasetId) .type(TableType.TABLE) .name(tableName) @@ -314,7 +322,7 @@ public DestinationTable.Builder buildDestinationTable(String tableName) { * temporary table dataset, with the default TTL and overwrite behavior. */ public DestinationTable.Builder buildTemporaryTable() { - return new DestinationTable.Builder() + return new DestinationTable.Builder(clock) .datasetId(TEMP_DATASET_NAME) .type(TableType.TABLE) .name(getRandomTableName()) diff --git a/core/src/main/java/google/registry/model/tld/label/PremiumListDao.java b/core/src/main/java/google/registry/model/tld/label/PremiumListDao.java index 531eb833650..77e7a2bfb75 100644 --- a/core/src/main/java/google/registry/model/tld/label/PremiumListDao.java +++ b/core/src/main/java/google/registry/model/tld/label/PremiumListDao.java @@ -129,7 +129,11 @@ public static Optional getPremiumPrice(String premiumListName, String lab public static PremiumList save(String name, CurrencyUnit currencyUnit, List inputData) { checkArgument(!inputData.isEmpty(), "New premium list data cannot be empty"); - return save(PremiumListUtils.parseToPremiumList(name, currencyUnit, inputData)); + return tm().transact( + () -> + save( + PremiumListUtils.parseToPremiumList( + name, currencyUnit, inputData, tm().getTransactionTime()))); } /** Saves the given premium list (and its premium list entries) to Cloud SQL. */ diff --git a/core/src/main/java/google/registry/model/tld/label/PremiumListUtils.java b/core/src/main/java/google/registry/model/tld/label/PremiumListUtils.java index d25f9d61dc3..463be206d5f 100644 --- a/core/src/main/java/google/registry/model/tld/label/PremiumListUtils.java +++ b/core/src/main/java/google/registry/model/tld/label/PremiumListUtils.java @@ -14,8 +14,6 @@ package google.registry.model.tld.label; -import static org.joda.time.DateTimeZone.UTC; - import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import google.registry.model.tld.label.PremiumList.PremiumEntry; @@ -29,12 +27,12 @@ public class PremiumListUtils { public static PremiumList parseToPremiumList( - String name, CurrencyUnit currencyUnit, List inputData) { + String name, CurrencyUnit currencyUnit, List inputData, DateTime creationTime) { PremiumList partialPremiumList = new PremiumList.Builder() .setName(name) .setCurrency(currencyUnit) - .setCreationTimestamp(DateTime.now(UTC)) + .setCreationTimestamp(creationTime) .build(); ImmutableMap prices = partialPremiumList.parse(inputData); Map priceAmounts = Maps.transformValues(prices, PremiumEntry::getValue); diff --git a/core/src/main/java/google/registry/model/tld/label/ReservedList.java b/core/src/main/java/google/registry/model/tld/label/ReservedList.java index 4020254303e..2f2a0caa750 100644 --- a/core/src/main/java/google/registry/model/tld/label/ReservedList.java +++ b/core/src/main/java/google/registry/model/tld/label/ReservedList.java @@ -24,10 +24,10 @@ import static google.registry.persistence.transaction.QueryComposer.Comparator.EQ; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.util.CollectionUtils.nullToEmpty; -import static org.joda.time.DateTimeZone.UTC; import com.github.benmanes.caffeine.cache.LoadingCache; import com.google.common.base.Splitter; +import com.google.common.base.Stopwatch; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.UncheckedExecutionException; @@ -47,7 +47,6 @@ import java.util.Map; import java.util.Optional; import javax.annotation.Nullable; -import org.joda.time.DateTime; /** * A list of reserved domain labels that are blocked from being registered for various reasons. @@ -234,7 +233,7 @@ public static ImmutableSet getReservationTypes(String label, St */ private static ImmutableSet getReservedListEntries( String label, String tldStr) { - DateTime startTime = DateTime.now(UTC); + Stopwatch stopwatch = Stopwatch.createStarted(); Tld tld = Tld.get(checkNotNull(tldStr, "tld must not be null")); ImmutableSet.Builder entriesBuilder = new ImmutableSet.Builder<>(); ImmutableSet.Builder metricMatchesBuilder = @@ -253,7 +252,7 @@ private static ImmutableSet getReservedListEntries( DomainLabelMetrics.recordReservedListCheckOutcome( tldStr, metricMatchesBuilder.build(), - DateTime.now(UTC).getMillis() - startTime.getMillis()); + stopwatch.elapsed().toMillis()); return entries; } diff --git a/core/src/main/java/google/registry/tools/CreateAnchorTenantCommand.java b/core/src/main/java/google/registry/tools/CreateAnchorTenantCommand.java index 4e392cb162c..c48da2a8c05 100644 --- a/core/src/main/java/google/registry/tools/CreateAnchorTenantCommand.java +++ b/core/src/main/java/google/registry/tools/CreateAnchorTenantCommand.java @@ -19,18 +19,17 @@ import static google.registry.model.tld.Tlds.findTldForNameOrThrow; import static google.registry.pricing.PricingEngineProxy.getDomainCreateCost; import static google.registry.util.StringGenerator.DEFAULT_PASSWORD_LENGTH; -import static org.joda.time.DateTimeZone.UTC; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; import com.google.common.net.InternetDomainName; import com.google.template.soy.data.SoyMapData; import google.registry.tools.soy.CreateAnchorTenantSoyInfo; +import google.registry.util.Clock; import google.registry.util.StringGenerator; import jakarta.inject.Inject; import jakarta.inject.Named; import org.joda.money.Money; -import org.joda.time.DateTime; /** A command to create a new anchor tenant domain. */ @Parameters(separators = " =", commandDescription = "Provision a domain for an anchor tenant.") @@ -76,6 +75,8 @@ final class CreateAnchorTenantCommand extends MutatingEppToolCommand { @Named("base64StringGenerator") StringGenerator passwordGenerator; + @Inject Clock clock; + @Override protected void initMutatingEppToolCommand() { checkArgument(superuser, "This command must be run as a superuser."); @@ -86,7 +87,7 @@ protected void initMutatingEppToolCommand() { Money cost = null; if (fee) { - cost = getDomainCreateCost(domainName, DateTime.now(UTC), DEFAULT_ANCHOR_TENANT_PERIOD_YEARS); + cost = getDomainCreateCost(domainName, clock.nowUtc(), DEFAULT_ANCHOR_TENANT_PERIOD_YEARS); } setSoyTemplate(CreateAnchorTenantSoyInfo.getInstance(), diff --git a/core/src/main/java/google/registry/tools/CreateDomainCommand.java b/core/src/main/java/google/registry/tools/CreateDomainCommand.java index 101e5017727..a9f32807d21 100644 --- a/core/src/main/java/google/registry/tools/CreateDomainCommand.java +++ b/core/src/main/java/google/registry/tools/CreateDomainCommand.java @@ -17,18 +17,17 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Strings.isNullOrEmpty; import static google.registry.pricing.PricingEngineProxy.getPricesForDomainName; -import static org.joda.time.DateTimeZone.UTC; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; import com.google.template.soy.data.SoyMapData; import google.registry.model.pricing.PremiumPricingEngine.DomainPrices; import google.registry.tools.soy.DomainCreateSoyInfo; +import google.registry.util.Clock; import google.registry.util.StringGenerator; import jakarta.inject.Inject; import jakarta.inject.Named; import org.joda.money.Money; -import org.joda.time.DateTime; /** A command to create a new domain via EPP. */ @Parameters(separators = " =", commandDescription = "Create a new domain via EPP.") @@ -53,6 +52,8 @@ final class CreateDomainCommand extends CreateOrUpdateDomainCommand { @Named("base64StringGenerator") StringGenerator passwordGenerator; + @Inject Clock clock; + private static final int PASSWORD_LENGTH = 16; @Override @@ -64,7 +65,7 @@ protected void initMutatingEppToolCommand() { for (String domain : domains) { String currency = null; String cost = null; - DomainPrices prices = getPricesForDomainName(domain, DateTime.now(UTC)); + DomainPrices prices = getPricesForDomainName(domain, clock.nowUtc()); // Check if the domain is premium and set the fee on the create command if so. if (prices.isPremium()) { diff --git a/core/src/main/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java b/core/src/main/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java index 469cd2c027f..f8a4f619d7d 100644 --- a/core/src/main/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java +++ b/core/src/main/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java @@ -21,7 +21,6 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet; import static google.registry.util.RegistrarUtils.normalizeRegistrarName; import static java.nio.charset.StandardCharsets.US_ASCII; -import static org.joda.time.DateTimeZone.UTC; import com.beust.jcommander.Parameter; import com.google.common.base.Ascii; @@ -37,6 +36,7 @@ import google.registry.tools.params.PathParameter.InputFile; import google.registry.tools.params.StringListParameter; import google.registry.util.CidrAddressBlock; +import google.registry.util.Clock; import jakarta.inject.Inject; import java.nio.file.Files; import java.nio.file.Path; @@ -56,6 +56,8 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand { @Inject CertificateChecker certificateChecker; + @Inject Clock clock; + @Parameter(description = "Client identifier of the registrar account", required = true) List mainParameters; @@ -272,7 +274,7 @@ protected void initRegistrarCommand() {} @Override protected final void init() throws Exception { initRegistrarCommand(); - DateTime now = DateTime.now(UTC); + DateTime now = clock.nowUtc(); for (String clientId : mainParameters) { Registrar oldRegistrar = getOldRegistrar(clientId); Registrar.Builder builder = diff --git a/core/src/main/java/google/registry/tools/CreateReservedListCommand.java b/core/src/main/java/google/registry/tools/CreateReservedListCommand.java index 7cbe44c3572..878e8ebf8dc 100644 --- a/core/src/main/java/google/registry/tools/CreateReservedListCommand.java +++ b/core/src/main/java/google/registry/tools/CreateReservedListCommand.java @@ -18,7 +18,6 @@ import static google.registry.model.tld.Tlds.assertTldExists; import static google.registry.util.ListNamingUtils.convertFilePathToName; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.joda.time.DateTimeZone.UTC; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; @@ -26,6 +25,8 @@ import com.google.common.base.Splitter; import com.google.common.base.Strings; import google.registry.model.tld.label.ReservedList; +import google.registry.util.Clock; +import jakarta.inject.Inject; import java.nio.file.Files; import java.util.List; import org.joda.time.DateTime; @@ -34,6 +35,8 @@ @Parameters(separators = " =", commandDescription = "Create a ReservedList.") final class CreateReservedListCommand extends CreateOrUpdateReservedListCommand { + @Inject Clock clock; + @VisibleForTesting static final String INVALID_FORMAT_ERROR_MESSAGE = "The name must be in the format {tld|common}_list-name " @@ -51,7 +54,7 @@ protected String prompt() throws Exception { if (!override) { validateListName(name); } - DateTime now = DateTime.now(UTC); + DateTime now = clock.nowUtc(); List allLines = Files.readAllLines(input, UTF_8); reservedList = new ReservedList.Builder() diff --git a/core/src/main/java/google/registry/tools/GenerateZoneFilesCommand.java b/core/src/main/java/google/registry/tools/GenerateZoneFilesCommand.java index b08ca56f482..6fd1fb55f49 100644 --- a/core/src/main/java/google/registry/tools/GenerateZoneFilesCommand.java +++ b/core/src/main/java/google/registry/tools/GenerateZoneFilesCommand.java @@ -15,7 +15,6 @@ package google.registry.tools; import static google.registry.model.tld.Tlds.assertTldsExist; -import static org.joda.time.DateTimeZone.UTC; import static org.joda.time.Duration.standardMinutes; import com.beust.jcommander.Parameter; @@ -23,6 +22,8 @@ import com.google.common.collect.ImmutableMap; import google.registry.tools.params.DateParameter; import google.registry.tools.server.GenerateZoneFilesAction; +import google.registry.util.Clock; +import jakarta.inject.Inject; import java.io.IOException; import java.util.List; import java.util.Map; @@ -43,7 +44,9 @@ final class GenerateZoneFilesCommand implements CommandWithConnection { description = "The date to generate the file for (defaults to today, or yesterday if run " + "before 00:02).", validateWith = DateParameter.class) - private DateTime exportDate = DateTime.now(UTC).minus(standardMinutes(2)).withTimeAtStartOfDay(); + private DateTime exportDate; + + @Inject Clock clock; private ServiceConnection connection; @@ -54,6 +57,9 @@ public void setConnection(ServiceConnection connection) { @Override public void run() throws IOException { + if (exportDate == null) { + exportDate = clock.nowUtc().minus(standardMinutes(2)).withTimeAtStartOfDay(); + } assertTldsExist(mainParameters); ImmutableMap params = ImmutableMap.of( "tlds", mainParameters, diff --git a/core/src/main/java/google/registry/tools/GetHistoryEntriesCommand.java b/core/src/main/java/google/registry/tools/GetHistoryEntriesCommand.java index 394cbc702df..9c2938d0573 100644 --- a/core/src/main/java/google/registry/tools/GetHistoryEntriesCommand.java +++ b/core/src/main/java/google/registry/tools/GetHistoryEntriesCommand.java @@ -17,7 +17,6 @@ import static com.google.common.base.Preconditions.checkArgument; import static google.registry.util.DateTimeUtils.END_OF_TIME; import static google.registry.util.DateTimeUtils.START_OF_TIME; -import static org.joda.time.DateTimeZone.UTC; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; @@ -26,7 +25,9 @@ import google.registry.model.reporting.HistoryEntryDao; import google.registry.persistence.VKey; import google.registry.tools.CommandUtilities.ResourceType; +import google.registry.util.Clock; import google.registry.xml.XmlTransformer; +import jakarta.inject.Inject; import org.joda.time.DateTime; /** Command to show history entries. */ @@ -35,6 +36,8 @@ commandDescription = "Show history entries that occurred in a given time range") final class GetHistoryEntriesCommand implements Command { + @Inject Clock clock; + @Parameter( names = {"-a", "--after"}, description = "Only show history entries that occurred at or after this time") @@ -58,7 +61,7 @@ public void run() { checkArgument( type != null && uniqueId != null, "If either of 'type' or 'id' are set then both must be"); - VKey parentKey = type.getKey(uniqueId, DateTime.now(UTC)); + VKey parentKey = type.getKey(uniqueId, clock.nowUtc()); historyEntries = HistoryEntryDao.loadHistoryObjectsForResource(parentKey, after, before); } else { historyEntries = HistoryEntryDao.loadAllHistoryObjects(after, before); diff --git a/core/src/main/java/google/registry/tools/UpdatePremiumListCommand.java b/core/src/main/java/google/registry/tools/UpdatePremiumListCommand.java index 4e8d7ed3169..8c04977164c 100644 --- a/core/src/main/java/google/registry/tools/UpdatePremiumListCommand.java +++ b/core/src/main/java/google/registry/tools/UpdatePremiumListCommand.java @@ -24,12 +24,16 @@ import google.registry.model.tld.label.PremiumList; import google.registry.model.tld.label.PremiumListDao; import google.registry.model.tld.label.PremiumListUtils; +import google.registry.util.Clock; +import jakarta.inject.Inject; import java.nio.file.Files; /** Command to safely update {@link PremiumList} in Database for a given TLD. */ @Parameters(separators = " =", commandDescription = "Update a PremiumList in Database.") class UpdatePremiumListCommand extends CreateOrUpdatePremiumListCommand { + @Inject Clock clock; + @Parameter( names = {"-d", "--dry_run"}, description = "Does not execute the entity mutation") @@ -62,7 +66,8 @@ protected String prompt() throws Exception { inputData = Files.readAllLines(inputFile, UTF_8); checkArgument(!inputData.isEmpty(), "New premium list data cannot be empty"); currency = existingList.getCurrency(); - PremiumList updatedPremiumList = PremiumListUtils.parseToPremiumList(name, currency, inputData); + PremiumList updatedPremiumList = + PremiumListUtils.parseToPremiumList(name, currency, inputData, clock.nowUtc()); if (!existingList .getLabelsToPrices() .entrySet() diff --git a/core/src/main/java/google/registry/ui/server/console/ConsoleDumDownloadAction.java b/core/src/main/java/google/registry/ui/server/console/ConsoleDumDownloadAction.java index e056d116ad0..c609a7d023b 100644 --- a/core/src/main/java/google/registry/ui/server/console/ConsoleDumDownloadAction.java +++ b/core/src/main/java/google/registry/ui/server/console/ConsoleDumDownloadAction.java @@ -16,7 +16,6 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.request.Action.Method.GET; -import static org.joda.time.DateTimeZone.UTC; import com.google.common.collect.ImmutableList; import com.google.common.flogger.FluentLogger; @@ -35,7 +34,6 @@ import java.io.IOException; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVPrinter; -import org.joda.time.DateTime; @Action( service = Service.CONSOLE, @@ -86,7 +84,7 @@ protected void getHandler(User user) { .setHeader("Cache-Control", "max-age=86400"); // 86400 seconds = 1 day consoleApiParams .response() - .setDateHeader("Expires", DateTime.now(UTC).withTimeAtStartOfDay().plusDays(1)); + .setDateHeader("Expires", clock.nowUtc().withTimeAtStartOfDay().plusDays(1)); try (var writer = consoleApiParams.response().getWriter()) { CSVPrinter csvPrinter = new CSVPrinter(writer, CSVFormat.DEFAULT); diff --git a/core/src/test/java/google/registry/batch/DeleteProberDataActionTest.java b/core/src/test/java/google/registry/batch/DeleteProberDataActionTest.java index 96c2d0c98e0..7304c07adbc 100644 --- a/core/src/test/java/google/registry/batch/DeleteProberDataActionTest.java +++ b/core/src/test/java/google/registry/batch/DeleteProberDataActionTest.java @@ -45,6 +45,7 @@ import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension; import google.registry.testing.DatabaseHelper; +import google.registry.testing.FakeClock; import google.registry.testing.SystemPropertyExtension; import google.registry.util.RegistryEnvironment; import java.time.Instant; @@ -62,9 +63,11 @@ class DeleteProberDataActionTest { private static final DateTime DELETION_TIME = DateTime.parse("2010-01-01T00:00:00.000Z"); + private final FakeClock clock = new FakeClock(DateTime.now(UTC)); + @RegisterExtension final JpaIntegrationTestExtension jpa = - new JpaTestExtensions.Builder().buildIntegrationTestExtension(); + new JpaTestExtensions.Builder().withClock(clock).buildIntegrationTestExtension(); @RegisterExtension final SystemPropertyExtension systemPropertyExtension = new SystemPropertyExtension(); @@ -94,7 +97,9 @@ void beforeEach() { } private void resetAction() { - action = new DeleteProberDataAction(false, ImmutableSet.of(), Optional.empty(), "TheRegistrar"); + action = + new DeleteProberDataAction( + false, ImmutableSet.of(), Optional.empty(), "TheRegistrar", clock); } @AfterEach @@ -124,7 +129,8 @@ void test_deletesAllInBatches() throws Exception { Set oaEntities = persistLotsOfDomains("oa-canary.test"); // Create action with batch size of 3 DeleteProberDataAction batchedAction = - new DeleteProberDataAction(false, ImmutableSet.of(), Optional.of(3), "TheRegistrar"); + new DeleteProberDataAction( + false, ImmutableSet.of(), Optional.of(3), "TheRegistrar", clock); batchedAction.run(); assertAllAbsent(ibEntities); assertAllAbsent(oaEntities); diff --git a/core/src/test/java/google/registry/batch/SendExpiringCertificateNotificationEmailActionTest.java b/core/src/test/java/google/registry/batch/SendExpiringCertificateNotificationEmailActionTest.java index d944a3e0953..42ac0ff4a6a 100644 --- a/core/src/test/java/google/registry/batch/SendExpiringCertificateNotificationEmailActionTest.java +++ b/core/src/test/java/google/registry/batch/SendExpiringCertificateNotificationEmailActionTest.java @@ -97,7 +97,8 @@ void beforeEach() throws Exception { EXPIRATION_WARNING_EMAIL_SUBJECT_TEXT, sendEmailService, certificateChecker, - response); + response, + clock); sampleRegistrar = persistResource(createRegistrar("clientId", "sampleRegistrar", null, null).build()); diff --git a/core/src/test/java/google/registry/model/tld/label/PremiumListUtilsTest.java b/core/src/test/java/google/registry/model/tld/label/PremiumListUtilsTest.java index 7abd8f5cbb9..add2245f5d3 100644 --- a/core/src/test/java/google/registry/model/tld/label/PremiumListUtilsTest.java +++ b/core/src/test/java/google/registry/model/tld/label/PremiumListUtilsTest.java @@ -17,20 +17,27 @@ import static com.google.common.truth.Truth.assertThat; import static google.registry.model.tld.label.PremiumListUtils.parseToPremiumList; import static org.joda.money.CurrencyUnit.USD; +import static org.joda.time.DateTimeZone.UTC; import static org.junit.jupiter.api.Assertions.assertThrows; import com.google.common.collect.ImmutableList; import java.math.BigDecimal; +import org.joda.time.DateTime; import org.junit.jupiter.api.Test; /** Unit tests for {@link PremiumListUtils}. */ class PremiumListUtilsTest { + private static final DateTime SAMPLE_TIME = DateTime.now(UTC); + @Test void parseInputToPremiumList_works() { PremiumList premiumList = parseToPremiumList( - "testlist", USD, ImmutableList.of("foo,USD 99.50", "bar,USD 30", "baz,USD 10")); + "testlist", + USD, + ImmutableList.of("foo,USD 99.50", "bar,USD 30", "baz,USD 10"), + SAMPLE_TIME); assertThat(premiumList.getName()).isEqualTo("testlist"); assertThat(premiumList.getLabelsToPrices()) .containsExactly("foo", twoDigits(99.50), "bar", twoDigits(30), "baz", twoDigits(10)); @@ -45,7 +52,8 @@ void parseInputToPremiumList_throwsOnInconsistentCurrencies() { parseToPremiumList( "testlist", USD, - ImmutableList.of("foo,USD 99.50", "bar,USD 30", "baz,JPY 990"))); + ImmutableList.of("foo,USD 99.50", "bar,USD 30", "baz,JPY 990"), + SAMPLE_TIME)); assertThat(thrown).hasMessageThat().isEqualTo("The currency unit must be USD"); } diff --git a/core/src/test/java/google/registry/reporting/icann/IcannReportingStagerTest.java b/core/src/test/java/google/registry/reporting/icann/IcannReportingStagerTest.java index 77092e5e1d9..4e2714781c3 100644 --- a/core/src/test/java/google/registry/reporting/icann/IcannReportingStagerTest.java +++ b/core/src/test/java/google/registry/reporting/icann/IcannReportingStagerTest.java @@ -16,6 +16,7 @@ import static com.google.common.truth.Truth.assertThat; import static java.nio.charset.StandardCharsets.UTF_8; +import static org.joda.time.DateTimeZone.UTC; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -31,15 +32,18 @@ import google.registry.bigquery.BigqueryUtils.TableType; import google.registry.gcs.GcsUtils; import google.registry.reporting.icann.IcannReportingModule.ReportType; +import google.registry.testing.FakeClock; import google.registry.testing.FakeResponse; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; +import org.joda.time.DateTime; import org.joda.time.YearMonth; import org.junit.jupiter.api.Test; /** Unit tests for {@link google.registry.reporting.icann.IcannReportingStager}. */ class IcannReportingStagerTest { + private final FakeClock clock = new FakeClock(DateTime.now(UTC)); private BigqueryConnection bigquery = mock(BigqueryConnection.class); FakeResponse response = new FakeResponse(); private YearMonth yearMonth = new YearMonth(2017, 6); @@ -63,7 +67,7 @@ private void setUpBigquery() { when(bigquery.startQuery(any(String.class), any(DestinationTable.class))) .thenReturn(fakeFuture()); DestinationTable.Builder tableBuilder = - new DestinationTable.Builder() + new DestinationTable.Builder(clock) .datasetId("testdataset") .type(TableType.TABLE) .name("tablename") diff --git a/core/src/test/java/google/registry/tools/CommandTestCase.java b/core/src/test/java/google/registry/tools/CommandTestCase.java index e2060506d3f..d298a0239a0 100644 --- a/core/src/test/java/google/registry/tools/CommandTestCase.java +++ b/core/src/test/java/google/registry/tools/CommandTestCase.java @@ -80,6 +80,18 @@ public final void beforeEachCommandTestCase() throws Exception { RegistryToolEnvironment.UNITTEST.setup(systemPropertyExtension); command = newCommandInstance(); + // Inject the fake clock into the command if it has a clock field. + for (Class c = command.getClass(); c != null; c = c.getSuperclass()) { + try { + java.lang.reflect.Field clockField = c.getDeclaredField("clock"); + clockField.setAccessible(true); + clockField.set(command, fakeClock); + break; + } catch (NoSuchFieldException e) { + // Fall through. + } + } + // Capture standard output/error. Use a single-byte encoding to emulate platforms where default // charset is not UTF_8. oldStdout = System.out; diff --git a/core/src/test/java/google/registry/tools/CreatePremiumListCommandTest.java b/core/src/test/java/google/registry/tools/CreatePremiumListCommandTest.java index 947fdffe816..ace6ce512e4 100644 --- a/core/src/test/java/google/registry/tools/CreatePremiumListCommandTest.java +++ b/core/src/test/java/google/registry/tools/CreatePremiumListCommandTest.java @@ -54,7 +54,6 @@ void commandRun_successCreateList() throws Exception { // since the old entity is always null and file cannot be empty, the prompt will NOT be "No entity // changes to apply." void commandPrompt_successStageNewEntity() throws Exception { - CreatePremiumListCommand command = new CreatePremiumListCommand(); command.inputFile = Paths.get(premiumTermsPath); command.currencyUnit = "USD"; command.prompt(); @@ -63,7 +62,6 @@ void commandPrompt_successStageNewEntity() throws Exception { @Test void commandPrompt_successStageNewEntityWithOverride() throws Exception { - CreatePremiumListCommand command = new CreatePremiumListCommand(); String alterTld = "override"; command.inputFile = Paths.get(premiumTermsPath); command.override = true; @@ -75,7 +73,6 @@ void commandPrompt_successStageNewEntityWithOverride() throws Exception { @Test void commandPrompt_failureNoInputFile() { - CreatePremiumListCommand command = new CreatePremiumListCommand(); assertThrows(NullPointerException.class, command::prompt); } @@ -83,7 +80,6 @@ void commandPrompt_failureNoInputFile() { void commandPrompt_failurePremiumListAlreadyExists() { String randomStr = "random"; DatabaseHelper.createTld(randomStr); - CreatePremiumListCommand command = new CreatePremiumListCommand(); command.name = randomStr; command.currencyUnit = "USD"; IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, command::prompt); @@ -92,7 +88,6 @@ void commandPrompt_failurePremiumListAlreadyExists() { @Test void commandPrompt_failureMismatchedTldFileName_noOverride() throws Exception { - CreatePremiumListCommand command = new CreatePremiumListCommand(); String fileName = "random"; Path tmpPath = tmpDir.resolve(String.format("%s.txt", fileName)); Files.write(new byte[0], tmpPath.toFile()); @@ -111,7 +106,6 @@ void commandPrompt_failureMismatchedTldFileName_noOverride() throws Exception { @Test void commandPrompt_failureMismatchedTldName_noOverride() { - CreatePremiumListCommand command = new CreatePremiumListCommand(); String fileName = "random"; command.name = fileName; command.currencyUnit = "USD"; diff --git a/core/src/test/java/google/registry/tools/CreateReservedListCommandTest.java b/core/src/test/java/google/registry/tools/CreateReservedListCommandTest.java index 4575b073119..acbb6c679d2 100644 --- a/core/src/test/java/google/registry/tools/CreateReservedListCommandTest.java +++ b/core/src/test/java/google/registry/tools/CreateReservedListCommandTest.java @@ -162,7 +162,6 @@ private void runNameTestWithOverride(String name) throws Exception { @Test void testStageEntityChange_succeeds() throws Exception { - CreateReservedListCommand command = new CreateReservedListCommand(); // file content is populated in @BeforeEach of CreateOrUpdateReservedListCommandTestCase.java command.input = Paths.get(reservedTermsPath); command.init(); @@ -176,7 +175,6 @@ void testStageEntityChange_succeeds() throws Exception { void testStageEntityChange_succeedsWithEmptyFile() throws Exception { Path tmpPath = tmpDir.resolve("xn--q9jyb4c_common-tmp.txt"); Files.write(new byte[0], tmpPath.toFile()); - CreateReservedListCommand command = new CreateReservedListCommand(); command.input = tmpPath; command.init(); assertThat(command.prompt()).contains("reservedListMap=[]"); diff --git a/core/src/test/java/google/registry/tools/UpdatePremiumListCommandTest.java b/core/src/test/java/google/registry/tools/UpdatePremiumListCommandTest.java index b76ab2cdad6..c0a46d67f7b 100644 --- a/core/src/test/java/google/registry/tools/UpdatePremiumListCommandTest.java +++ b/core/src/test/java/google/registry/tools/UpdatePremiumListCommandTest.java @@ -60,7 +60,6 @@ void commandPrompt_successStageEntityChange() throws Exception { File tmpFile = tmpDir.resolve(String.format("%s.txt", TLD_TEST)).toFile(); String newPremiumListData = "omg,USD 1234"; Files.asCharSink(tmpFile, UTF_8).write(newPremiumListData); - UpdatePremiumListCommand command = new UpdatePremiumListCommand(); command.inputFile = Paths.get(tmpFile.getPath()); command.name = TLD_TEST; assertThat(command.prompt()).contains("Update premium list for prime?"); @@ -69,7 +68,6 @@ void commandPrompt_successStageEntityChange() throws Exception { @Test void commandPrompt_successStageNoChange() throws Exception { File tmpFile = tmpDir.resolve(String.format("%s.txt", TLD_TEST)).toFile(); - UpdatePremiumListCommand command = new UpdatePremiumListCommand(); command.inputFile = Paths.get(tmpFile.getPath()); command.name = TLD_TEST; assertThat(command.prompt()) @@ -82,7 +80,6 @@ void commandRun_successUpdateList() throws Exception { String newPremiumListData = "eth,USD 9999"; Files.asCharSink(tmpFile, UTF_8).write(newPremiumListData); - UpdatePremiumListCommand command = new UpdatePremiumListCommand(); // data come from @beforeEach of CreateOrUpdatePremiumListCommandTestCase.java command.inputFile = Paths.get(tmpFile.getPath()); runCommandForced("--name=" + TLD_TEST, "--input=" + command.inputFile); @@ -96,7 +93,6 @@ void commandRun_successUpdateList() throws Exception { void commandRun_successNoChange() throws Exception { File tmpFile = tmpDir.resolve(String.format("%s.txt", TLD_TEST)).toFile(); - UpdatePremiumListCommand command = new UpdatePremiumListCommand(); command.inputFile = Paths.get(tmpFile.getPath()); runCommandForced("--name=" + TLD_TEST, "--input=" + command.inputFile); @@ -113,7 +109,6 @@ void commandRun_successUpdateList_whenExistingListIsEmpty() throws Exception { String newPremiumListData = "eth,USD 9999"; Files.asCharSink(newPremiumFile, UTF_8).write(newPremiumListData); - UpdatePremiumListCommand command = new UpdatePremiumListCommand(); // data come from @beforeEach of CreateOrUpdatePremiumListCommandTestCase.java command.inputFile = Paths.get(newPremiumFile.getPath()); runCommandForced("--name=" + TLD_TEST, "--input=" + command.inputFile); @@ -129,7 +124,6 @@ void commandRun_successUpdateMultiLineList() throws Exception { String premiumTerms = "foo,USD 9000\ndoge,USD 100\nelon,USD 2021"; Files.asCharSink(tmpFile, UTF_8).write(premiumTerms); - UpdatePremiumListCommand command = new UpdatePremiumListCommand(); command.inputFile = Paths.get(tmpFile.getPath()); runCommandForced("--name=" + TLD_TEST, "--input=" + command.inputFile); @@ -146,7 +140,6 @@ void commandPrompt_failureUpdateEmptyList() throws Exception { Path tmpPath = tmpDir.resolve(String.format("%s.txt", TLD_TEST)); Files.write(new byte[0], tmpPath.toFile()); - UpdatePremiumListCommand command = new UpdatePremiumListCommand(); command.inputFile = tmpPath; command.name = TLD_TEST; IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, command::prompt); @@ -156,7 +149,6 @@ void commandPrompt_failureUpdateEmptyList() throws Exception { @Test void commandPrompt_failureNoPreviousVersion() { registry = createTld("random", null, null); - UpdatePremiumListCommand command = new UpdatePremiumListCommand(); command.name = "random"; IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, command::prompt); assertThat(thrown) @@ -166,13 +158,11 @@ void commandPrompt_failureNoPreviousVersion() { @Test void commandPrompt_failureNoInputFile() { - UpdatePremiumListCommand command = new UpdatePremiumListCommand(); assertThrows(NullPointerException.class, command::prompt); } @Test void commandPrompt_failureTldFromNameDoesNotExist() { - UpdatePremiumListCommand command = new UpdatePremiumListCommand(); command.name = "random2"; IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, command::prompt); assertThat(thrown) @@ -182,7 +172,6 @@ void commandPrompt_failureTldFromNameDoesNotExist() { @Test void commandPrompt_failureTldFromInputFileDoesNotExist() { - UpdatePremiumListCommand command = new UpdatePremiumListCommand(); // using tld extracted from file name but this tld is not part of the registry command.inputFile = Paths.get(tmpDir.resolve("random3.txt").toFile().getPath()); IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, command::prompt); @@ -197,7 +186,6 @@ void commandDryRun_noChangesMade() throws Exception { String newPremiumListData = "eth,USD 9999"; Files.asCharSink(tmpFile, UTF_8).write(newPremiumListData); - UpdatePremiumListCommand command = new UpdatePremiumListCommand(); command.inputFile = Paths.get(tmpFile.getPath()); runCommandForced("--name=" + TLD_TEST, "--input=" + command.inputFile, "--dry_run"); diff --git a/core/src/test/java/google/registry/tools/UpdateReservedListCommandTest.java b/core/src/test/java/google/registry/tools/UpdateReservedListCommandTest.java index 52d3f5f12cd..8d7f4943ea0 100644 --- a/core/src/test/java/google/registry/tools/UpdateReservedListCommandTest.java +++ b/core/src/test/java/google/registry/tools/UpdateReservedListCommandTest.java @@ -98,7 +98,6 @@ void testSuccess_noChanges() throws Exception { Files.asCharSink(reservedTermsFile, UTF_8).write(reservedTermsCsv); reservedTermsPath = reservedTermsFile.getPath(); // create a command instance and assign its input - UpdateReservedListCommand command = new UpdateReservedListCommand(); command.input = Paths.get(reservedTermsPath); // run again with terms from example_reserved_terms.csv command.init(); @@ -110,7 +109,6 @@ void testSuccess_noChanges() throws Exception { void testSuccess_withChanges() throws Exception { // changes come from example_reserved_terms.csv, which are populated in @BeforeEach of // CreateOrUpdateReservedListCommandTestCases.java - UpdateReservedListCommand command = new UpdateReservedListCommand(); command.input = Paths.get(reservedTermsPath); command.init(); diff --git a/networking/src/main/java/google/registry/networking/module/CertificateSupplierModule.java b/networking/src/main/java/google/registry/networking/module/CertificateSupplierModule.java index d0b24f1ebed..ef4fb9e13c2 100644 --- a/networking/src/main/java/google/registry/networking/module/CertificateSupplierModule.java +++ b/networking/src/main/java/google/registry/networking/module/CertificateSupplierModule.java @@ -25,6 +25,7 @@ import dagger.Lazy; import dagger.Module; import dagger.Provides; +import google.registry.util.Clock; import google.registry.util.SelfSignedCaCertificate; import jakarta.inject.Named; import jakarta.inject.Provider; @@ -156,9 +157,9 @@ static Supplier> provideCertificatesSupplier( @Singleton @Provides - static SelfSignedCaCertificate provideSelfSignedCertificate() { + static SelfSignedCaCertificate provideSelfSignedCertificate(Clock clock) { try { - return SelfSignedCaCertificate.create(); + return SelfSignedCaCertificate.create(clock); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/networking/src/test/java/google/registry/networking/handler/SslClientInitializerTest.java b/networking/src/test/java/google/registry/networking/handler/SslClientInitializerTest.java index 7db756da6d1..928d3ed5f7a 100644 --- a/networking/src/test/java/google/registry/networking/handler/SslClientInitializerTest.java +++ b/networking/src/test/java/google/registry/networking/handler/SslClientInitializerTest.java @@ -22,6 +22,7 @@ import static org.joda.time.DateTimeZone.UTC; import com.google.common.collect.ImmutableList; +import google.registry.testing.FakeClock; import google.registry.util.SelfSignedCaCertificate; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; @@ -173,7 +174,7 @@ void testSuccess_nullPort(SslProvider sslProvider) { @MethodSource("provideTestCombinations") void testFailure_defaultTrustManager_rejectSelfSignedCert(SslProvider sslProvider) throws Exception { - SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create(SSL_HOST); + SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create(SSL_HOST, new FakeClock()); LocalAddress localAddress = new LocalAddress("DEFAULT_TRUST_MANAGER_REJECT_SELF_SIGNED_CERT_" + sslProvider); nettyExtension.setUpServer(localAddress, getServerHandler(false, ssc.key(), ssc.cert())); @@ -204,7 +205,7 @@ void testSuccess_customTrustManager_acceptCertSignedByTrustedCa(SslProvider sslP KeyPair keyPair = getKeyPair(); // Generate a self-signed certificate, and use it to sign the key pair. - SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create(); + SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create(new FakeClock()); X509Certificate cert = signKeyPair(ssc, keyPair, SSL_HOST); // Set up the server to use the signed cert and private key to perform handshake; @@ -239,7 +240,7 @@ void testFailure_customTrustManager_serverCertExpired(SslProvider sslProvider) t KeyPair keyPair = getKeyPair(); // Generate a self-signed certificate, and use it to sign the key pair. - SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create(); + SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create(new FakeClock()); X509Certificate cert = signKeyPair( ssc, keyPair, SSL_HOST, DateTime.now(UTC).minusDays(2), DateTime.now(UTC).minusDays(1)); @@ -276,7 +277,7 @@ void testFailure_customTrustManager_serverCertNotYetValid(SslProvider sslProvide KeyPair keyPair = getKeyPair(); // Generate a self-signed certificate, and use it to sign the key pair. - SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create(); + SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create(new FakeClock()); X509Certificate cert = signKeyPair( ssc, keyPair, SSL_HOST, DateTime.now(UTC).plusDays(1), DateTime.now(UTC).plusDays(2)); @@ -310,8 +311,8 @@ void testSuccess_customTrustManager_acceptSelfSignedCert_clientCertRequired( new LocalAddress( "CUSTOM_TRUST_MANAGER_ACCEPT_SELF_SIGNED_CERT_CLIENT_CERT_REQUIRED_" + sslProvider); - SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST); - SelfSignedCaCertificate clientSsc = SelfSignedCaCertificate.create(); + SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST, new FakeClock()); + SelfSignedCaCertificate clientSsc = SelfSignedCaCertificate.create(new FakeClock()); // Set up the server to require client certificate. nettyExtension.setUpServer( @@ -352,7 +353,7 @@ void testFailure_customTrustManager_wrongHostnameInCertificate(SslProvider sslPr KeyPair keyPair = getKeyPair(); // Generate a self-signed certificate, and use it to sign the key pair. - SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create(); + SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create(new FakeClock()); X509Certificate cert = signKeyPair(ssc, keyPair, "wrong.com"); // Set up the server to use the signed cert and private key to perform handshake; diff --git a/networking/src/test/java/google/registry/networking/handler/SslServerInitializerTest.java b/networking/src/test/java/google/registry/networking/handler/SslServerInitializerTest.java index d1736bfa848..d7646e00b5b 100644 --- a/networking/src/test/java/google/registry/networking/handler/SslServerInitializerTest.java +++ b/networking/src/test/java/google/registry/networking/handler/SslServerInitializerTest.java @@ -24,6 +24,7 @@ import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; +import google.registry.testing.FakeClock; import google.registry.util.SelfSignedCaCertificate; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelInitializer; @@ -148,7 +149,7 @@ private ChannelHandler getClientHandler( @ParameterizedTest @MethodSource("provideTestCombinations") void testSuccess_swappedInitializerWithSslHandler(SslProvider sslProvider) throws Exception { - SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create(SSL_HOST); + SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create(SSL_HOST, new FakeClock()); SslServerInitializer sslServerInitializer = new SslServerInitializer<>( true, @@ -169,13 +170,13 @@ void testSuccess_swappedInitializerWithSslHandler(SslProvider sslProvider) throw @ParameterizedTest @MethodSource("provideTestCombinations") void testSuccess_trustAnyClientCert(SslProvider sslProvider) throws Exception { - SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST); + SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST, new FakeClock()); LocalAddress localAddress = new LocalAddress("TRUST_ANY_CLIENT_CERT_" + sslProvider); nettyExtension.setUpServer( localAddress, getServerHandler(true, false, sslProvider, serverSsc.key(), serverSsc.cert())); - SelfSignedCaCertificate clientSsc = SelfSignedCaCertificate.create(); + SelfSignedCaCertificate clientSsc = SelfSignedCaCertificate.create(new FakeClock()); nettyExtension.setUpClient( localAddress, getClientHandler(sslProvider, serverSsc.cert(), clientSsc.key(), clientSsc.cert())); @@ -193,7 +194,7 @@ void testSuccess_trustAnyClientCert(SslProvider sslProvider) throws Exception { @ParameterizedTest @MethodSource("provideTestCombinations") void testFailure_cipherNotAccepted(SslProvider sslProvider) throws Exception { - SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST); + SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST, new FakeClock()); LocalAddress localAddress = new LocalAddress("CIPHER_NOT_ACCEPTED_" + sslProvider); nettyExtension.setUpServer( @@ -220,7 +221,7 @@ void testFailure_cipherNotAccepted(SslProvider sslProvider) throws Exception { @ParameterizedTest @MethodSource("provideTestCombinations") void testSuccess_someCiphersNotAccepted(SslProvider sslProvider) throws Exception { - SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST); + SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST, new FakeClock()); LocalAddress localAddress = new LocalAddress("SOME_CIPHERS_NOT_ACCEPTED_" + sslProvider); nettyExtension.setUpServer( @@ -258,7 +259,7 @@ void testSuccess_someCiphersNotAccepted(SslProvider sslProvider) throws Exceptio @ParameterizedTest @MethodSource("provideTestCombinations") void testFailure_protocolNotAccepted(SslProvider sslProvider) throws Exception { - SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST); + SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST, new FakeClock()); LocalAddress localAddress = new LocalAddress("PROTOCOL_NOT_ACCEPTED_" + sslProvider); nettyExtension.setUpServer( @@ -288,7 +289,7 @@ void testFailure_protocolNotAccepted(SslProvider sslProvider) throws Exception { @ParameterizedTest @MethodSource("provideTestCombinations") void testFailure_clientCertExpired(SslProvider sslProvider) throws Exception { - SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST); + SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST, new FakeClock()); LocalAddress localAddress = new LocalAddress("CLIENT_CERT_EXPIRED_" + sslProvider); nettyExtension.setUpServer( @@ -309,7 +310,7 @@ void testFailure_clientCertExpired(SslProvider sslProvider) throws Exception { @ParameterizedTest @MethodSource("provideTestCombinations") void testFailure_clientCertNotYetValid(SslProvider sslProvider) throws Exception { - SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST); + SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST, new FakeClock()); LocalAddress localAddress = new LocalAddress("CLIENT_CERT_EXPIRED_" + sslProvider); nettyExtension.setUpServer( @@ -330,7 +331,7 @@ void testFailure_clientCertNotYetValid(SslProvider sslProvider) throws Exception @ParameterizedTest @MethodSource("provideTestCombinations") void testSuccess_doesNotRequireClientCert(SslProvider sslProvider) throws Exception { - SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST); + SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST, new FakeClock()); LocalAddress localAddress = new LocalAddress("DOES_NOT_REQUIRE_CLIENT_CERT_" + sslProvider); nettyExtension.setUpServer( @@ -353,7 +354,7 @@ void testSuccess_doesNotRequireClientCert(SslProvider sslProvider) throws Except @MethodSource("provideTestCombinations") void testSuccess_CertSignedByOtherCa(SslProvider sslProvider) throws Exception { // The self-signed cert of the CA. - SelfSignedCaCertificate caSsc = SelfSignedCaCertificate.create(); + SelfSignedCaCertificate caSsc = SelfSignedCaCertificate.create(new FakeClock()); KeyPair keyPair = getKeyPair(); X509Certificate serverCert = signKeyPair(caSsc, keyPair, SSL_HOST); LocalAddress localAddress = new LocalAddress("CERT_SIGNED_BY_OTHER_CA_" + sslProvider); @@ -368,7 +369,7 @@ void testSuccess_CertSignedByOtherCa(SslProvider sslProvider) throws Exception { // Serving both the server cert, and the CA cert serverCert, caSsc.cert())); - SelfSignedCaCertificate clientSsc = SelfSignedCaCertificate.create(); + SelfSignedCaCertificate clientSsc = SelfSignedCaCertificate.create(new FakeClock()); nettyExtension.setUpClient( localAddress, getClientHandler( @@ -392,7 +393,7 @@ void testSuccess_CertSignedByOtherCa(SslProvider sslProvider) throws Exception { @ParameterizedTest @MethodSource("provideTestCombinations") void testFailure_requireClientCertificate(SslProvider sslProvider) throws Exception { - SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST); + SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST, new FakeClock()); LocalAddress localAddress = new LocalAddress("REQUIRE_CLIENT_CERT_" + sslProvider); nettyExtension.setUpServer( @@ -417,13 +418,14 @@ void testFailure_requireClientCertificate(SslProvider sslProvider) throws Except @ParameterizedTest @MethodSource("provideTestCombinations") void testFailure_wrongHostnameInCertificate(SslProvider sslProvider) throws Exception { - SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create("wrong.com"); + SelfSignedCaCertificate serverSsc = + SelfSignedCaCertificate.create("wrong.com", new FakeClock()); LocalAddress localAddress = new LocalAddress("WRONG_HOSTNAME_" + sslProvider); nettyExtension.setUpServer( localAddress, getServerHandler(true, false, sslProvider, serverSsc.key(), serverSsc.cert())); - SelfSignedCaCertificate clientSsc = SelfSignedCaCertificate.create(); + SelfSignedCaCertificate clientSsc = SelfSignedCaCertificate.create(new FakeClock()); nettyExtension.setUpClient( localAddress, getClientHandler(sslProvider, serverSsc.cert(), clientSsc.key(), clientSsc.cert())); diff --git a/networking/src/test/java/google/registry/networking/module/CertificateSupplierModuleTest.java b/networking/src/test/java/google/registry/networking/module/CertificateSupplierModuleTest.java index 70ffbfe3ecb..13acae86ac3 100644 --- a/networking/src/test/java/google/registry/networking/module/CertificateSupplierModuleTest.java +++ b/networking/src/test/java/google/registry/networking/module/CertificateSupplierModuleTest.java @@ -26,6 +26,8 @@ import dagger.Module; import dagger.Provides; import google.registry.networking.module.CertificateSupplierModule.Mode; +import google.registry.testing.FakeClock; +import google.registry.util.Clock; import google.registry.util.SelfSignedCaCertificate; import jakarta.inject.Named; import jakarta.inject.Singleton; @@ -59,7 +61,7 @@ private static TestComponent createComponentForPem(Object... objects) throws Exc @BeforeEach void beforeEach() throws Exception { - ssc = SelfSignedCaCertificate.create(); + ssc = SelfSignedCaCertificate.create(new FakeClock()); KeyPair keyPair = getKeyPair(); key = keyPair.getPrivate(); cert = signKeyPair(ssc, keyPair, "example.tld"); @@ -147,6 +149,11 @@ Duration provideCachingDuration() { // Make the supplier always return the save value for test to save time. return Duration.ofDays(1); } + + @Provides + Clock provideClock() { + return new FakeClock(); + } } /** diff --git a/proxy/src/test/java/google/registry/proxy/EppProtocolModuleTest.java b/proxy/src/test/java/google/registry/proxy/EppProtocolModuleTest.java index f6fb6f53460..fa71772ec4a 100644 --- a/proxy/src/test/java/google/registry/proxy/EppProtocolModuleTest.java +++ b/proxy/src/test/java/google/registry/proxy/EppProtocolModuleTest.java @@ -24,6 +24,7 @@ import com.google.common.base.Throwables; import google.registry.proxy.handler.HttpsRelayServiceHandler.NonOkHttpResponseException; +import google.registry.testing.FakeClock; import google.registry.util.SelfSignedCaCertificate; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -122,7 +123,7 @@ private static FullHttpResponse makeEppHttpResponse( @Override void beforeEach() throws Exception { testComponent = makeTestComponent(); - certificate = SelfSignedCaCertificate.create().cert(); + certificate = SelfSignedCaCertificate.create(new FakeClock()).cert(); initializeChannel( ch -> { ch.attr(REMOTE_ADDRESS_KEY).set(CLIENT_ADDRESS); diff --git a/proxy/src/test/java/google/registry/proxy/handler/EppServiceHandlerTest.java b/proxy/src/test/java/google/registry/proxy/handler/EppServiceHandlerTest.java index cc4c4578d1e..8ccab79f1ae 100644 --- a/proxy/src/test/java/google/registry/proxy/handler/EppServiceHandlerTest.java +++ b/proxy/src/test/java/google/registry/proxy/handler/EppServiceHandlerTest.java @@ -32,6 +32,7 @@ import google.registry.proxy.TestUtils; import google.registry.proxy.handler.HttpsRelayServiceHandler.NonOkHttpResponseException; import google.registry.proxy.metric.FrontendMetrics; +import google.registry.testing.FakeClock; import google.registry.util.ProxyHttpHeaders; import google.registry.util.SelfSignedCaCertificate; import io.netty.buffer.ByteBuf; @@ -114,7 +115,7 @@ private FullHttpRequest makeEppHttpRequest(String content, Cookie... cookies) th @BeforeEach void beforeEach() throws Exception { - clientCertificate = SelfSignedCaCertificate.create().cert(); + clientCertificate = SelfSignedCaCertificate.create(new FakeClock()).cert(); channel = setUpNewChannel(eppServiceHandler); } @@ -171,7 +172,7 @@ void testSuccess_connectionMetrics_twoConnections_differentClients() throws Exce new EppServiceHandler( RELAY_HOST, RELAY_PATH, false, () -> ID_TOKEN, HELLO.getBytes(UTF_8), metrics); EmbeddedChannel channel2 = setUpNewChannel(eppServiceHandler2); - X509Certificate clientCertificate2 = SelfSignedCaCertificate.create().cert(); + X509Certificate clientCertificate2 = SelfSignedCaCertificate.create(new FakeClock()).cert(); setHandshakeSuccess(channel2, clientCertificate2); String certHash2 = getCertificateHash(clientCertificate2); diff --git a/util/src/main/java/google/registry/util/PosixTarHeader.java b/util/src/main/java/google/registry/util/PosixTarHeader.java index 4d9b40550d2..5c68086c253 100644 --- a/util/src/main/java/google/registry/util/PosixTarHeader.java +++ b/util/src/main/java/google/registry/util/PosixTarHeader.java @@ -324,12 +324,12 @@ public static class Builder { private final byte[] header = new byte[HEADER_LENGTH]; private boolean hasName = false; private boolean hasSize = false; + private boolean hasMtime = false; public Builder() { setMode(DEFAULT_MODE); setUid(DEFAULT_UID); setGid(DEFAULT_GID); - setMtime(DateTime.now(UTC)); setType(DEFAULT_TYPE); setMagic(); setVersion(); @@ -418,6 +418,7 @@ public Builder setSize(long size) { public Builder setMtime(DateTime mtime) { checkNotNull(mtime, "mtime"); setField("mtime", 136, 12, String.format("%011o", mtime.getMillis() / MILLIS_PER_SECOND)); + hasMtime = true; return this; } @@ -483,8 +484,10 @@ private void setVersion() { public PosixTarHeader build() { checkState(hasName, "name not set"); checkState(hasSize, "size not set"); + checkState(hasMtime, "mtime not set"); hasName = false; hasSize = false; + hasMtime = false; setChksum(); // Calculate the checksum last. return new PosixTarHeader(header.clone()); } diff --git a/util/src/main/java/google/registry/util/SelfSignedCaCertificate.java b/util/src/main/java/google/registry/util/SelfSignedCaCertificate.java index 189c77cf549..4f58929ce0f 100644 --- a/util/src/main/java/google/registry/util/SelfSignedCaCertificate.java +++ b/util/src/main/java/google/registry/util/SelfSignedCaCertificate.java @@ -15,7 +15,6 @@ package google.registry.util; import static com.google.common.base.Preconditions.checkArgument; -import static org.joda.time.DateTimeZone.UTC; import com.google.common.collect.ImmutableMap; import java.math.BigInteger; @@ -42,8 +41,6 @@ public class SelfSignedCaCertificate { private static final String DEFAULT_ISSUER_FQDN = "registry-test"; - private static final DateTime DEFAULT_NOT_BEFORE = DateTime.now(UTC).minusHours(1); - private static final DateTime DEFAULT_NOT_AFTER = DateTime.now(UTC).plusDays(1); private static final Random RANDOM = new Random(); private static final BouncyCastleProvider PROVIDER = new BouncyCastleProvider(); @@ -68,13 +65,16 @@ public X509Certificate cert() { return cert; } - public static SelfSignedCaCertificate create() throws Exception { + public static SelfSignedCaCertificate create(Clock clock) throws Exception { return create( - keyGen.generateKeyPair(), DEFAULT_ISSUER_FQDN, DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER); + keyGen.generateKeyPair(), + DEFAULT_ISSUER_FQDN, + clock.nowUtc().minusHours(1), + clock.nowUtc().plusDays(1)); } - public static SelfSignedCaCertificate create(String fqdn) throws Exception { - return create(fqdn, DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER); + public static SelfSignedCaCertificate create(String fqdn, Clock clock) throws Exception { + return create(fqdn, clock.nowUtc().minusHours(1), clock.nowUtc().plusDays(1)); } public static SelfSignedCaCertificate create(String fqdn, DateTime from, DateTime to) diff --git a/util/src/test/java/google/registry/util/PosixTarHeaderTest.java b/util/src/test/java/google/registry/util/PosixTarHeaderTest.java index da8a1a70c0f..1b0d41bc902 100644 --- a/util/src/test/java/google/registry/util/PosixTarHeaderTest.java +++ b/util/src/test/java/google/registry/util/PosixTarHeaderTest.java @@ -27,6 +27,7 @@ import java.util.Arrays; import java.util.zip.GZIPInputStream; import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; import org.joda.time.format.ISODateTimeFormat; import org.junit.jupiter.api.Test; @@ -199,7 +200,11 @@ void testLoad() { @Test void testBadChecksum() { PosixTarHeader header = - new PosixTarHeader.Builder().setName("(◕‿◕).txt").setSize(31337).build(); + new PosixTarHeader.Builder() + .setName("(◕‿◕).txt") + .setSize(31337) + .setMtime(DateTime.now(DateTimeZone.UTC)) + .build(); byte[] bytes = header.getBytes(); bytes[150] = '0'; bytes[151] = '0'; @@ -234,6 +239,7 @@ void testHashEquals() { new PosixTarHeader.Builder() .setName("(•︵•).txt") // Awwwww! It looks so sad... .setSize(123) + .setMtime(DateTime.now(DateTimeZone.UTC)) .build()) .testEquals(); }