1
0
mirror of https://github.com/google/nomulus synced 2026-05-16 21:01:44 +00:00

Compare commits

..

14 Commits

Author SHA1 Message Date
Weimin Yu
3de790fb00 Enable new errorprone checks and fix violations (#3018) 2026-04-20 21:03:36 +00:00
Weimin Yu
9d5650132b Add missing commands to RegistryToolComponent (#3017)
They worked in the past unexpectedly but no longer.
2026-04-20 18:03:03 +00:00
Ben McIlwain
30b507cc79 Update GEMINI.md with advanced Java idioms and source control instructions (#3016) 2026-04-20 16:57:53 +00:00
Ben McIlwain
ea1ad7901c Delete RemoveAllDomainContactsAction (#2872)
We no longer need this now that all domain contacts have been successfully
removed in sandbox and production, and additionally, we are removing all
contacts entirely soon enough.
2026-04-20 14:03:20 +00:00
Ben McIlwain
c427f2cc9f Migrate core temporal models and related entities to java.time.Instant (#3001)
This comprehensive refactor continues the migration from Joda-Time to java.time (Instant), focusing on core timestamp models, transition properties, and their integration across the codebase.

Key changes:
- Migrated CreateAutoTimestamp and UpdateAutoTimestamp to use Instant internally, providing Joda-Time bridge methods for backward compatibility.
- Updated TimedTransitionProperty to handle Instant-based transition maps and updated corresponding Hibernate UserTypes (TimedTransitionBaseUserType).
- Migrated GracePeriod, BillingBase, BillingEvent, PollMessage, and PendingActionNotificationResponse fields (e.g., expirationTime, eventTime) to Instant.
- Migrated additional core entities (DomainBase, Registrar, HostBase, LaunchNotice, BsaLabel, DomainTransactionRecord) to use Instant for registrationExpirationTime, lastTransferTime, creationTime, etc.
- Updated Tld and FeatureFlag models to use Instant for claimsPeriodEnd, bsaEnrollStartTime, and status transitions.
- Enhanced CLI tools and parameters (TransitionListParameter, InstantParameter, RequestParameters) to support Instant-based input and output.
- Updated EntityYamlUtils with custom Instant serializers/deserializers to maintain format consistency (e.g., .SSSZ precision) required for YAML-based tests.
- Implemented UtcInstantAdapter to ensure JAXB XML serialization maintains millisecond accuracy, matching legacy Joda-Time behavior.
- Resolved Hibernate 6 type mismatches in JPQL and Native queries by ensuring consistent use of Instant for comparisons.
- Updated GEMINI.md with project-specific engineering standards, including the 'one commit per PR' mandate, full-build validation requirement, and commit message style rules.
- Cleaned up unnecessary @JsonIgnore and @JsonProperty annotations that were previously added to methods with parameters or redundant fields.
- Refactored DateTimeUtils to use strongly-typed overloads and standardized naming (earliestOf, latestOf) while avoiding type erasure clashes.
- Cleaned up fully qualified calls to toDateTime and toInstant by adding static imports across core model and flow files.
- Refactored test suites to use clock.now() (Instant) instead of nowUtc() (DateTime) and removed custom Truth subjects in favor of standard assertions.
2026-04-20 14:03:20 +00:00
gbrodman
db733aa50f Fix XML parsing issues that occur on dependency update (#3012)
We want to make sure that we use the same XML factories no matter what,
so we use "newDefaultFactory" instead of "newFactory" (to avoid picking
up some random thing on the classpath).

This also fixes an exception that occurs if you haven't synced the
internal repo with the public repo.
2026-04-16 20:15:02 +00:00
Weimin Yu
409a7ba66f Change language and bytecode levels to Java 25 (#3009)
Release/Build tested in alpha.

Deployed in crash.
2026-04-15 16:34:52 +00:00
Weimin Yu
e85f48beba Fix urs command after DomainUpdateFlow change (#3008)
PR 2930 forbids adding statuses that already exist on a domain.
This PR updates the uniform_rapid_suspension command to conform.

PR 2930 also forbids removing non-existent statuses, but it does
not apply to this command.
2026-04-13 20:52:29 +00:00
gbrodman
b78d12e73f Add a DelegatingReplicaJpaTransactionManager to handle multiple replicas (#3005)
This will allow us to spread the load across multiple Postgres replica
instances which should help with latency and stability.
2026-04-10 20:46:06 +00:00
Weimin Yu
4a1d0609f3 Support Fee-1.0 XML parser in all environments (#3007)
Add the Fee-1.0 schema in production, allowing the requests with this
extension to be parsed. This allows us to test this extension before hand.

The announcement of this extension in greeting is controlled by a
feature flag in ProtocolDefinition.java. As long as it is not announced,
we do not expect real customers to use this extension.
2026-04-09 20:45:36 +00:00
gbrodman
61b121f464 Fix Gradle issues when running devTool (#3006)
I think this may have been introduced as part of Gradle 9? Not sure why
it's not showing up in the remote builds but without this, I can't run
any "devTool" commands.

Note: the fixes were suggested by gemini-cli
2026-04-09 20:11:44 +00:00
Weimin Yu
074f78cfb3 Fix docker client api version on CloudBuild (#3004)
The docker engine provided by CloudBuild only supports up to 1.41.

Explicitly set API version for downloaded client for now.
2026-04-08 18:13:26 +00:00
Weimin Yu
7be5fe4c01 Fix Flyway script (#3003)
Removing the obsolete command line argument `community`.
2026-04-07 20:40:04 +00:00
Weimin Yu
1876e2c3e8 Fix Kyeth for Java 25 (#3002) 2026-04-06 21:42:11 +00:00
291 changed files with 4877 additions and 1880 deletions

View File

@@ -20,6 +20,6 @@ jobs:
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '21'
java-version: '25'
- name: Generate and submit dependency graph
uses: gradle/actions/dependency-submission@v3

View File

@@ -1,21 +1,27 @@
# Engineering Standards for Gemini CLI
This document outlines foundational mandates, architectural patterns, and project-specific conventions to ensure high-quality, idiomatic, and consistent code from the first iteration.
This document outlines foundational mandates, architectural patterns, and project-specific conventions to ensure high-quality, idiomatic, and consistent code from the first iteration. When modifying this file, always review the full document to prevent the introduction of duplicate instructions and ensure the content remains coherent and logically organized.
## Core Mandates
### 1. Rigorous Import Management
- **Addition:** When adding new symbols, ensure the corresponding import is added.
- **Removal:** When removing the last usage of a class or symbol from a file (e.g., removing a `@Inject Clock clock;` field), **immediately remove the associated import**. Do not wait for a build failure to identify unused imports.
- **No Redundant Qualifications:** NEVER use fully qualified class names (e.g., `java.time.temporal.ChronoUnit.DAYS`) in code when an import can be used instead. Always prefer adding an import and using the simple name.
- **Static Imports for Utilities:** Always statically import methods from utility classes like `DateTimeUtils` or `CacheUtils`. (e.g., use `toInstant(...)` instead of `DateTimeUtils.toInstant(...)`).
- **Checkstyle:** Proactively fix common checkstyle errors (line length > 100, formatting, unused imports) during the initial code write. Do not wait for CI/build failures to address these, as iterative fixes are inefficient.
- **Verification:** Before finalizing any change, scan the imports section for redundancy.
- **Verification**: Before finalizing any change, scan the imports section for redundancy.
- **License Headers**: When creating new files, ensure the license header uses the current year (e.g., 2026). Existing files should retain their original year.
### 2. Time and Precision Handling (java.time Migration)
## 2. Time and Precision Handling (java.time Migration)
- **Idiomatic java.time Usage:** Avoid redundant conversions between `Instant` and `DateTime`. If a field or parameter is an `Instant`, use it directly. Do not convert to `DateTime` just to call a deprecated method if an `Instant` alternative exists or can be easily created.
- **Millisecond Precision:** Always truncate `Instant.now()` to milliseconds (using `.truncatedTo(ChronoUnit.MILLIS)`) to maintain consistency with Joda `DateTime` and the PostgreSQL schema (which enforces millisecond precision via JPA converters).
- **Clock Injection:**
- Avoid direct calls to `Instant.now()`, `DateTime.now()`, `ZonedDateTime.now()`, or `System.currentTimeMillis()`.
- Inject `google.registry.util.Clock` (production) or `google.registry.testing.FakeClock` (tests).
- Use `clock.nowDate()` to get a `ZonedDateTime` in UTC.
- When defining timestamps for tests, prefer using a fixed, static constant (e.g., `Instant.parse("2024-03-27T10:15:30.105Z")`) over capturing `clock.now()` to prevent flaky tests caused by the passage of real time. Avoid using the Unix epoch (`START_INSTANT`) unless specifically testing epoch-related logic; instead, use realistic dates and vary them across different test suites to ensure logic isn't dependent on a specific "standard" date.
- **Beam Pipelines:**
- Ensure `Clock` is serializable (it is by default in this project) when used in Beam `DoFn`s.
- Pass the `Clock` through the constructor or via Dagger provider methods in the pipeline module.
@@ -50,3 +56,84 @@ This document outlines foundational mandates, architectural patterns, and projec
## Performance and Efficiency
- **Turn Minimization:** Aim for "perfect" code in the first iteration. Iterative fixes for checkstyle or compilation errors consume significant context and time.
- **Context Management:** Use sub-agents for batch refactoring or high-volume output tasks to keep the main session history lean and efficient.
## General Code Review Lessons & Avoidable Mistakes
Based on historical PR reviews, avoid the following common mistakes:
- **No Unnecessary Casts:** Do not unnecessarily cast objects if the method signature accepts the type directly (e.g., avoid `(Instant) fakeClock.now()` or `(ImmutableSet<String>) bsaQuery(...)` if it compiles without it).
- **Visibility Modifiers:** Do not use `/* package */` comments to denote package-private visibility. Just leave the modifier blank; it is an established idiom in this codebase.
### Advanced Java & Guava Idioms
- **Immutable Types:** Declare variables, fields, and return types explicitly as Guava immutable types (e.g., `ImmutableList<T>`, `ImmutableMap<K, V>`) instead of their generic interfaces (`List<T>`, `Map<K, V>`) to clearly communicate immutability contracts to callers. Use `toImmutableList()` and `toImmutableMap()` collectors in streams rather than manually accumulating into an `ArrayList` or `HashMap`.
- **Constructors:** Do not perform heavy logic, I/O, or external API calls inside constructors. Initialization logic should be deferred or handled in a factory method or a dedicated startup routine.
- **Exception Handling:** Do not catch generic `Exception` or `Throwable` if a more specific exception is expected. Never "log and re-throw" the same exception; either handle it entirely (and log), or throw it up the chain. For batch processes, catch exceptions at the individual item/chunk level so one failure doesn't abort the entire batch.
- **Fail Fast:** Validate inputs and fail fast (using `Preconditions.checkArgument` or similar) at the highest level possible rather than passing invalid state (like `null`s) deeper into business logic.
- **Magic Numbers:** Always document magic numbers or hardcoded limits (like `50.0` or `30`) with inline comments explaining the rationale.
- **Null Safety and Optional:** Prefer using `Optional` for any variable that is expected to potentially be null. For any other variable that can be null but cannot use an `Optional` (e.g., function parameters or return types where `Optional` is not idiomatic), it MUST be annotated with `@Nullable`. Always use the `javax.annotation.Nullable` annotation.
---
# Gemini Engineering Guide: Nomulus Codebase
This document captures high-level architectural patterns, lessons learned from large-scale refactorings (like the Joda-Time to `java.time` migration), and specific instructions to avoid common pitfalls in this environment.
## 🏛 Architecture Overview
- **Transaction Management:** The codebase uses a custom wrapper around JPA. Always use `tm()` (from `TransactionManagerFactory`) to interact with the database.
- **Dependency Injection:** Dagger 2 is used extensively. If you see "cannot find symbol" errors for classes starting with `Dagger...`, the project is in a state where annotation processing failed. Fix compilation in core models first to restore generated code.
- **Value Types:** AutoValue and "ImmutableObject" patterns are dominant. Most models follow a `Buildable` pattern with a nested `Builder`.
- **Temporal Logic:** The project is migrating from Joda-Time to `java.time`.
- Core boundaries: `DateTimeUtils.START_OF_TIME_INSTANT` (Unix Epoch) and `END_OF_TIME_INSTANT` (Long.MAX_VALUE / 1000).
- Year Arithmetic: Use `DateTimeUtils.plusYears()` and `DateTimeUtils.minusYears()` to handle February 29th logic correctly.
## Source Control
- **Committing:** Always create a new commit on the branch if one hasn't been created yet for the branch's specific work. Only perform amending (`git commit --amend --no-edit`) for subsequent changes once the initial commit has been successfully created.
- **One Commit Per PR:** All changes for a single PR must be squashed into a single commit before merging.
- **Default to Amend:** Once an initial commit is created for a PR, all subsequent functional changes should be amended into that same commit by default (`git commit --amend --no-edit`). This ensures the PR remains a single, clean unit of work throughout the development lifecycle.
- **Commit Message Style:** Follow standard Git commit best practices. The subject line (first line) should be concise, capitalized, and **must not end with punctuation** (e.g., a period).
- **Final Validation:** Always run `git status` as the final step before declaring a task complete to ensure all changes are committed and the working directory is clean.
- **Commit Verification:** After any commit or amendment, explicitly verify the success of the operation (e.g., using `git status` and reviewing the diff). Never report a Git operation as "done" without having first successfully executed the command and confirmed the repository state.
- **Diff Review:** Before finalizing a task, review the full diff (e.g., `git diff HEAD^`) to ensure all changes are functional and relevant. Identify and revert any formatting-only changes in files that do not contain functional updates to keep the commit focused.
## Refactoring & Migration Guardrails
### 1. Compiler Warnings are Errors (`-Werror`)
This project treats Error Prone warnings as errors.
- **`@InlineMeSuggester`**: When creating deprecated Joda-Time bridge methods (e.g., `getTimestamp() -> return toDateTime(getTimestampInstant())`), you **MUST** immediately add `@SuppressWarnings("InlineMeSuggester")`. If you don't, the build will fail.
- **Repeatable Annotations**: `@SuppressWarnings` is **NOT** repeatable in this environment. If a method or class already has a suppression (e.g., `@SuppressWarnings("unchecked")`), you must merge them:
-`@SuppressWarnings("unchecked") @SuppressWarnings("InlineMeSuggester")`
-`@SuppressWarnings({"unchecked", "InlineMeSuggester"})`
### 2. Resolving Ambiguity
- **Null Overloads**: Adding an `Instant` overload to a method that previously took `DateTime` will break all `create(null)` calls. You must cast them: `create((Instant) null)`.
- **Type Erasure**: Methods taking `Optional<DateTime>` and `Optional<Instant>` will clash due to erasure. Use distinct names, e.g., `setAutorenewEndTimeInstant(Optional<Instant> time)`.
### 3. Build Strategy
- **Surgical Changes**: In large-scale migrations, focus on "leaf" nodes first (Utilities -> Models -> Flows -> Actions).
- **PR Size**: Minimize PR size by retaining Joda-Time bridge methods for high-level "Action" and "Flow" classes unless a full migration is requested. Reverting changes to DNS and Reporting logic while updating the underlying models is a valid strategy to keep PRs reviewable.
- **Validation**: Always run `./gradlew build -x test` before attempting to run unit tests. Unit tests will not run if there are compilation errors in any part of the `core` module. Before finalizing a PR or declaring a task done, you MUST run the entire build using `./gradlew build` and verify that it succeeds completely without errors. Do not declare success if formatting checks (e.g., `spotlessCheck` or `javaIncrementalFormatCheck`) or tests fail. If formatting fails, run `./gradlew spotlessApply` and then re-run `./gradlew build` to verify everything passes.
## 🚫 Common Pitfalls to Avoid
- **Do not go in circles with the build:** If you see an `InlineMeSuggester` error, apply the suppression to **ALL** similar methods in that file and related files in one turn. Do not fix them one by one.
- Dagger/AutoValue corruption: If you modify a builder or a component incorrectly, Dagger will fail to generate code, leading to hundreds of "cannot find symbol" errors. If this happens, `git checkout` the last working state of the specific file and re-apply changes more surgically.
- **`replace` tool context**: When using `replace` on large files (like `Tld.java` or `DomainBase.java`), provide significant surrounding context. These files have many similar method signatures (getters/setters) that can lead to incorrect replacements.
---
# GitHub and Pull Request Protocol
This protocol defines the standard for interacting with GitHub repositories and processing Pull Request (PR) feedback.
## 1. Interaction via `gh` CLI
- **Primary Tool:** ALWAYS use the `gh` CLI for all GitHub-related operations (listing PRs, viewing PR content, checking status, adding comments).
- **Credential Safety:** Never expose tokens or credentials in shell commands.
## 2. Processing PR Feedback
- **Systematic Review:** When asked to address PR comments, first fetch all comments using `gh pr view <number> --json reviews,comments`.
- **Minimal Scope Expansion:** Address comments surgically. If a fix requires changes beyond a few lines or expands the PR's original scope significantly, DO NOT implement it without explicit user approval. Instead, report the issue to the user.
- **Verification:** After addressing feedback, run the full build (`./gradlew build`) and relevant tests to ensure no regressions were introduced.
## 3. PR Lifecycle Management
- **One Commit Per PR:** Ensure all changes are squashed into a single, clean commit. Use `git commit --amend --no-edit` for follow-up fixes.
- **Clean Workspace:** Always run `git status` and verify the repository state before declaring a task complete.

View File

@@ -200,6 +200,7 @@ allprojects {
"--add-exports",
"jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED"]
options.forkOptions.jvmArgs = ["-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
"-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
"-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
"-J--add-exports=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED",
"-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
@@ -358,11 +359,13 @@ subprojects {
// search for `flex-template-base-image` and update the parameter value.
// There are at least two instances, one in core/build.gradle, one in
// release/stage_beam_pipeline.sh
// Also need to change:
// - base images in Dockerfiles under core, jetty, and proxy.
// - Java installation command in the builder image under release.
// - cloudbuild-release.yaml under release.
java {
// TODO(b/457758757): change to V_25 once Java in all environments are
// upgraded.
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
sourceCompatibility = JavaVersion.VERSION_25
targetCompatibility = JavaVersion.VERSION_25
}
project.tasks.test.dependsOn runPresubmits

View File

@@ -22,6 +22,11 @@ import com.google.common.collect.Ordering;
import java.sql.Date;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.format.SignStyle;
import java.time.temporal.ChronoField;
import javax.annotation.Nullable;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
@@ -55,6 +60,38 @@ public abstract class DateTimeUtils {
*/
public static final Instant END_INSTANT = Instant.ofEpochMilli(Long.MAX_VALUE / 1000);
/**
* Standard ISO 8601 formatter with millisecond precision in UTC.
*
* <p>Example: {@code 2024-03-27T10:15:30.105Z}
*
* <p>Handles large/negative years by using a sign prefix if necessary, compatible with {@link
* Instant#parse}.
*/
public static final DateTimeFormatter ISO_8601_FORMATTER =
new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4, 10, SignStyle.NORMAL)
.appendPattern("-MM-dd'T'HH:mm:ss.SSS'Z'")
.toFormatter()
.withZone(ZoneOffset.UTC);
/**
* Parses an ISO-8601 string to an {@link Instant}.
*
* <p>This method is lenient and supports both strings with and without millisecond precision
* (e.g. {@code 2024-03-27T10:15:30Z} and {@code 2024-03-27T10:15:30.105Z}). It also supports
* large years (e.g. {@code 294247-01-10T04:00:54.775Z}).
*/
public static Instant parseInstant(String timestamp) {
try {
// Try the standard millisecond precision format first.
return Instant.from(ISO_8601_FORMATTER.parse(timestamp));
} catch (DateTimeParseException e) {
// Fall back to the standard ISO instant parser which handles varied precision.
return Instant.parse(timestamp);
}
}
/** 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));
@@ -79,15 +116,26 @@ public abstract class DateTimeUtils {
/** Returns the latest of a number of given {@link DateTime} instances. */
public static DateTime latestOf(DateTime first, DateTime... rest) {
return latestDateTimeOf(Lists.asList(first, rest));
}
/** Returns the latest of a number of given {@link Instant} instances. */
public static Instant latestOf(Instant first, Instant... rest) {
return latestOf(Lists.asList(first, rest));
}
/** Returns the latest element in a {@link DateTime} iterable. */
public static DateTime latestOf(Iterable<DateTime> dates) {
public static DateTime latestDateTimeOf(Iterable<DateTime> dates) {
checkArgument(!Iterables.isEmpty(dates));
return Ordering.<DateTime>natural().max(dates);
}
/** Returns the latest element in a {@link Instant} iterable. */
public static Instant latestOf(Iterable<Instant> instants) {
checkArgument(!Iterables.isEmpty(instants));
return Ordering.<Instant>natural().max(instants);
}
/** Returns whether the first {@link DateTime} is equal to or earlier than the second. */
public static boolean isBeforeOrAt(DateTime timeToCheck, DateTime timeToCompareTo) {
return !timeToCheck.isAfter(timeToCompareTo);
@@ -112,8 +160,7 @@ public abstract class DateTimeUtils {
* 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) {
public static DateTime plusYears(DateTime now, int years) {
checkArgument(years >= 0);
return years == 0 ? now : now.plusYears(1).plusYears(years - 1);
}
@@ -122,7 +169,7 @@ public abstract class DateTimeUtils {
* Adds years to a date, in the {@code Duration} sense of semantic years. Use this instead of
* {@link java.time.ZonedDateTime#plusYears} to ensure that we never end up on February 29.
*/
public static Instant leapSafeAddYears(Instant now, long years) {
public static Instant plusYears(Instant now, long years) {
checkArgument(years >= 0);
return (years == 0)
? now
@@ -133,8 +180,7 @@ public abstract class DateTimeUtils {
* 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) {
public static DateTime minusYears(DateTime now, int years) {
checkArgument(years >= 0);
return years == 0 ? now : now.minusYears(1).minusYears(years - 1);
}
@@ -143,13 +189,31 @@ public abstract class DateTimeUtils {
* Subtracts years from a date, in the {@code Duration} sense of semantic years. Use this instead
* of {@link java.time.ZonedDateTime#minusYears} to ensure that we never end up on February 29.
*/
public static Instant leapSafeSubtractYears(Instant now, int years) {
public static Instant minusYears(Instant now, long years) {
checkArgument(years >= 0);
return (years == 0)
? now
: now.atZone(ZoneOffset.UTC).minusYears(1).minusYears(years - 1).toInstant();
}
/**
* @deprecated Use {@link #plusYears(DateTime, int)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static DateTime leapSafeAddYears(DateTime now, int years) {
return plusYears(now, years);
}
/**
* @deprecated Use {@link #minusYears(DateTime, int)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static DateTime leapSafeSubtractYears(DateTime now, int years) {
return minusYears(now, years);
}
public static Date toSqlDate(LocalDate localDate) {
return new Date(localDate.toDateTimeAtStartOfDay().getMillis());
}
@@ -176,10 +240,6 @@ public abstract class DateTimeUtils {
return (instant == null) ? null : org.joda.time.Instant.ofEpochMilli(instant.toEpochMilli());
}
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();
}

View File

@@ -18,6 +18,7 @@ import static org.joda.time.DateTimeZone.UTC;
import jakarta.inject.Inject;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import javax.annotation.concurrent.ThreadSafe;
import org.joda.time.DateTime;
@@ -42,6 +43,6 @@ public class SystemClock implements Clock {
// (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);
return Instant.now().truncatedTo(ChronoUnit.MILLIS);
}
}

View File

@@ -49,6 +49,11 @@ public final class FakeClock implements Clock {
setTo(startTime);
}
/** Creates a FakeClock initialized to a specific time. */
public FakeClock(Instant startTime) {
setTo(startTime);
}
/** Returns the current time. */
@Override
public DateTime nowUtc() {
@@ -89,6 +94,11 @@ public final class FakeClock implements Clock {
currentTimeMillis.set(time.getMillis());
}
/** Sets the time to the specified instant. */
public void setTo(Instant time) {
currentTimeMillis.set(time.toEpochMilli());
}
/** Invokes {@link #setAutoIncrementStep} with one millisecond-step. */
public FakeClock setAutoIncrementByOneMilli() {
return setAutoIncrementStep(Duration.millis(1));

View File

@@ -33,6 +33,8 @@ import com.google.common.truth.Fact;
import com.google.common.truth.FailureMetadata;
import com.google.common.truth.SimpleSubjectBuilder;
import com.google.common.truth.Subject;
import com.google.errorprone.annotations.FormatMethod;
import com.google.errorprone.annotations.FormatString;
import java.io.IOException;
import java.net.URL;
import java.util.Collection;
@@ -125,8 +127,9 @@ public class TextDiffSubject extends Subject {
return assertThat(Resources.asCharSource(resourceUrl, UTF_8).readLines());
}
@FormatMethod
public static SimpleSubjectBuilder<TextDiffSubject, URL> assertWithMessageAboutUrlSource(
String format, Object... params) {
@FormatString String format, Object... params) {
return assertWithMessage(format, params).about(urlFactory());
}
@@ -192,7 +195,7 @@ public class TextDiffSubject extends Subject {
private record SideBySideRowFormatter(int maxExpectedLineLength, int maxActualLineLength) {
public String formatRow(String expected, String actual, char padChar) {
String formatRow(String expected, String actual, char padChar) {
return String.format(
"|%s|%s|",
Strings.padEnd(expected, maxExpectedLineLength, padChar),

View File

@@ -153,7 +153,7 @@ PRESUBMITS = {
PresubmitCheck(
r".*java\.util\.Date.*",
"java",
{"/node_modules/", "JpaTransactionManagerImpl.java"},
{"/node_modules/", "JpaTransactionManagerImpl.java", "DateTimeUtils.java"},
):
"Do not use java.util.Date. Use classes in java.time package instead.",
PresubmitCheck(

View File

@@ -74,11 +74,7 @@ sourceSets {
nonprod {
java {
compileClasspath += main.output
// Add the DB runtime classpath to nonprod so we can load the flyway
// scripts.
runtimeClasspath += main.output +
rootProject.project(":db").sourceSets.main.runtimeClasspath
runtimeClasspath += main.output
}
}
test {
@@ -102,6 +98,7 @@ configurations {
devtool
nonprodImplementation.extendsFrom implementation
nonprodRuntimeOnly.extendsFrom runtimeOnly
testImplementation.extendsFrom nonprodImplementation
@@ -259,6 +256,7 @@ dependencies {
implementation project(':util')
// Import NomulusPostreSql from ':db' for implementation but exclude dependencies.
implementation project(path: ':db', configuration: 'implementationApi')
nonprodRuntimeOnly project(':db')
testRuntimeOnly project(':db')
annotationProcessor deps['com.google.auto.service:auto-service']
@@ -385,6 +383,7 @@ task soyToJava {
project.services.get(ExecOperations).javaexec {
mainClass = "com.google.template.soy.SoyParseInfoGenerator"
classpath = configurations.soy
jvmArgs = ["--sun-misc-unsafe-memory-access=allow", "--enable-native-access=ALL-UNNAMED"]
args = ["--javaPackage", "${javaPackage}",
"--outputDirectory", "${outputDirectory}",
"--javaClassNameSource", "filename",
@@ -440,7 +439,7 @@ artifacts {
task findGoldenImages(type: JavaExec) {
classpath = sourceSets.test.runtimeClasspath
mainClass = 'google.registry.webdriver.GoldenImageFinder'
jvmArgs "--sun-misc-unsafe-memory-access=allow"
def arguments = []
arguments << "--screenshots_for_goldens_dir=${screenshotsForGoldensDir}"
arguments << "--new_goldens_dir=${newGoldensDir}"
@@ -491,6 +490,7 @@ ext.createToolTask = {
project.tasks.create(taskName, JavaExec) {
classpath = sourceSet.runtimeClasspath
mainClass = mainClassName
jvmArgs "--sun-misc-unsafe-memory-access=allow", "--enable-native-access=ALL-UNNAMED"
doFirst {
getToolArgsList().ifPresent {
@@ -508,6 +508,7 @@ createToolTask(
project.tasks.create('generateSqlSchema', JavaExec) {
classpath = sourceSets.nonprod.runtimeClasspath
mainClass = 'google.registry.tools.DevTool'
jvmArgs "--sun-misc-unsafe-memory-access=allow"
args = [
'-e', 'alpha',
'generate_sql_schema', '--start_postgresql', '-o',
@@ -666,6 +667,7 @@ artifacts {
task runTestServer(type: JavaExec) {
mainClass = 'google.registry.server.RegistryTestServerMain'
classpath = sourceSets.test.runtimeClasspath
jvmArgs "--sun-misc-unsafe-memory-access=allow"
dependsOn(rootProject.project('console-webapp').tasks.named('buildConsoleWebapp'))
}

View File

@@ -7,10 +7,12 @@ com.charleskorn.kaml:kaml:0.20.0=deploy_jar,nonprodRuntimeClasspath,runtimeClass
com.fasterxml.jackson.core:jackson-annotations:2.21=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-core:2.20.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-databind:2.20.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.20.2=testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.20.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.datatype:jackson-datatype-joda:2.20.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.20.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson:jackson-bom:2.20.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.woodstox:woodstox-core:7.0.0=testCompileClasspath,testRuntimeClasspath
com.fasterxml:classmate:1.7.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.github.ben-manes.caffeine:caffeine:3.0.5=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
com.github.ben-manes.caffeine:caffeine:3.2.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -33,7 +35,8 @@ com.google.api-client:google-api-client-java6:2.1.4=compileClasspath,deploy_jar,
com.google.api-client:google-api-client-servlet:2.7.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api-client:google-api-client-servlet:2.9.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api-client:google-api-client:2.9.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:gapic-google-cloud-storage-v2:2.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:gapic-google-cloud-storage-v2:2.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.api.grpc:gapic-google-cloud-storage-v2:2.67.0=testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1:3.9.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1:3.9.2=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.181.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
@@ -52,7 +55,8 @@ com.google.api.grpc:grpc-google-cloud-spanner-admin-instance-v1:6.74.0=compileCl
com.google.api.grpc:grpc-google-cloud-spanner-admin-instance-v1:6.77.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-spanner-v1:6.74.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:grpc-google-cloud-spanner-v1:6.77.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-storage-v2:2.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-storage-v2:2.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.api.grpc:grpc-google-cloud-storage-v2:2.67.0=testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-common-protos:2.43.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:grpc-google-common-protos:2.45.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1:3.9.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
@@ -86,22 +90,27 @@ com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:6.77.0=deploy_j
com.google.api.grpc:proto-google-cloud-spanner-executor-v1:6.74.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-cloud-spanner-v1:6.74.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-cloud-spanner-v1:6.77.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-storage-v2:2.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-storage-v2:2.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.api.grpc:proto-google-cloud-storage-v2:2.67.0=testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2:2.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.141.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.141.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-common-protos:2.60.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-iam-v1:1.50.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api:api-common:2.57.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api:gax-grpc:2.64.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api:gax-httpjson:2.69.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-iam-v1:1.50.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.api.grpc:proto-google-iam-v1:1.65.0=testCompileClasspath,testRuntimeClasspath
com.google.api:api-common:2.57.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.api:api-common:2.62.0=testCompileClasspath,testRuntimeClasspath
com.google.api:gax-grpc:2.64.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.api:gax-grpc:2.79.0=testCompileClasspath,testRuntimeClasspath
com.google.api:gax-httpjson:2.69.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.api:gax-httpjson:2.79.0=testCompileClasspath,testRuntimeClasspath
com.google.api:gax:2.74.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-admin-directory:directory_v1-rev20260227-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-bigquery:v2-rev20240815-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-cloudresourcemanager:v1-rev20240310-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dataflow:v1b3-rev20260213-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dns:v1-rev20260219-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-drive:v3-rev20260322-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dataflow:v1b3-rev20260405-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dns:v1-rev20260402-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-drive:v3-rev20260405-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-gmail:v1-rev20260112-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-groupssettings:v1-rev20220614-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-healthcare:v1-rev20240130-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -112,9 +121,11 @@ com.google.apis:google-api-services-pubsub:v1-rev20220904-2.0.0=compileClasspath
com.google.apis:google-api-services-sheets:v4-rev20260213-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-sqladmin:v1beta4-rev20251201-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-storage:v1-rev20250312-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.apis:google-api-services-storage:v1-rev20251118-2.0.0=testCompileClasspath,testRuntimeClasspath
com.google.auth:google-auth-library-credentials:1.43.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.auth:google-auth-library-oauth2-http:1.43.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-storage:v1-rev20260204-2.0.0=testCompileClasspath,testRuntimeClasspath
com.google.auth:google-auth-library-credentials:1.43.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.auth:google-auth-library-credentials:1.46.0=testCompileClasspath,testRuntimeClasspath
com.google.auth:google-auth-library-oauth2-http:1.43.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.auth:google-auth-library-oauth2-http:1.46.0=testCompileClasspath,testRuntimeClasspath
com.google.auto.service:auto-service-annotations:1.0.1=nonprodAnnotationProcessor,testAnnotationProcessor
com.google.auto.service:auto-service-annotations:1.1.1=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.auto.service:auto-service:1.1.1=annotationProcessor
@@ -132,19 +143,21 @@ com.google.cloud.opentelemetry:detector-resources-support:0.33.0=deploy_jar,nonp
com.google.cloud.opentelemetry:exporter-metrics:0.33.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud.opentelemetry:shared-resourcemapping:0.33.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud.sql:jdbc-socket-factory-core:1.28.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud.sql:postgres-socket-factory:1.28.2=deploy_jar,runtimeClasspath,testRuntimeClasspath
com.google.cloud.sql:postgres-socket-factory:1.28.2=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-bigquerystorage:3.9.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.cloud:google-cloud-bigquerystorage:3.9.2=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-bigtable:2.43.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.cloud:google-cloud-bigtable:2.44.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-compute:1.82.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core-grpc:2.54.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core-http:2.54.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core-grpc:2.54.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.cloud:google-cloud-core-grpc:2.69.0=testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core-http:2.54.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.cloud:google-cloud-core-http:2.69.0=testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core:2.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-firestore:3.25.1=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.cloud:google-cloud-firestore:3.26.5=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-monitoring:3.52.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-nio:0.129.0-rc1=testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-nio:0.131.0=testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-pubsub:1.132.1=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.cloud:google-cloud-pubsub:1.133.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-pubsublite:1.14.1=compileClasspath,nonprodCompileClasspath,testCompileClasspath
@@ -185,16 +198,20 @@ com.google.guava:failureaccess:1.0.1=soy
com.google.guava:failureaccess:1.0.3=annotationProcessor,checkstyle,compileClasspath,deploy_jar,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
com.google.guava:guava-parent:32.1.1-jre=soy
com.google.guava:guava-testlib:33.3.0-jre=testRuntimeClasspath
com.google.guava:guava-testlib:33.5.0-jre=testCompileClasspath
com.google.guava:guava-testlib:33.6.0-jre=testCompileClasspath
com.google.guava:guava:32.1.1-jre=soy
com.google.guava:guava:33.4.8-jre=checkstyle
com.google.guava:guava:33.5.0-jre=annotationProcessor,compileClasspath,deploy_jar,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
com.google.guava:guava:33.5.0-jre=annotationProcessor,compileClasspath,deploy_jar,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testRuntimeClasspath
com.google.guava:guava:33.6.0-jre=testCompileClasspath
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=annotationProcessor,checkstyle,compileClasspath,deploy_jar,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
com.google.gwt:gwt-user:2.10.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-apache-v2:2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-appengine:1.46.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-apache-v2:2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.http-client:google-http-client-apache-v2:2.1.0=testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-appengine:1.46.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.http-client:google-http-client-appengine:2.1.0=testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-gson:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-jackson2:1.46.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-jackson2:1.46.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.http-client:google-http-client-jackson2:2.1.0=testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-protobuf:1.44.2=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.http-client:google-http-client-protobuf:1.45.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.http-client:google-http-client:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -218,7 +235,8 @@ com.google.protobuf:protobuf-java-util:4.34.1=testCompileClasspath,testRuntimeCl
com.google.protobuf:protobuf-java:3.21.7=soy
com.google.protobuf:protobuf-java:4.33.2=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
com.google.protobuf:protobuf-java:4.34.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.re2j:re2j:1.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.re2j:re2j:1.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath
com.google.re2j:re2j:1.8=testRuntimeClasspath
com.google.template:soy:2024-02-26=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
com.google.truth:truth:1.4.5=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.googlecode.json-simple:json-simple:1.1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -272,31 +290,48 @@ io.github.classgraph:classgraph:4.8.162=compileClasspath,deploy_jar,nonprodCompi
io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
io.github.java-diff-utils:java-diff-utils:4.16=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-alts:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-api:1.70.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
io.grpc:grpc-api:1.71.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-auth:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-alts:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.grpc:grpc-alts:1.80.0=testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-api:1.70.0=compileClasspath,nonprodCompileClasspath
io.grpc:grpc-api:1.71.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
io.grpc:grpc-api:1.80.0=testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-auth:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.grpc:grpc-auth:1.80.0=testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-census:1.66.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
io.grpc:grpc-census:1.68.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-context:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-core:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-googleapis:1.70.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-grpclb:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-inprocess:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-netty-shaded:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-context:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.grpc:grpc-context:1.80.0=testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-core:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.grpc:grpc-core:1.80.0=testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-googleapis:1.70.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
io.grpc:grpc-googleapis:1.80.0=testRuntimeClasspath
io.grpc:grpc-grpclb:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.grpc:grpc-grpclb:1.80.0=testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-inprocess:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.grpc:grpc-inprocess:1.80.0=testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-netty-shaded:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.grpc:grpc-netty-shaded:1.80.0=testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-netty:1.66.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
io.grpc:grpc-netty:1.68.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-opentelemetry:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-protobuf-lite:1.70.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-protobuf:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-rls:1.70.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-opentelemetry:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.grpc:grpc-opentelemetry:1.80.0=testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-protobuf-lite:1.70.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
io.grpc:grpc-protobuf-lite:1.80.0=testRuntimeClasspath
io.grpc:grpc-protobuf:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.grpc:grpc-protobuf:1.80.0=testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-rls:1.70.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
io.grpc:grpc-rls:1.80.0=testRuntimeClasspath
io.grpc:grpc-services:1.66.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
io.grpc:grpc-services:1.70.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-stub:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-services:1.70.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
io.grpc:grpc-services:1.80.0=testRuntimeClasspath
io.grpc:grpc-stub:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.grpc:grpc-stub:1.80.0=testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-util:1.66.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
io.grpc:grpc-util:1.70.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-util:1.70.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
io.grpc:grpc-util:1.80.0=testRuntimeClasspath
io.grpc:grpc-xds:1.66.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
io.grpc:grpc-xds:1.70.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-xds:1.70.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
io.grpc:grpc-xds:1.80.0=testRuntimeClasspath
io.netty:netty-buffer:4.1.100.Final=compileClasspath,nonprodCompileClasspath,testCompileClasspath
io.netty:netty-buffer:4.1.110.Final=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.netty:netty-codec-http2:4.1.100.Final=compileClasspath,nonprodCompileClasspath,testCompileClasspath
@@ -330,33 +365,34 @@ io.opencensus:opencensus-exporter-metrics-util:0.31.0=compileClasspath,deploy_ja
io.opencensus:opencensus-exporter-stats-stackdriver:0.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opencensus:opencensus-impl-core:0.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opencensus:opencensus-impl:0.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opentelemetry.contrib:opentelemetry-gcp-resources:1.37.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opentelemetry.contrib:opentelemetry-gcp-resources:1.37.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry.contrib:opentelemetry-gcp-resources:1.45.0-alpha=testCompileClasspath,testRuntimeClasspath
io.opentelemetry.instrumentation:opentelemetry-grpc-1.6:2.1.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-incubator:2.1.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opentelemetry.instrumentation:opentelemetry-instrumentation-api:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opentelemetry.semconv:opentelemetry-semconv:1.29.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-api-incubator:1.42.1-alpha=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-api:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-api:1.59.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-api:1.60.1=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-bom:1.42.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-common:1.59.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-common:1.60.1=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-context:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-context:1.59.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-exporter-logging:1.59.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-context:1.60.1=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-exporter-logging:1.60.1=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-extension-incubator:1.35.0-alpha=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-common:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk-common:1.59.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-common:1.60.1=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.59.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.59.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.60.1=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.60.1=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-logs:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk-logs:1.59.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-logs:1.60.1=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-metrics:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk-metrics:1.59.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-metrics:1.60.1=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-trace:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk-trace:1.59.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-trace:1.60.1=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk:1.59.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk:1.60.1=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-semconv:1.26.0-alpha=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.outfoxx:swiftpoet:1.3.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.perfmark:perfmark-api:0.27.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -381,7 +417,7 @@ net.arnx:nashorn-promise:0.1.1=testRuntimeClasspath
net.bytebuddy:byte-buddy-agent:1.17.7=testCompileClasspath,testRuntimeClasspath
net.bytebuddy:byte-buddy:1.14.12=compileClasspath,nonprodCompileClasspath
net.bytebuddy:byte-buddy:1.17.8=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
net.bytebuddy:byte-buddy:1.18.5=testCompileClasspath,testRuntimeClasspath
net.bytebuddy:byte-buddy:1.18.8-jdk5=testCompileClasspath,testRuntimeClasspath
net.java.dev.jna:jna:5.13.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
net.ltgt.gradle.incap:incap:0.2=annotationProcessor,testAnnotationProcessor
net.sf.saxon:Saxon-HE:12.5=checkstyle
@@ -436,17 +472,17 @@ org.apache.maven.doxia:doxia-logging-api:1.12.0=checkstyle
org.apache.maven.doxia:doxia-module-xdoc:1.12.0=checkstyle
org.apache.maven.doxia:doxia-sink-api:1.12.0=checkstyle
org.apache.mina:mina-core:2.2.4=testCompileClasspath,testRuntimeClasspath
org.apache.sshd:sshd-common:3.0.0-M2=testCompileClasspath,testRuntimeClasspath
org.apache.sshd:sshd-core:3.0.0-M2=testCompileClasspath,testRuntimeClasspath
org.apache.sshd:sshd-scp:3.0.0-M2=testCompileClasspath,testRuntimeClasspath
org.apache.sshd:sshd-sftp:3.0.0-M2=testCompileClasspath,testRuntimeClasspath
org.apache.tomcat:tomcat-annotations-api:11.0.20=testCompileClasspath,testRuntimeClasspath
org.apache.sshd:sshd-common:3.0.0-M3=testCompileClasspath,testRuntimeClasspath
org.apache.sshd:sshd-core:3.0.0-M3=testCompileClasspath,testRuntimeClasspath
org.apache.sshd:sshd-scp:3.0.0-M3=testCompileClasspath,testRuntimeClasspath
org.apache.sshd:sshd-sftp:3.0.0-M3=testCompileClasspath,testRuntimeClasspath
org.apache.tomcat:tomcat-annotations-api:11.0.21=testCompileClasspath,testRuntimeClasspath
org.apache.xbean:xbean-reflect:3.7=checkstyle
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
org.bouncycastle:bcpg-jdk18on:1.83=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcpkix-jdk18on:1.83=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcprov-jdk18on:1.83=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcutil-jdk18on:1.83=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcpg-jdk18on:1.84=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcpkix-jdk18on:1.84=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcprov-jdk18on:1.84=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcutil-jdk18on:1.84=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.checkerframework:checker-compat-qual:2.5.3=annotationProcessor,compileClasspath,nonprodCompileClasspath,soy,testAnnotationProcessor,testCompileClasspath
org.checkerframework:checker-compat-qual:2.5.6=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.checkerframework:checker-qual:3.19.0=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
@@ -454,29 +490,31 @@ org.checkerframework:checker-qual:3.33.0=soy
org.checkerframework:checker-qual:3.49.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
org.checkerframework:checker-qual:3.49.3=checkstyle
org.checkerframework:checker-qual:3.52.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.codehaus.mojo:animal-sniffer-annotations:1.24=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.codehaus.mojo:animal-sniffer-annotations:1.24=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
org.codehaus.mojo:animal-sniffer-annotations:1.26=testCompileClasspath,testRuntimeClasspath
org.codehaus.plexus:plexus-classworlds:2.6.0=checkstyle
org.codehaus.plexus:plexus-component-annotations:2.1.0=checkstyle
org.codehaus.plexus:plexus-container-default:2.1.0=checkstyle
org.codehaus.plexus:plexus-utils:3.3.0=checkstyle
org.codehaus.woodstox:stax2-api:4.2.2=testCompileClasspath,testRuntimeClasspath
org.conscrypt:conscrypt-openjdk-uber:2.5.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.eclipse.angus:angus-activation:2.0.3=jaxb
org.eclipse.angus:angus-activation:2.1.0-M1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.eclipse.angus:jakarta.mail:2.1.0-M1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.eclipse.collections:eclipse-collections-api:11.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.eclipse.collections:eclipse-collections:11.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty.ee10:jetty-ee10-servlet:12.1.7=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty.ee10:jetty-ee10-webapp:12.1.7=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty.ee:jetty-ee-webapp:12.1.7=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-http:12.1.7=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-io:12.1.7=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-security:12.1.7=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-server:12.1.7=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-session:12.1.7=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-util:12.1.7=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-xml:12.1.7=testCompileClasspath,testRuntimeClasspath
org.flywaydb:flyway-core:12.3.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.flywaydb:flyway-database-postgresql:12.3.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty.ee10:jetty-ee10-servlet:12.1.8=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty.ee10:jetty-ee10-webapp:12.1.8=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty.ee:jetty-ee-webapp:12.1.8=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-http:12.1.8=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-io:12.1.8=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-security:12.1.8=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-server:12.1.8=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-session:12.1.8=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-util:12.1.8=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-xml:12.1.8=testCompileClasspath,testRuntimeClasspath
org.flywaydb:flyway-core:12.4.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.flywaydb:flyway-database-postgresql:12.4.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.glassfish.jaxb:codemodel:4.0.7=jaxb
org.glassfish.jaxb:jaxb-core:4.0.6=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.glassfish.jaxb:jaxb-core:4.0.7=jaxb
@@ -563,23 +601,23 @@ org.pcollections:pcollections:4.0.1=annotationProcessor,nonprodAnnotationProcess
org.postgresql:postgresql:42.7.10=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.reflections:reflections:0.10.2=checkstyle
org.rnorth.duct-tape:duct-tape:1.0.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-api:4.41.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-chrome-driver:4.41.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-chromium-driver:4.41.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-devtools-v143:4.41.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-devtools-v144:4.41.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-devtools-v145:4.41.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-edge-driver:4.41.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-firefox-driver:4.41.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-http:4.41.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-ie-driver:4.41.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-java:4.41.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-json:4.41.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-manager:4.41.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-os:4.41.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-remote-driver:4.41.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-safari-driver:4.41.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-support:4.41.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-api:4.43.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-chrome-driver:4.43.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-chromium-driver:4.43.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-devtools-v145:4.43.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-devtools-v146:4.43.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-devtools-v147:4.43.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-edge-driver:4.43.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-firefox-driver:4.43.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-http:4.43.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-ie-driver:4.43.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-java:4.43.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-json:4.43.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-manager:4.43.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-os:4.43.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-remote-driver:4.43.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-safari-driver:4.43.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-support:4.43.0=testCompileClasspath,testRuntimeClasspath
org.slf4j:jcl-over-slf4j:1.7.36=testCompileClasspath,testRuntimeClasspath
org.slf4j:jul-to-slf4j:1.7.30=testRuntimeClasspath
org.slf4j:slf4j-api:2.0.17=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -597,16 +635,16 @@ org.webjars.npm:viz.js-graphviz-java:2.1.3=testRuntimeClasspath
org.xerial.snappy:snappy-java:1.1.10.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.xmlresolver:xmlresolver:5.2.2=checkstyle
org.yaml:snakeyaml:2.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
tools.jackson.core:jackson-core:3.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
tools.jackson.core:jackson-databind:3.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
tools.jackson:jackson-bom:3.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
tools.jackson.core:jackson-core:3.1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
tools.jackson.core:jackson-databind:3.1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
tools.jackson:jackson-bom:3.1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-api:17.1.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-diagram:17.9.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-operations:17.9.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-postgresql:17.9.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-text:17.9.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-diagram:17.10.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-operations:17.10.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-postgresql:17.10.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-text:17.10.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-tools:17.1.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-utility:17.1.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler:17.9.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler:17.10.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
xerces:xmlParserAPIs:2.6.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
empty=devtool,shadow

View File

@@ -21,7 +21,7 @@ import static google.registry.request.RequestParameters.extractBooleanParameter;
import static google.registry.request.RequestParameters.extractIntParameter;
import static google.registry.request.RequestParameters.extractLongParameter;
import static google.registry.request.RequestParameters.extractOptionalBooleanParameter;
import static google.registry.request.RequestParameters.extractOptionalDatetimeParameter;
import static google.registry.request.RequestParameters.extractOptionalInstantParameter;
import static google.registry.request.RequestParameters.extractOptionalIntParameter;
import static google.registry.request.RequestParameters.extractOptionalParameter;
import static google.registry.request.RequestParameters.extractRequiredDatetimeParameter;
@@ -41,6 +41,7 @@ import google.registry.request.OptionalJsonPayload;
import google.registry.request.Parameter;
import jakarta.inject.Named;
import jakarta.servlet.http.HttpServletRequest;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import org.joda.time.DateTime;
@@ -121,14 +122,14 @@ public class BatchModule {
@Provides
@Parameter(ExpandBillingRecurrencesAction.PARAM_START_TIME)
static Optional<DateTime> provideStartTime(HttpServletRequest req) {
return extractOptionalDatetimeParameter(req, ExpandBillingRecurrencesAction.PARAM_START_TIME);
static Optional<Instant> provideStartTime(HttpServletRequest req) {
return extractOptionalInstantParameter(req, ExpandBillingRecurrencesAction.PARAM_START_TIME);
}
@Provides
@Parameter(ExpandBillingRecurrencesAction.PARAM_END_TIME)
static Optional<DateTime> provideEndTime(HttpServletRequest req) {
return extractOptionalDatetimeParameter(req, ExpandBillingRecurrencesAction.PARAM_END_TIME);
static Optional<Instant> provideEndTime(HttpServletRequest req) {
return extractOptionalInstantParameter(req, ExpandBillingRecurrencesAction.PARAM_END_TIME);
}
@Provides

View File

@@ -15,7 +15,8 @@ package google.registry.batch;
import static google.registry.persistence.PersistenceModule.TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.END_INSTANT;
import static google.registry.util.DateTimeUtils.minusYears;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -110,7 +111,8 @@ public class CheckBulkComplianceAction implements Runnable {
+ " 'DOMAIN_CREATE'",
Long.class)
.setParameter("token", bulkPricingPackage.getToken())
.setParameter("lastBilling", bulkPricingPackage.getNextBillingDate().minusYears(1))
.setParameter(
"lastBilling", minusYears(bulkPricingPackage.getNextBillingDateInstant(), 1))
.getSingleResult();
if (creates > bulkPricingPackage.getMaxCreates()) {
long overage = creates - bulkPricingPackage.getMaxCreates();
@@ -127,7 +129,7 @@ public class CheckBulkComplianceAction implements Runnable {
+ " AND deletionTime = :endOfTime",
Long.class)
.setParameter("token", bulkPricingPackage.getToken())
.setParameter("endOfTime", END_OF_TIME)
.setParameter("endOfTime", END_INSTANT)
.getSingleResult();
if (activeDomains > bulkPricingPackage.getMaxDomains()) {
int overage = Ints.saturatedCast(activeDomains) - bulkPricingPackage.getMaxDomains();

View File

@@ -19,7 +19,6 @@ 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;
import static jakarta.servlet.http.HttpServletResponse.SC_NO_CONTENT;
@@ -43,10 +42,10 @@ import google.registry.request.auth.Auth;
import google.registry.request.lock.LockHandler;
import google.registry.util.Clock;
import jakarta.inject.Inject;
import java.time.Instant;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import org.joda.time.DateTime;
import org.joda.time.Duration;
/**
@@ -125,7 +124,7 @@ public class DeleteExpiredDomainsAction implements Runnable {
}
private void runLocked() {
DateTime runTime = clock.nowUtc();
Instant runTime = clock.now();
logger.atInfo().log(
"Deleting non-renewing domains with autorenew end times up through %s.", runTime);
@@ -134,7 +133,7 @@ public class DeleteExpiredDomainsAction implements Runnable {
() ->
tm().createQueryComposer(Domain.class)
.where("autorenewEndTime", Comparator.LTE, runTime)
.where("deletionTime", Comparator.EQ, END_OF_TIME)
.where("deletionTime", Comparator.EQ, END_INSTANT)
.list());
if (domainsToDelete.isEmpty()) {
logger.atInfo().log("Found 0 domains to delete.");

View File

@@ -27,6 +27,7 @@ import static google.registry.request.Action.Method.POST;
import static google.registry.request.RequestParameters.PARAM_BATCH_SIZE;
import static google.registry.request.RequestParameters.PARAM_DRY_RUN;
import static google.registry.request.RequestParameters.PARAM_TLDS;
import static google.registry.util.DateTimeUtils.toInstant;
import static google.registry.util.RegistryEnvironment.PRODUCTION;
import com.google.common.base.Strings;
@@ -188,8 +189,8 @@ public class DeleteProberDataAction implements Runnable {
.setParameter("tlds", deletableTlds)
.setParameter(
"creationTimeCutoff", CreateAutoTimestamp.create(now.minus(DOMAIN_USED_DURATION)))
.setParameter("nowMinusSoftDeleteDelay", now.minus(SOFT_DELETE_DELAY))
.setParameter("now", now);
.setParameter("nowMinusSoftDeleteDelay", toInstant(now.minus(SOFT_DELETE_DELAY)))
.setParameter("now", toInstant(now));
ImmutableList<Domain> domainList =
query.setMaxResults(batchSize).getResultStream().collect(toImmutableList());
ImmutableList.Builder<String> domainRepoIdsToHardDelete = new ImmutableList.Builder<>();

View File

@@ -19,7 +19,7 @@ import static google.registry.beam.BeamUtils.createJobName;
import static google.registry.model.common.Cursor.CursorType.RECURRING_BILLING;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.request.RequestParameters.PARAM_DRY_RUN;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static google.registry.util.DateTimeUtils.START_INSTANT;
import static jakarta.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
@@ -42,8 +42,9 @@ import google.registry.util.Clock;
import google.registry.util.RegistryEnvironment;
import jakarta.inject.Inject;
import java.io.IOException;
import java.time.Instant;
import java.util.Locale;
import java.util.Optional;
import org.joda.time.DateTime;
/**
* An action that kicks off a {@link ExpandBillingRecurrencesPipeline} dataflow job to expand {@link
@@ -74,11 +75,11 @@ public class ExpandBillingRecurrencesAction implements Runnable {
@Inject
@Parameter(PARAM_START_TIME)
Optional<DateTime> startTimeParam;
Optional<Instant> startTimeParam;
@Inject
@Parameter(PARAM_END_TIME)
Optional<DateTime> endTimeParam;
Optional<Instant> endTimeParam;
@Inject
@Config("projectId")
@@ -102,33 +103,36 @@ public class ExpandBillingRecurrencesAction implements Runnable {
@Override
public void run() {
checkArgument(!(isDryRun && advanceCursor), "Cannot advance the cursor in a dry run.");
DateTime endTime = endTimeParam.orElse(clock.nowUtc());
checkArgument(
!endTime.isAfter(clock.nowUtc()), "End time (%s) must be at or before now", endTime);
DateTime startTime =
Instant endTime = endTimeParam.orElse(clock.now());
checkArgument(!endTime.isAfter(clock.now()), "End time (%s) must be at or before now", endTime);
Instant startTime =
startTimeParam.orElse(
tm().transact(
() ->
tm().loadByKeyIfPresent(Cursor.createGlobalVKey(RECURRING_BILLING))
.orElse(Cursor.createGlobal(RECURRING_BILLING, START_OF_TIME))
.getCursorTime()));
.orElse(Cursor.createGlobal(RECURRING_BILLING, START_INSTANT))
.getCursorTimeInstant()));
checkArgument(
startTime.isBefore(endTime),
String.format("Start time (%s) must be before end time (%s)", startTime, endTime));
"Start time (%s) must be before end time (%s)",
startTime,
endTime);
LaunchFlexTemplateParameter launchParameter =
new LaunchFlexTemplateParameter()
.setJobName(
createJobName(
String.format(
"expand-billing-%s", startTime.toString("yyyy-MM-dd't'HH-mm-ss'z'")),
"expand-billing-%s",
startTime.toString().replace(':', '-').replace('.', '-'))
.toLowerCase(Locale.ROOT),
clock))
.setContainerSpecGcsPath(
String.format("%s/%s_metadata.json", stagingBucketUrl, PIPELINE_NAME))
.setParameters(
new ImmutableMap.Builder<String, String>()
.put("registryEnvironment", RegistryEnvironment.get().name())
.put("startTime", startTime.toString("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"))
.put("endTime", endTime.toString("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"))
.put("startTime", startTime.toString())
.put("endTime", endTime.toString())
.put("isDryRun", Boolean.toString(isDryRun))
.put("advanceCursor", Boolean.toString(advanceCursor))
.build());

View File

@@ -21,10 +21,12 @@ import static google.registry.model.domain.Period.Unit.YEARS;
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_AUTORENEW;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.CollectionUtils.union;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static google.registry.util.DateTimeUtils.START_INSTANT;
import static google.registry.util.DateTimeUtils.earliestOf;
import static google.registry.util.DateTimeUtils.latestOf;
import static google.registry.util.DateTimeUtils.toInstant;
import static google.registry.util.DateTimeUtils.minusYears;
import static google.registry.util.DateTimeUtils.plusYears;
import static google.registry.util.DateTimeUtils.toDateTime;
import static org.apache.beam.sdk.values.TypeDescriptors.voids;
import com.google.common.collect.ImmutableMap;
@@ -54,6 +56,8 @@ import google.registry.util.Clock;
import google.registry.util.SystemClock;
import jakarta.inject.Singleton;
import java.io.Serializable;
import java.time.Duration;
import java.time.Instant;
import java.util.Optional;
import java.util.Set;
import org.apache.beam.sdk.Pipeline;
@@ -73,7 +77,6 @@ import org.apache.beam.sdk.transforms.Wait;
import org.apache.beam.sdk.values.KV;
import org.apache.beam.sdk.values.PCollection;
import org.apache.beam.sdk.values.PDone;
import org.joda.time.DateTime;
/**
* Definition of a Dataflow Flex pipeline template, which expands {@link BillingRecurrence} to
@@ -134,9 +137,9 @@ public class ExpandBillingRecurrencesPipeline implements Serializable {
}
// Inclusive lower bound of the expansion window.
private final DateTime startTime;
private final Instant startTime;
// Exclusive lower bound of the expansion window.
private final DateTime endTime;
private final Instant endTime;
private final boolean isDryRun;
private final boolean advanceCursor;
private final Counter recurrencesInScopeCounter =
@@ -151,14 +154,14 @@ public class ExpandBillingRecurrencesPipeline implements Serializable {
Metrics.counter("ExpandBilling", "OneTimes that would be expanded");
ExpandBillingRecurrencesPipeline(ExpandBillingRecurrencesPipelineOptions options, Clock clock) {
startTime = DateTime.parse(options.getStartTime());
endTime = DateTime.parse(options.getEndTime());
checkArgument(
!endTime.isAfter(clock.nowUtc()),
String.format("End time %s must be at or before now.", endTime));
startTime = Instant.parse(options.getStartTime());
endTime = Instant.parse(options.getEndTime());
checkArgument(!endTime.isAfter(clock.now()), "End time %s must be at or before now.", endTime);
checkArgument(
startTime.isBefore(endTime),
String.format("[%s, %s) is not a valid window of operation.", startTime, endTime));
"[%s, %s) is not a valid window of operation.",
startTime,
endTime);
isDryRun = options.getIsDryRun();
advanceCursor = options.getAdvanceCursor();
}
@@ -199,7 +202,7 @@ public class ExpandBillingRecurrencesPipeline implements Serializable {
"startTime",
startTime,
"oneYearAgo",
endTime.minusYears(1)),
minusYears(endTime, 1)),
true,
(Long id) -> {
recurrencesInScopeCounter.inc();
@@ -268,18 +271,18 @@ public class ExpandBillingRecurrencesPipeline implements Serializable {
// The best way to handle any unexpected behavior is to simply drop the recurrence from
// expansion, if its new state still calls for an expansion, it would be picked up the next time
// the pipeline runs.
ImmutableSet<DateTime> eventTimes;
ImmutableSet<Instant> eventTimes;
try {
eventTimes =
ImmutableSet.copyOf(
billingRecurrence
.getRecurrenceTimeOfYear()
.getInstancesInRange(
.getInstancesInRangeInstant(
Range.closedOpen(
latestOf(
billingRecurrence.getRecurrenceLastExpansion().plusYears(1),
plusYears(billingRecurrence.getRecurrenceLastExpansionInstant(), 1),
startTime),
earliestOf(billingRecurrence.getRecurrenceEndTime(), endTime))));
earliestOf(billingRecurrence.getRecurrenceEndTimeInstant(), endTime))));
} catch (IllegalArgumentException e) {
return;
}
@@ -289,28 +292,29 @@ public class ExpandBillingRecurrencesPipeline implements Serializable {
// Find the times for which the OneTime billing event are already created, making this expansion
// idempotent. There is no need to match to the domain repo ID as the cancellation matching
// billing event itself can only be for a single domain.
ImmutableSet<DateTime> existingEventTimes =
ImmutableSet<Instant> existingEventTimes =
ImmutableSet.copyOf(
tm().query(
"SELECT eventTime FROM BillingEvent WHERE cancellationMatchingBillingEvent ="
+ " :key",
DateTime.class)
Instant.class)
.setParameter("key", billingRecurrence.createVKey())
.getResultList());
Set<DateTime> eventTimesToExpand = difference(eventTimes, existingEventTimes);
Set<Instant> eventTimesToExpand = difference(eventTimes, existingEventTimes);
if (eventTimesToExpand.isEmpty()) {
return;
}
DateTime recurrenceLastExpansionTime = billingRecurrence.getRecurrenceLastExpansion();
Instant recurrenceLastExpansionTime = billingRecurrence.getRecurrenceLastExpansionInstant();
// Create new OneTime and DomainHistory for EventTimes that needs to be expanded.
for (DateTime eventTime : eventTimesToExpand) {
for (Instant eventTime : eventTimesToExpand) {
recurrenceLastExpansionTime = latestOf(recurrenceLastExpansionTime, eventTime);
oneTimesToExpandCounter.inc();
DateTime billingTime = eventTime.plus(tld.getAutoRenewGracePeriodLength());
Instant billingTime =
eventTime.plus(Duration.ofMillis(tld.getAutoRenewGracePeriodLength().getMillis()));
// Note that the DomainHistory is created as of transaction time, as opposed to event time.
// This might be counterintuitive because other DomainHistories are created at the time
// mutation events occur, such as in DomainDeleteFlow or DomainRenewFlow. Therefore, it is
@@ -337,7 +341,7 @@ public class ExpandBillingRecurrencesPipeline implements Serializable {
new DomainHistory.Builder()
.setBySuperuser(false)
.setRegistrarId(billingRecurrence.getRegistrarId())
.setModificationTime(tm().getTransactionTime())
.setModificationTime(tm().getTxTime())
.setDomain(domain)
.setPeriod(Period.create(1, YEARS))
.setReason("Domain autorenewal by ExpandRecurringBillingEventsPipeline")
@@ -373,7 +377,7 @@ public class ExpandBillingRecurrencesPipeline implements Serializable {
// during ARGP).
//
// See: DomainFlowUtils#createCancellingRecords
domain.getDeletionTime().isBefore(toInstant(billingTime))
domain.getDeletionTime().isBefore(billingTime)
? ImmutableSet.of()
: ImmutableSet.of(
DomainTransactionRecord.create(
@@ -398,7 +402,7 @@ public class ExpandBillingRecurrencesPipeline implements Serializable {
.getRenewPrice(
tld,
billingRecurrence.getTargetId(),
eventTime,
toDateTime(eventTime),
1,
billingRecurrence,
Optional.empty())
@@ -439,12 +443,12 @@ public class ExpandBillingRecurrencesPipeline implements Serializable {
public void processElement() {
tm().transact(
() -> {
DateTime currentCursorTime =
Instant currentCursorTime =
tm().loadByKeyIfPresent(
Cursor.createGlobalVKey(RECURRING_BILLING))
.orElse(
Cursor.createGlobal(RECURRING_BILLING, START_OF_TIME))
.getCursorTime();
Cursor.createGlobal(RECURRING_BILLING, START_INSTANT))
.getCursorTimeInstant();
if (!currentCursorTime.equals(startTime)) {
throw new IllegalStateException(
String.format(

View File

@@ -133,7 +133,7 @@ public class RdeIO {
private final byte[] stagingKeyBytes;
private final RdeMarshaller marshaller;
protected RdeWriter(
RdeWriter(
GcsUtils gcsUtils,
String rdeBucket,
byte[] stagingKeyBytes,
@@ -144,7 +144,7 @@ public class RdeIO {
this.marshaller = new RdeMarshaller(validationMode);
}
@SuppressWarnings("unused")
@SuppressWarnings({"EffectivelyPrivate", "unused"})
@Setup
public void setup() {
Security.addProvider(new BouncyCastleProvider());

View File

@@ -27,6 +27,7 @@ import static google.registry.beam.rde.RdePipeline.TupleTags.REVISION_ID;
import static google.registry.beam.rde.RdePipeline.TupleTags.SUPERORDINATE_DOMAINS;
import static google.registry.model.reporting.HistoryEntryDao.RESOURCE_TYPES_TO_HISTORY_TYPES;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.DateTimeUtils.toInstant;
import static google.registry.util.SafeSerializationUtils.safeDeserializeCollection;
import static google.registry.util.SafeSerializationUtils.serializeCollection;
import static google.registry.util.SerializeUtils.decodeBase64;
@@ -314,17 +315,16 @@ public class RdePipeline implements Serializable {
String.format("Load most recent %s", historyClass.getSimpleName()),
RegistryJpaIO.read(
("SELECT repoId, revisionId FROM %entity% WHERE (repoId, modificationTime) IN"
+ " (SELECT repoId, MAX(modificationTime) FROM %entity% WHERE"
+ " modificationTime <= :watermark GROUP BY repoId) AND resource.deletionTime"
+ " > :watermark AND COALESCE(resource.creationRegistrarId, '') NOT LIKE"
+ " 'prober-%' AND COALESCE(resource.currentSponsorRegistrarId, '') NOT LIKE"
+ " 'prober-%' AND COALESCE(resource.lastEppUpdateRegistrarId, '') NOT LIKE"
+ " 'prober-%' "
+ " (SELECT repoId, MAX(modificationTime) FROM %entity% WHERE modificationTime"
+ " <= :watermark GROUP BY repoId) AND resource.deletionTime > :watermark AND"
+ " COALESCE(resource.creationRegistrarId, '') NOT LIKE 'prober-%' AND"
+ " COALESCE(resource.currentSponsorRegistrarId, '') NOT LIKE 'prober-%' AND"
+ " COALESCE(resource.lastEppUpdateRegistrarId, '') NOT LIKE 'prober-%' "
+ (historyClass == DomainHistory.class
? "AND resource.tld IN " + "(SELECT id FROM Tld WHERE tldType = 'REAL')"
: ""))
.replace("%entity%", historyClass.getSimpleName()),
ImmutableMap.of("watermark", watermark),
ImmutableMap.of("watermark", toInstant(watermark)),
Object[].class,
row -> KV.of((String) row[0], (long) row[1]))
.withCoder(KvCoder.of(StringUtf8Coder.of(), VarLongCoder.of())));

View File

@@ -16,6 +16,7 @@ package google.registry.beam.resave;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.DateTimeUtils.END_INSTANT;
import static org.apache.beam.sdk.values.TypeDescriptors.integers;
import com.google.common.collect.ImmutableList;
@@ -30,7 +31,6 @@ import google.registry.model.domain.DomainBase;
import google.registry.model.host.Host;
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
import google.registry.persistence.VKey;
import google.registry.util.DateTimeUtils;
import java.io.Serializable;
import org.apache.beam.sdk.Pipeline;
import org.apache.beam.sdk.PipelineResult;
@@ -74,7 +74,7 @@ public class ResaveAllEppResourcesPipeline implements Serializable {
"SELECT repoId FROM Domain d WHERE (d.transferData.transferStatus = 'PENDING' AND"
+ " d.transferData.pendingTransferExpirationTime < current_timestamp()) OR"
+ " (d.registrationExpirationTime < current_timestamp() AND d.deletionTime ="
+ " (:END_OF_TIME)) OR (EXISTS (SELECT 1 FROM GracePeriod gp WHERE gp.domainRepoId ="
+ " (:END_INSTANT)) OR (EXISTS (SELECT 1 FROM GracePeriod gp WHERE gp.domainRepoId ="
+ " d.repoId AND gp.expirationTime < current_timestamp()))";
private final ResaveAllEppResourcesPipelineOptions options;
@@ -108,7 +108,7 @@ public class ResaveAllEppResourcesPipeline implements Serializable {
Read<String, String> repoIdRead =
RegistryJpaIO.read(
DOMAINS_TO_PROJECT_QUERY,
ImmutableMap.of("END_OF_TIME", DateTimeUtils.END_OF_TIME),
ImmutableMap.of("END_INSTANT", END_INSTANT),
String.class,
r -> r)
.withCoder(StringUtf8Coder.of());
@@ -154,9 +154,7 @@ public class ResaveAllEppResourcesPipeline implements Serializable {
}
@ProcessElement
public void processElement(
@Element KV<ShardedKey<Integer>, Iterable<String>> element,
OutputReceiver<Void> outputReceiver) {
public void processElement(@Element KV<ShardedKey<Integer>, Iterable<String>> element) {
tm().transact(
() -> {
DateTime now = tm().getTransactionTime();

View File

@@ -210,8 +210,8 @@ public class Spec11Pipeline implements Serializable {
String registrarId = kv.getKey();
checkArgument(
kv.getValue().iterator().hasNext(),
String.format(
"Registrar with ID %s had no corresponding threats", registrarId));
"Registrar with ID %s had no corresponding threats",
registrarId);
String email = kv.getValue().iterator().next().email();
JSONObject output = new JSONObject();
try {

View File

@@ -29,8 +29,8 @@ public final class BigqueryJobFailureException extends RuntimeException {
/** Delegate {@link IOException} errors, checking for {@link GoogleJsonResponseException} */
public static BigqueryJobFailureException create(IOException cause) {
if (cause instanceof GoogleJsonResponseException) {
return create((GoogleJsonResponseException) cause);
if (cause instanceof GoogleJsonResponseException googleJsonResponseException) {
return create(googleJsonResponseException);
} else {
return new BigqueryJobFailureException(cause.getMessage(), cause, null, null);
}

View File

@@ -62,7 +62,7 @@ public class BlockListFetcher {
// TODO: use more informative exceptions to describe retriable errors
return retrier.callWithRetry(
() -> tryFetch(blockListType),
e -> e instanceof BsaException && ((BsaException) e).isRetriable());
e -> e instanceof BsaException bsaException && bsaException.isRetriable());
}
LazyBlockList tryFetch(BlockListType blockListType) {

View File

@@ -36,9 +36,9 @@ import google.registry.request.auth.Auth;
import google.registry.util.BatchedStreams;
import google.registry.util.Clock;
import jakarta.inject.Inject;
import java.time.Duration;
import java.util.Optional;
import java.util.stream.Stream;
import org.joda.time.Duration;
@Action(
service = Action.Service.BACKEND,
@@ -67,7 +67,7 @@ public class BsaRefreshAction implements Runnable {
GcsClient gcsClient,
BsaReportSender bsaReportSender,
@Config("bsaTxnBatchSize") int transactionBatchSize,
@Config("domainCreateTxnCommitTimeLag") Duration domainCreateTxnCommitTimeLag,
@Config("domainCreateTxnCommitTimeLag") org.joda.time.Duration domainCreateTxnCommitTimeLag,
BsaEmailSender emailSender,
BsaLock bsaLock,
Clock clock,
@@ -76,7 +76,7 @@ public class BsaRefreshAction implements Runnable {
this.gcsClient = gcsClient;
this.bsaReportSender = bsaReportSender;
this.transactionBatchSize = transactionBatchSize;
this.domainCreateTxnCommitTimeLag = domainCreateTxnCommitTimeLag;
this.domainCreateTxnCommitTimeLag = Duration.ofMillis(domainCreateTxnCommitTimeLag.getMillis());
this.emailSender = emailSender;
this.bsaLock = bsaLock;
this.clock = clock;
@@ -103,7 +103,7 @@ public class BsaRefreshAction implements Runnable {
/** Executes the refresh action while holding the BSA lock. */
Void runWithinLock() {
// Cannot enroll new TLDs after download starts. This may change if b/309175410 is fixed.
if (!Tlds.hasActiveBsaEnrollment(clock.nowUtc())) {
if (!Tlds.hasActiveBsaEnrollment(clock.now())) {
logger.atInfo().log("No TLDs enrolled with BSA. Quitting.");
return null;
}
@@ -116,7 +116,7 @@ public class BsaRefreshAction implements Runnable {
DomainsRefresher refresher =
new DomainsRefresher(
schedule.prevRefreshTime(),
clock.nowUtc(),
clock.now(),
domainCreateTxnCommitTimeLag,
transactionBatchSize);
switch (schedule.stage()) {

View File

@@ -57,11 +57,11 @@ import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
import jakarta.inject.Inject;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
import org.joda.time.DateTime;
import org.joda.time.Duration;
/** Validates the BSA data in the database against the most recent block lists. */
@Action(
@@ -88,14 +88,14 @@ public class BsaValidateAction implements Runnable {
IdnChecker idnChecker,
BsaEmailSender emailSender,
@Config("bsaTxnBatchSize") int transactionBatchSize,
@Config("bsaValidationMaxStaleness") Duration maxStaleness,
@Config("bsaValidationMaxStaleness") org.joda.time.Duration maxStaleness,
Clock clock,
Response response) {
this.gcsClient = gcsClient;
this.idnChecker = idnChecker;
this.emailSender = emailSender;
this.transactionBatchSize = transactionBatchSize;
this.maxStaleness = maxStaleness;
this.maxStaleness = Duration.ofMillis(maxStaleness.getMillis());
this.clock = clock;
this.response = response;
}
@@ -186,7 +186,7 @@ public class BsaValidateAction implements Runnable {
ForeignKeyUtils.loadResources(
Domain.class,
batch.stream().map(UnblockableDomain::domainName).collect(toImmutableList()),
clock.nowUtc());
clock.now());
for (var unblockable : batch) {
verifyDomainStillUnblockableWithReason(unblockable, activeDomains).ifPresent(errors::add);
}
@@ -199,7 +199,7 @@ public class BsaValidateAction implements Runnable {
Optional<String> verifyDomainStillUnblockableWithReason(
UnblockableDomain domain, ImmutableMap<String, Domain> activeDomains) {
DateTime now = clock.nowUtc();
Instant now = clock.now();
boolean isRegistered = activeDomains.containsKey(domain.domainName());
boolean isReserved = isReservedDomain(domain.domainName(), now);
InternetDomainName domainName = InternetDomainName.from(domain.domainName());
@@ -228,7 +228,7 @@ public class BsaValidateAction implements Runnable {
}
boolean isStalenessAllowed(Domain domain) {
return domain.getCreationTime().plus(maxStaleness).isAfter(clock.nowUtc());
return domain.getCreationTimeInstant().plus(maxStaleness).isAfter(clock.now());
}
/** Returns unique labels across all block lists in the download specified by {@code jobName}. */
@@ -262,20 +262,20 @@ public class BsaValidateAction implements Runnable {
}
ImmutableList<String> checkMissingUnblockableDomains() {
DateTime now = clock.nowUtc();
Instant now = clock.now();
ImmutableList.Builder<String> errors = new ImmutableList.Builder<>();
errors.addAll(checkForMissingReservedUnblockables(now));
errors.addAll(checkForMissingRegisteredUnblockables(now));
return errors.build();
}
ImmutableList<String> checkForMissingRegisteredUnblockables(DateTime now) {
ImmutableList<String> checkForMissingRegisteredUnblockables(Instant now) {
ImmutableList.Builder<String> errors = new ImmutableList.Builder<>();
ImmutableList<Tld> bsaEnabledTlds =
getTldEntitiesOfType(TldType.REAL).stream()
.filter(tld -> isEnrolledWithBsa(tld, now))
.collect(toImmutableList());
DateTime stalenessThreshold = now.minus(maxStaleness);
Instant stalenessThreshold = now.minus(maxStaleness);
bsaEnabledTlds.stream()
.map(Tld::getTldStr)
.map(tld -> bsaQuery(() -> queryMissedRegisteredUnblockables(tld, now)))
@@ -290,7 +290,7 @@ public class BsaValidateAction implements Runnable {
return errors.build();
}
ImmutableList<String> checkForMissingReservedUnblockables(DateTime now) {
ImmutableList<String> checkForMissingReservedUnblockables(Instant now) {
ImmutableList.Builder<String> errors = new ImmutableList.Builder<>();
try (Stream<ImmutableList<String>> reservedNames =
toBatches(

View File

@@ -20,6 +20,7 @@ import static google.registry.bsa.BsaStringUtils.DOMAIN_JOINER;
import static google.registry.flows.domain.DomainFlowUtils.isReserved;
import static google.registry.model.tld.Tlds.findTldForName;
import static google.registry.model.tld.label.ReservedList.loadReservedLists;
import static google.registry.util.DateTimeUtils.toInstant;
import com.google.common.collect.ImmutableSet;
import com.google.common.net.InternetDomainName;
@@ -28,6 +29,7 @@ import google.registry.model.tld.Tld.TldState;
import google.registry.model.tld.Tld.TldType;
import google.registry.model.tld.Tlds;
import google.registry.model.tld.label.ReservedList;
import java.time.Instant;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -45,7 +47,16 @@ public final class ReservedDomainsUtils {
private ReservedDomainsUtils() {}
/**
* @deprecated Use {@link #getAllReservedNames(Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static Stream<String> getAllReservedNames(DateTime now) {
return getAllReservedNames(toInstant(now));
}
public static Stream<String> getAllReservedNames(Instant now) {
return Tlds.getTldEntitiesOfType(TldType.REAL).stream()
.filter(tld -> Tld.isEnrolledWithBsa(tld, now))
.map(tld -> getAllReservedDomainsInTld(tld, now))
@@ -53,7 +64,7 @@ public final class ReservedDomainsUtils {
}
/** Returns all reserved domains in a given {@code tld} as of {@code now}. */
static ImmutableSet<String> getAllReservedDomainsInTld(Tld tld, DateTime now) {
static ImmutableSet<String> getAllReservedDomainsInTld(Tld tld, Instant now) {
return loadReservedLists(tld.getReservedListNames()).stream()
.map(ReservedList::getReservedListEntries)
.map(Map::keySet)
@@ -63,11 +74,20 @@ public final class ReservedDomainsUtils {
.collect(toImmutableSet());
}
/**
* @deprecated Use {@link #getAllReservedDomainsInTld(Tld, Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
static ImmutableSet<String> getAllReservedDomainsInTld(Tld tld, DateTime now) {
return getAllReservedDomainsInTld(tld, toInstant(now));
}
/**
* Returns true if {@code domain} is a reserved name that can be registered right now (e.g.,
* during sunrise or with allocation token), therefore unblockable.
*/
public static boolean isReservedDomain(String domain, DateTime now) {
public static boolean isReservedDomain(String domain, Instant now) {
Optional<InternetDomainName> tldStr = findTldForName(InternetDomainName.from(domain));
verify(tldStr.isPresent(), "Tld for domain [%s] unexpectedly missing.", domain);
Tld tld = Tld.get(tldStr.get().toString());
@@ -75,4 +95,13 @@ public final class ReservedDomainsUtils {
InternetDomainName.from(domain),
Objects.equals(tld.getTldState(now), TldState.START_DATE_SUNRISE));
}
/**
* @deprecated Use {@link #isReservedDomain(String, Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static boolean isReservedDomain(String domain, DateTime now) {
return isReservedDomain(domain, toInstant(now));
}
}

View File

@@ -54,6 +54,7 @@ import java.io.OutputStreamWriter;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.Writer;
import java.time.Instant;
import java.util.Optional;
import java.util.zip.GZIPOutputStream;
import okhttp3.MediaType;
@@ -65,7 +66,6 @@ import okhttp3.Response;
import okio.BufferedSink;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joda.time.DateTime;
/**
* Daily action that uploads unavailable domain names on applicable TLDs to BSA.
@@ -122,7 +122,7 @@ public class UploadBsaUnavailableDomainsAction implements Runnable {
public void run() {
// TODO(mcilwain): Implement a date Cursor, have the cronjob run frequently, and short-circuit
// the run if the daily upload is already completed.
DateTime runTime = clock.nowUtc();
Instant runTime = clock.now();
ImmutableSortedSet<String> unavailableDomains = getUnavailableDomains(runTime);
if (unavailableDomains.isEmpty()) {
logger.atWarning().log("No unavailable domains found; terminating.");
@@ -141,7 +141,7 @@ public class UploadBsaUnavailableDomainsAction implements Runnable {
}
/** Uploads the unavailable domains list to GCS in the unavailable domains bucket. */
boolean uploadToGcs(ImmutableSortedSet<String> unavailableDomains, DateTime runTime) {
boolean uploadToGcs(ImmutableSortedSet<String> unavailableDomains, Instant runTime) {
logger.atInfo().log("Uploading unavailable names file to GCS in bucket %s", gcsBucket);
BlobId blobId = BlobId.of(gcsBucket, createFilename(runTime));
// `gcsUtils.openOutputStream` returns a buffered stream
@@ -159,7 +159,7 @@ public class UploadBsaUnavailableDomainsAction implements Runnable {
}
}
boolean uploadToBsa(ImmutableSortedSet<String> unavailableDomains, DateTime runTime) {
boolean uploadToBsa(ImmutableSortedSet<String> unavailableDomains, Instant runTime) {
try {
Hasher sha512Hasher = Hashing.sha512().newHasher();
unavailableDomains.stream()
@@ -211,11 +211,11 @@ public class UploadBsaUnavailableDomainsAction implements Runnable {
}
}
private static String createFilename(DateTime runTime) {
private static String createFilename(Instant runTime) {
return String.format("unavailable_domains_%s.txt", runTime.toString());
}
private ImmutableSortedSet<String> getUnavailableDomains(DateTime runTime) {
private ImmutableSortedSet<String> getUnavailableDomains(Instant runTime) {
// Get list of TLDs to process.
ImmutableSet<Tld> bsaEnabledTlds =
getTldEntitiesOfType(TldType.REAL).stream()

View File

@@ -71,19 +71,19 @@ public class BsaReportSender {
public void sendOrderStatusReport(String payload) {
retrier.callWithRetry(
() -> trySendData(this.orderStatusUrl, payload),
e -> e instanceof BsaException && ((BsaException) e).isRetriable());
e -> e instanceof BsaException bsaException && bsaException.isRetriable());
}
public void addUnblockableDomainsUpdates(String payload) {
retrier.callWithRetry(
() -> trySendData(this.addUnblockableDomainsUrl, payload),
e -> e instanceof BsaException && ((BsaException) e).isRetriable());
e -> e instanceof BsaException bsaException && bsaException.isRetriable());
}
public void removeUnblockableDomainsUpdates(String payload) {
retrier.callWithRetry(
() -> trySendData(this.removeUnblockableDomainsUrl, payload),
e -> e instanceof BsaException && ((BsaException) e).isRetriable());
e -> e instanceof BsaException bsaException && bsaException.isRetriable());
}
Void trySendData(String urlString, String payload) {

View File

@@ -29,7 +29,7 @@ import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import org.joda.time.DateTime;
import java.time.Instant;
/**
* Records of completed and ongoing refresh actions, which recomputes the set of unblockable domains
@@ -46,10 +46,10 @@ class BsaDomainRefresh {
Long jobId;
@Column(nullable = false)
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create((Instant) null);
@Column(nullable = false)
UpdateAutoTimestamp updateTime = UpdateAutoTimestamp.create(null);
UpdateAutoTimestamp updateTime = UpdateAutoTimestamp.create((Instant) null);
@Column(nullable = false)
@Enumerated(EnumType.STRING)
@@ -61,7 +61,7 @@ class BsaDomainRefresh {
return jobId;
}
DateTime getCreationTime() {
Instant getCreationTime() {
return creationTime.getTimestamp();
}

View File

@@ -17,6 +17,7 @@ package google.registry.bsa.persistence;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static google.registry.bsa.DownloadStage.DONE;
import static google.registry.bsa.DownloadStage.DOWNLOAD_BLOCK_LISTS;
import static google.registry.util.DateTimeUtils.ISO_8601_FORMATTER;
import com.google.common.base.Joiner;
import com.google.common.base.Objects;
@@ -37,8 +38,8 @@ import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import java.time.Instant;
import java.util.Locale;
import org.joda.time.DateTime;
/** Records of ongoing and completed download jobs. */
@Entity
@@ -53,10 +54,10 @@ class BsaDownload {
Long jobId;
@Column(nullable = false)
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create((Instant) null);
@Column(nullable = false)
UpdateAutoTimestamp updateTime = UpdateAutoTimestamp.create(null);
UpdateAutoTimestamp updateTime = UpdateAutoTimestamp.create((Instant) null);
@Column(nullable = false)
String blockListChecksums = "";
@@ -71,7 +72,7 @@ class BsaDownload {
return jobId;
}
DateTime getCreationTime() {
Instant getCreationTime() {
return creationTime.getTimestamp();
}
@@ -83,7 +84,7 @@ class BsaDownload {
*/
String getJobName() {
// Return a value based on job start time, which is unique.
return getCreationTime().toString().toLowerCase(Locale.ROOT).replace(":", "");
return ISO_8601_FORMATTER.format(getCreationTime()).toLowerCase(Locale.ROOT).replace(":", "");
}
boolean isDone() {

View File

@@ -19,7 +19,7 @@ import google.registry.persistence.VKey;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import org.joda.time.DateTime;
import java.time.Instant;
/**
* Specifies a second-level TLD name that should be blocked from registration in all TLDs except by
@@ -41,12 +41,12 @@ final class BsaLabel {
*/
@SuppressWarnings("unused")
@Column(nullable = false)
DateTime creationTime;
Instant creationTime;
// For Hibernate.
private BsaLabel() {}
BsaLabel(String label, DateTime creationTime) {
BsaLabel(String label, Instant creationTime) {
this.label = label;
this.creationTime = creationTime;
}

View File

@@ -31,6 +31,7 @@ import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import jakarta.persistence.IdClass;
import java.io.Serializable;
import java.time.Instant;
/** A domain matching a BSA label but is in use (registered or reserved), so cannot be blocked. */
@Entity
@@ -52,7 +53,7 @@ class BsaUnblockableDomain {
*/
@SuppressWarnings("unused")
@Column(nullable = false)
CreateAutoTimestamp createTime = CreateAutoTimestamp.create(null);
CreateAutoTimestamp createTime = CreateAutoTimestamp.create((Instant) null);
// For Hibernate
BsaUnblockableDomain() {}

View File

@@ -45,14 +45,13 @@ import google.registry.model.domain.Domain;
import google.registry.model.tld.Tld;
import google.registry.model.tld.Tld.TldType;
import google.registry.util.BatchedStreams;
import java.time.Instant;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Stream;
import org.joda.time.DateTime;
import org.joda.time.Duration;
/**
* Rechecks {@link BsaUnblockableDomain the registered/reserved domain names} in the database for
@@ -89,14 +88,14 @@ import org.joda.time.Duration;
*/
public final class DomainsRefresher {
private final DateTime prevRefreshStartTime;
private final Instant prevRefreshStartTime;
private final int transactionBatchSize;
private final DateTime now;
private final Instant now;
public DomainsRefresher(
DateTime prevRefreshStartTime,
DateTime now,
Duration domainTxnMaxDuration,
Instant prevRefreshStartTime,
Instant now,
java.time.Duration domainTxnMaxDuration,
int transactionBatchSize) {
this.prevRefreshStartTime = prevRefreshStartTime.minus(domainTxnMaxDuration);
this.now = now;
@@ -249,7 +248,7 @@ public final class DomainsRefresher {
}
static ImmutableSet<String> getNewlyCreatedUnblockables(
DateTime prevRefreshStartTime, DateTime now) {
Instant prevRefreshStartTime, Instant now) {
ImmutableSet<String> bsaEnabledTlds =
getTldEntitiesOfType(TldType.REAL).stream()
.filter(tld -> isEnrolledWithBsa(tld, now))
@@ -260,7 +259,7 @@ public final class DomainsRefresher {
return getBlockedDomainNames(liveDomains);
}
static ImmutableSet<String> getAllReservedUnblockables(DateTime now, int batchSize) {
static ImmutableSet<String> getAllReservedUnblockables(Instant now, int batchSize) {
Stream<String> allReserved = getAllReservedNames(now);
return BatchedStreams.toBatches(allReserved, batchSize)
.map(DomainsRefresher::getBlockedDomainNames)

View File

@@ -24,8 +24,8 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
import com.google.common.collect.ImmutableMap;
import google.registry.bsa.BlockListType;
import google.registry.bsa.DownloadStage;
import java.time.Instant;
import java.util.Optional;
import org.joda.time.DateTime;
/**
* Information needed when handling a download from BSA.
@@ -36,7 +36,7 @@ import org.joda.time.DateTime;
*/
public record DownloadSchedule(
long jobId,
DateTime jobCreationTime,
Instant jobCreationTime,
String jobName,
DownloadStage stage,
Optional<CompletedJob> latestCompleted,

View File

@@ -115,7 +115,11 @@ public final class DownloadScheduler {
}
private boolean isTimeAgain(BsaDownload mostRecent, Duration interval) {
return mostRecent.getCreationTime().plus(interval).minus(CRON_JITTER).isBefore(clock.nowUtc());
return mostRecent
.getCreationTime()
.plus(java.time.Duration.ofMillis(interval.getMillis()))
.minus(java.time.Duration.ofMillis(CRON_JITTER.getMillis()))
.isBefore(clock.now());
}
/**

View File

@@ -19,19 +19,16 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
import static google.registry.bsa.BsaStringUtils.DOMAIN_SPLITTER;
import static google.registry.bsa.BsaTransactions.bsaQuery;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static org.joda.time.DateTimeZone.UTC;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import google.registry.bsa.api.UnblockableDomain;
import google.registry.model.CreateAutoTimestamp;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.joda.time.DateTime;
/** Helpers for querying BSA JPA entities. */
public final class Queries {
@@ -135,15 +132,15 @@ public final class Queries {
}
static ImmutableSet<String> queryNewlyCreatedDomains(
ImmutableCollection<String> tlds, DateTime minCreationTime, DateTime now) {
ImmutableCollection<String> tlds, Instant minCreationTime, Instant now) {
return ImmutableSet.copyOf(
tm().getEntityManager()
.createQuery(
"SELECT domainName FROM Domain WHERE creationTime >= :minCreationTime "
"SELECT domainName FROM Domain WHERE creationTime.creationTime >= :minCreationTime "
+ "AND deletionTime > :now "
+ "AND tld in (:tlds)",
String.class)
.setParameter("minCreationTime", CreateAutoTimestamp.create(minCreationTime))
.setParameter("minCreationTime", minCreationTime)
.setParameter("now", now)
.setParameter("tlds", tlds)
.getResultList());
@@ -156,44 +153,36 @@ public final class Queries {
* @return The missing unblockables and their creation and deletion time.
*/
public static ImmutableList<DomainLifeSpan> queryMissedRegisteredUnblockables(
String tld, DateTime now) {
String tld, Instant now) {
String sqlTemplate =
"""
SELECT l.domain_name, creation_time, deletion_time
FROM
(SELECT d.domain_name, d.creation_time, d.deletion_time
FROM
"Domain" d
JOIN
(SELECT concat(label, '.', :tld) AS domain_name from "BsaLabel") b
ON b.domain_name = d.domain_name
WHERE deletion_time > :now) l
LEFT OUTER JOIN
(SELECT concat(label, '.', tld) as domain_name
FROM "BsaUnblockableDomain"
WHERE tld = :tld and reason = 'REGISTERED') r
ON l.domain_name = r.domain_name
WHERE r.domain_name is null;
""";
SELECT l.domain_name, creation_time, deletion_time
FROM
(SELECT d.domain_name, d.creation_time, d.deletion_time
FROM
"Domain" d
JOIN
(SELECT concat(label, '.', :tld) AS domain_name from "BsaLabel") b
ON b.domain_name = d.domain_name
WHERE deletion_time > :now) l
LEFT OUTER JOIN
(SELECT concat(label, '.', tld) as domain_name
FROM "BsaUnblockableDomain"
WHERE tld = :tld and reason = 'REGISTERED') r
ON l.domain_name = r.domain_name
WHERE r.domain_name is null;
""";
return ((Stream<?>)
tm().getEntityManager()
.createNativeQuery(sqlTemplate)
.setParameter("tld", tld)
.setParameter("now", Instant.ofEpochMilli(now.getMillis()))
.setParameter("now", now)
.getResultStream())
.map(Object[].class::cast)
.map(
row ->
new DomainLifeSpan(
(String) row[0], toDateTime((Instant) row[1]), toDateTime((Instant) row[2])))
.map(row -> new DomainLifeSpan((String) row[0], (Instant) row[1], (Instant) row[2]))
.collect(toImmutableList());
}
// For testing convenience: 'assertEquals' fails between `new DateTime(timestamp)` and below.
static DateTime toDateTime(Instant timestamp) {
return new DateTime(timestamp.toEpochMilli(), UTC);
}
public record DomainLifeSpan(String domainName, DateTime creationTime, DateTime deletionTime) {}
public record DomainLifeSpan(String domainName, Instant creationTime, Instant deletionTime) {}
}

View File

@@ -19,7 +19,7 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import google.registry.bsa.RefreshStage;
import org.joda.time.DateTime;
import java.time.Instant;
/**
* Information needed when handling a domain refresh.
@@ -28,10 +28,10 @@ import org.joda.time.DateTime;
*/
public record RefreshSchedule(
long jobId,
DateTime jobCreationTime,
Instant jobCreationTime,
String jobName,
RefreshStage stage,
DateTime prevRefreshTime) {
Instant prevRefreshTime) {
/** Updates the current job to the new stage. */
@CanIgnoreReturnValue
@@ -50,7 +50,7 @@ public record RefreshSchedule(
});
}
static RefreshSchedule create(BsaDomainRefresh job, DateTime prevJobCreationTime) {
static RefreshSchedule create(BsaDomainRefresh job, Instant prevJobCreationTime) {
return new RefreshSchedule(
job.getJobId(),
job.getCreationTime(),

View File

@@ -20,8 +20,8 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import jakarta.inject.Inject;
import java.time.Instant;
import java.util.Optional;
import org.joda.time.DateTime;
/** Assigns work for each cron invocation of domain refresh job. */
public class RefreshScheduler {
@@ -56,7 +56,7 @@ public class RefreshScheduler {
return Optional.empty();
}
DateTime prevDownloadTime = mostRecentDownload.get().getCreationTime();
Instant prevDownloadTime = mostRecentDownload.get().getCreationTime();
if (recentJobs.isEmpty()) {
return Optional.of(scheduleNewJob(prevDownloadTime));
} else {
@@ -65,13 +65,13 @@ public class RefreshScheduler {
});
}
RefreshSchedule scheduleNewJob(DateTime prevRefreshTime) {
RefreshSchedule scheduleNewJob(Instant prevRefreshTime) {
BsaDomainRefresh newJob = new BsaDomainRefresh();
tm().insert(newJob);
return RefreshSchedule.create(newJob, prevRefreshTime);
}
RefreshSchedule rescheduleOngoingJob(BsaDomainRefresh ongoingJob, DateTime prevJobStartTime) {
RefreshSchedule rescheduleOngoingJob(BsaDomainRefresh ongoingJob, Instant prevJobStartTime) {
return RefreshSchedule.create(ongoingJob, prevJobStartTime);
}

View File

@@ -131,7 +131,7 @@ public class DelegatedCredentials extends GoogleCredentials {
Clock clock,
Duration tokenRefreshDelay) {
checkArgument(
tokenRefreshDelay.getSeconds() <= MAX_TOKEN_REFRESH_DELAY.getSeconds(),
tokenRefreshDelay.toSeconds() <= MAX_TOKEN_REFRESH_DELAY.toSeconds(),
"Max refresh delay must not exceed %s.",
MAX_TOKEN_REFRESH_DELAY);
@@ -206,7 +206,7 @@ public class DelegatedCredentials extends GoogleCredentials {
JsonWebToken.Payload payload = new JsonWebToken.Payload();
payload.setIssuer(this.delegatedServiceAccountUser);
payload.setIssuedAtTimeSeconds(currentTime / 1000);
payload.setExpirationTimeSeconds(currentTime / 1000 + tokenRefreshDelay.getSeconds());
payload.setExpirationTimeSeconds(currentTime / 1000 + tokenRefreshDelay.toSeconds());
payload.setSubject(delegatingUserEmail);
payload.put("scope", Joiner.on(' ').join(scopes));
payload.setAudience(DEFAULT_TOKEN_URI);
@@ -241,10 +241,10 @@ public class DelegatedCredentials extends GoogleCredentials {
if (value == null) {
throw new IOException(String.format(VALUE_NOT_FOUND_MESSAGE, errorPrefix, key));
}
if (!(value instanceof String)) {
if (!(value instanceof String string)) {
throw new IOException(String.format(VALUE_WRONG_TYPE_MESSAGE, errorPrefix, "string", key));
}
return (String) value;
return string;
}
/** Return the specified integer from JSON or throw a helpful error message. */
@@ -257,9 +257,9 @@ public class DelegatedCredentials extends GoogleCredentials {
if (value instanceof BigDecimal bigDecimalValue) {
return bigDecimalValue.intValueExact();
}
if (!(value instanceof Integer)) {
if (!(value instanceof Integer i)) {
throw new IOException(String.format(VALUE_WRONG_TYPE_MESSAGE, errorPrefix, "integer", key));
}
return (Integer) value;
return i;
}
}

View File

@@ -114,13 +114,13 @@ public class ExportDomainListsAction implements Runnable {
.unwrap(NativeQuery.class)
.setTupleTransformer(new DomainResultTransformer())
.setParameter("tld", tld)
.setParameter("now", replicaTm().getTransactionTime().toString())
.setParameter("now", replicaTm().getTxTime().toString())
.getResultList();
} else {
return replicaTm()
.query(SELECT_DOMAINS_STATEMENT, String.class)
.setParameter("tld", tld)
.setParameter("now", replicaTm().getTransactionTime())
.setParameter("now", replicaTm().getTxTime())
.getResultList();
}
});

View File

@@ -144,7 +144,8 @@ public class ExportPremiumTermsAction implements Runnable {
String premiumListName = tld.getPremiumListName().get();
checkState(
PremiumListDao.getLatestRevision(premiumListName).isPresent(),
"Could not load premium list for " + tldStr);
"Could not load premium list for %s",
tldStr);
SortedSet<String> premiumTerms =
PremiumListDao.loadAllPremiumEntries(premiumListName).stream()
.map(PremiumEntry::toString)

View File

@@ -76,7 +76,7 @@ public class SyncRegistrarsSheetAction implements Runnable {
}};
private final int statusCode;
protected final String message;
final String message;
Result(int statusCode, String message) {
this.statusCode = statusCode;
@@ -84,7 +84,7 @@ public class SyncRegistrarsSheetAction implements Runnable {
}
/** Log an error message. Results that use log levels other than info should override this. */
protected void log(@Nullable Exception cause) {
void log(@Nullable Exception cause) {
logger.atInfo().withCause(cause).log(message);
}

View File

@@ -217,12 +217,13 @@ public abstract class EppException extends Exception {
public static class UnimplementedCommandException extends EppException {
public UnimplementedCommandException(InnerCommand command) {
super(String.format(
"No flow found for %s with extension %s",
command.getClass().getSimpleName(),
command instanceof ResourceCommandWrapper
? ((ResourceCommandWrapper) command).getResourceCommand().getClass().getSimpleName()
: null));
super(
String.format(
"No flow found for %s with extension %s",
command.getClass().getSimpleName(),
command instanceof ResourceCommandWrapper resourceCommandWrapper
? resourceCommandWrapper.getResourceCommand().getClass().getSimpleName()
: null));
}
public UnimplementedCommandException(String message) {

View File

@@ -15,6 +15,7 @@
package google.registry.flows;
import static com.google.common.base.Preconditions.checkState;
import static google.registry.xml.XmlTransformer.createXmlInputFactory;
import com.google.common.base.CharMatcher;
import com.google.common.collect.ImmutableSet;
@@ -22,6 +23,7 @@ import com.google.common.flogger.FluentLogger;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Locale;
@@ -72,8 +74,8 @@ public class EppXmlSanitizer {
private static final String DEFAULT_MASK = "*";
private static final XMLInputFactory XML_INPUT_FACTORY = createXmlInputFactory();
private static final XMLOutputFactory XML_OUTPUT_FACTORY = XMLOutputFactory.newFactory();
private static final XMLEventFactory XML_EVENT_FACTORY = XMLEventFactory.newFactory();
private static final XMLOutputFactory XML_OUTPUT_FACTORY = XMLOutputFactory.newDefaultFactory();
private static final XMLEventFactory XML_EVENT_FACTORY = XMLEventFactory.newDefaultFactory();
/**
* Returns sanitized EPP XML message. For malformed XML messages, base64-encoded raw bytes will be
@@ -139,7 +141,7 @@ public class EppXmlSanitizer {
}
}
xmlEventWriter.flush();
return outputXmlBytes.toString(inputEncoding);
return outputXmlBytes.toString(Charset.forName(inputEncoding));
}
private static String maskSensitiveData(String original) {
@@ -158,16 +160,4 @@ public class EppXmlSanitizer {
private static boolean isMatchingEndEvent(XMLEvent xmlEvent, QName startEventName) {
return xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().equals(startEventName);
}
private static XMLInputFactory createXmlInputFactory() {
XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory();
// Coalesce adjacent data, so that all chars in a string will be grouped as one item.
xmlInputFactory.setProperty(XMLInputFactory.IS_COALESCING, true);
// Preserve Name Space information.
xmlInputFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, true);
// Prevent XXE attacks.
xmlInputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
return xmlInputFactory;
}
}

View File

@@ -16,6 +16,7 @@ package google.registry.flows.certs;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static google.registry.util.DateTimeUtils.toDateTime;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableSet;
@@ -94,8 +95,8 @@ public class CertificateChecker {
}
private static int getValidityLengthInDays(X509Certificate certificate) {
DateTime start = DateTime.parse(certificate.getNotBefore().toInstant().toString());
DateTime end = DateTime.parse(certificate.getNotAfter().toInstant().toString());
DateTime start = toDateTime(certificate.getNotBefore().toInstant());
DateTime end = toDateTime(certificate.getNotAfter().toInstant());
return Days.daysBetween(start.withTimeAtStartOfDay(), end.withTimeAtStartOfDay()).getDays();
}

View File

@@ -51,8 +51,8 @@ import static google.registry.model.tld.Tld.TldState.QUIET_PERIOD;
import static google.registry.model.tld.Tld.TldState.START_DATE_SUNRISE;
import static google.registry.model.tld.label.ReservationType.NAME_COLLISION;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.leapSafeAddYears;
import static google.registry.util.DateTimeUtils.END_INSTANT;
import static google.registry.util.DateTimeUtils.plusYears;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -331,7 +331,7 @@ public final class DomainCreateFlow implements MutatingFlow {
validateFeeChallenge(feeCreate, feesAndCredits, defaultTokenUsed);
Optional<SecDnsCreateExtension> secDnsCreate =
validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class));
DateTime registrationExpirationTime = leapSafeAddYears(now, years);
DateTime registrationExpirationTime = plusYears(now, years);
String repoId = createDomainRepoId(tm().allocateId(), tld.getTldStr());
long historyRevisionId = tm().allocateId();
HistoryEntryId domainHistoryId = new HistoryEntryId(repoId, historyRevisionId);
@@ -630,7 +630,7 @@ public final class DomainCreateFlow implements MutatingFlow {
.setTargetId(targetId)
.setRegistrarId(registrarId)
.setEventTime(registrationExpirationTime)
.setRecurrenceEndTime(END_OF_TIME)
.setRecurrenceEndTime(END_INSTANT)
.setDomainHistoryId(domainHistoryId)
.setRenewalPriceBehavior(renewalPriceBehavior)
.setRenewalPrice(allocationToken.flatMap(AllocationToken::getRenewalPrice).orElse(null))

View File

@@ -40,6 +40,7 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
import static google.registry.pricing.PricingEngineProxy.getDomainRenewCost;
import static google.registry.util.CollectionUtils.nullToEmpty;
import static google.registry.util.CollectionUtils.union;
import static google.registry.util.DateTimeUtils.toInstant;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -201,7 +202,7 @@ public final class DomainDeleteFlow implements MutatingFlow, SqlStatementLogging
GracePeriod.createWithoutBillingEvent(
GracePeriodStatus.REDEMPTION,
existingDomain.getRepoId(),
redemptionTime,
toInstant(redemptionTime),
registrarId)));
// Note: The expiration time is unchanged, so if it's before the new deletion time, there will
// be a "phantom autorenew" where the expiration time advances. No poll message will be

View File

@@ -104,7 +104,7 @@ public class DomainDeletionTimeCache {
ForeignKeyUtils.loadMostRecentResources(
Domain.class, ImmutableSet.of(domainName), false)
.get(domainName);
return mostRecentResource == null ? null : mostRecentResource.deletionTime();
return mostRecentResource == null ? null : mostRecentResource.getDeletionTime();
};
// Unfortunately, maintenance tasks aren't necessarily already in a transaction

View File

@@ -60,10 +60,10 @@ public final class DomainFlowTmchUtils {
if (signedMarks.size() > 1) {
throw new TooManySignedMarksException();
}
if (!(signedMarks.get(0) instanceof EncodedSignedMark)) {
if (!(signedMarks.get(0) instanceof EncodedSignedMark encodedSignedMark)) {
throw new SignedMarksMustBeEncodedException();
}
SignedMark signedMark = verifyEncodedSignedMark((EncodedSignedMark) signedMarks.get(0), now);
SignedMark signedMark = verifyEncodedSignedMark(encodedSignedMark, now);
return verifySignedMarkValidForDomainLabel(signedMark, domainLabel);
}

View File

@@ -42,7 +42,8 @@ import static google.registry.pricing.PricingEngineProxy.isDomainPremium;
import static google.registry.util.CollectionUtils.nullToEmpty;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.isAtOrAfter;
import static google.registry.util.DateTimeUtils.leapSafeAddYears;
import static google.registry.util.DateTimeUtils.plusYears;
import static google.registry.util.DateTimeUtils.toInstant;
import static google.registry.util.DomainNameUtils.ACE_PREFIX;
import static java.util.stream.Collectors.joining;
@@ -565,7 +566,7 @@ public class DomainFlowUtils {
}
BillingRecurrence newBillingRecurrence =
existingBillingRecurrence.asBuilder().setRecurrenceEndTime(newEndTime).build();
existingBillingRecurrence.asBuilder().setRecurrenceEndTime(toInstant(newEndTime)).build();
tm().update(newBillingRecurrence);
return newBillingRecurrence;
}
@@ -847,7 +848,7 @@ public class DomainFlowUtils {
*/
public static void validateRegistrationPeriod(DateTime now, DateTime newExpirationTime)
throws EppException {
if (leapSafeAddYears(now, MAX_REGISTRATION_YEARS).isBefore(newExpirationTime)) {
if (plusYears(now, MAX_REGISTRATION_YEARS).isBefore(newExpirationTime)) {
throw new ExceedsMaxRegistrationYearsException();
}
}
@@ -1113,7 +1114,7 @@ public class DomainFlowUtils {
"FROM DomainHistory WHERE modificationTime >= :beginning AND repoId = "
+ ":repoId ORDER BY modificationTime ASC",
DomainHistory.class)
.setParameter("beginning", now.minus(maxSearchPeriod))
.setParameter("beginning", toInstant(now.minus(maxSearchPeriod)))
.setParameter("repoId", domain.getRepoId())
.getResultList();
}

View File

@@ -34,7 +34,7 @@ import static google.registry.flows.domain.token.AllocationTokenFlowUtils.maybeA
import static google.registry.flows.domain.token.AllocationTokenFlowUtils.verifyBulkTokenAllowedOnDomain;
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_RENEW;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.DateTimeUtils.leapSafeAddYears;
import static google.registry.util.DateTimeUtils.plusYears;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -191,7 +191,7 @@ public final class DomainRenewFlow implements MutatingFlow {
existingDomain = maybeApplyBulkPricingRemovalToken(existingDomain, allocationToken);
DateTime newExpirationTime =
leapSafeAddYears(existingDomain.getRegistrationExpirationDateTime(), years); // Uncapped
plusYears(existingDomain.getRegistrationExpirationDateTime(), years); // Uncapped
validateRegistrationPeriod(now, newExpirationTime);
Optional<FeeRenewCommandExtension> feeRenew =
eppInput.getSingleExtension(FeeRenewCommandExtension.class);

View File

@@ -30,7 +30,7 @@ import static google.registry.flows.domain.DomainFlowUtils.verifyPremiumNameIsNo
import static google.registry.flows.domain.DomainFlowUtils.verifyRegistrarIsActive;
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_RESTORE;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.END_INSTANT;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -163,14 +163,14 @@ public final class DomainRestoreRequestFlow implements MutatingFlow {
BillingRecurrence autorenewEvent =
newAutorenewBillingEvent(existingDomain)
.setEventTime(newExpirationTime)
.setRecurrenceEndTime(END_OF_TIME)
.setRecurrenceEndTime(END_INSTANT)
.setDomainHistoryId(domainHistoryId)
.build();
entitiesToInsert.add(autorenewEvent);
PollMessage.Autorenew autorenewPollMessage =
newAutorenewPollMessage(existingDomain)
.setEventTime(newExpirationTime)
.setAutorenewEndTime(END_OF_TIME)
.setAutorenewEndTime(END_INSTANT)
.setDomainHistoryId(domainHistoryId)
.build();
entitiesToInsert.add(autorenewPollMessage);
@@ -242,7 +242,7 @@ public final class DomainRestoreRequestFlow implements MutatingFlow {
return existingDomain
.asBuilder()
.setRegistrationExpirationTime(newExpirationTime)
.setDeletionTime(END_OF_TIME)
.setDeletionTime(END_INSTANT)
.setStatusValues(null)
.setGracePeriods(null)
.setDeletePollMessage(null)

View File

@@ -32,7 +32,7 @@ import static google.registry.model.reporting.DomainTransactionRecord.Transactio
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_TRANSFER_APPROVE;
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.END_INSTANT;
import static google.registry.util.DateTimeUtils.toDateTime;
import static google.registry.util.DateTimeUtils.toInstant;
@@ -211,7 +211,7 @@ public final class DomainTransferApproveFlow implements MutatingFlow {
? RenewalPriceBehavior.DEFAULT
: existingBillingRecurrence.getRenewalPriceBehavior())
.setRenewalPrice(renewalPrice)
.setRecurrenceEndTime(END_OF_TIME)
.setRecurrenceEndTime(END_INSTANT)
.setDomainHistoryId(domainHistoryId)
.build();
// Create a new autorenew poll message.
@@ -220,7 +220,7 @@ public final class DomainTransferApproveFlow implements MutatingFlow {
.setTargetId(targetId)
.setRegistrarId(gainingRegistrarId)
.setEventTime(newExpirationTime)
.setAutorenewEndTime(END_OF_TIME)
.setAutorenewEndTime(END_INSTANT)
.setMsg("Domain was auto-renewed.")
.setDomainHistoryId(domainHistoryId)
.build();

View File

@@ -16,7 +16,7 @@ package google.registry.flows.domain;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.collect.MoreCollectors.onlyElement;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.END_INSTANT;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -240,7 +240,7 @@ public final class DomainTransferUtils {
.setTargetId(targetId)
.setRegistrarId(gainingRegistrarId)
.setEventTime(serverApproveNewExpirationTime)
.setAutorenewEndTime(END_OF_TIME)
.setAutorenewEndTime(END_INSTANT)
.setMsg("Domain was auto-renewed.")
.setDomainHistoryId(domainHistoryId)
.build();
@@ -258,7 +258,7 @@ public final class DomainTransferUtils {
.setTargetId(targetId)
.setRegistrarId(gainingRegistrarId)
.setEventTime(serverApproveNewExpirationTime)
.setRecurrenceEndTime(END_OF_TIME)
.setRecurrenceEndTime(END_INSTANT)
.setRenewalPriceBehavior(existingBillingRecurrence.getRenewalPriceBehavior())
.setRenewalPrice(existingBillingRecurrence.getRenewalPrice().orElse(null))
.setDomainHistoryId(domainHistoryId)

View File

@@ -127,11 +127,11 @@ public class FeesAndCredits extends ImmutableObject implements Buildable {
}
public Builder addFeeOrCredit(BaseFee feeOrCredit) {
if (feeOrCredit instanceof Credit) {
if (feeOrCredit instanceof Credit credit) {
getInstance().credits =
new ImmutableList.Builder<Credit>()
.addAll(nullToEmptyImmutableCopy(getInstance().credits))
.add((Credit) feeOrCredit)
.add(credit)
.build();
} else {
getInstance().fees =
@@ -147,8 +147,8 @@ public class FeesAndCredits extends ImmutableObject implements Buildable {
ImmutableList.Builder<Fee> feeBuilder = new ImmutableList.Builder<>();
ImmutableList.Builder<Credit> creditBuilder = new ImmutableList.Builder<>();
for (BaseFee feeOrCredit : feesAndCredits) {
if (feeOrCredit instanceof Credit) {
creditBuilder.add((Credit) feeOrCredit);
if (feeOrCredit instanceof Credit credit) {
creditBuilder.add(credit);
} else {
feeBuilder.add((Fee) feeOrCredit);
}

View File

@@ -19,6 +19,7 @@ import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
import static google.registry.flows.host.HostFlowUtils.validateHostName;
import static google.registry.model.EppResourceUtils.isLinked;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.DateTimeUtils.toDateTime;
import com.google.common.collect.ImmutableSet;
import google.registry.flows.EppException;
@@ -80,7 +81,7 @@ public final class HostInfoFlow implements TransactionalFlow {
tm().loadByKey(host.getSuperordinateDomain()).cloneProjectedAtTime(now);
hostInfoDataBuilder
.setCurrentSponsorRegistrarId(superordinateDomain.getCurrentSponsorRegistrarId())
.setLastTransferTime(host.computeLastTransferTime(superordinateDomain));
.setLastTransferTime(toDateTime(host.computeLastTransferTime(superordinateDomain)));
if (superordinateDomain.getStatusValues().contains(StatusValue.PENDING_TRANSFER)) {
statusValues.add(StatusValue.PENDING_TRANSFER);
}

View File

@@ -32,6 +32,7 @@ import static google.registry.flows.host.HostFlowUtils.verifySuperordinateDomain
import static google.registry.model.reporting.HistoryEntry.Type.HOST_UPDATE;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.CollectionUtils.isNullOrEmpty;
import static google.registry.util.DateTimeUtils.toDateTime;
import com.google.cloud.tasks.v2.Task;
import com.google.common.collect.ImmutableMultimap;
@@ -175,7 +176,8 @@ public final class HostUpdateFlow implements MutatingFlow {
// have just completed. This is only critical for updates that rename a host away from its
// current superordinate domain, where we must "freeze" the last transfer time, but it's easiest
// to just update it unconditionally.
DateTime lastTransferTime = existingHost.computeLastTransferTime(oldSuperordinateDomain);
DateTime lastTransferTime =
toDateTime(existingHost.computeLastTransferTime(oldSuperordinateDomain));
// Copy the clientId onto the host. This is only really needed when the host will be external,
// since external hosts store their own clientId. For subordinate hosts the canonical clientId
// comes from the superordinate domain, but we might as well update the persisted value. For

View File

@@ -90,8 +90,12 @@ public class FlowPicker {
/** Get the flow associated with this {@link EppInput} or return null to signal no match. */
Class<? extends Flow> get(EppInput eppInput) {
InnerCommand innerCommand = eppInput.getCommandWrapper().getCommand();
return get(eppInput, innerCommand, (innerCommand instanceof ResourceCommandWrapper)
? ((ResourceCommandWrapper) innerCommand).getResourceCommand() : null);
return get(
eppInput,
innerCommand,
(innerCommand instanceof ResourceCommandWrapper resourceCommandWrapper)
? resourceCommandWrapper.getResourceCommand()
: null);
}
/**
@@ -129,10 +133,10 @@ public class FlowPicker {
@Override
Class<? extends Flow> get(
EppInput eppInput, InnerCommand innerCommand, ResourceCommand resourceCommand) {
if (!(innerCommand instanceof Poll)) {
if (!(innerCommand instanceof Poll poll)) {
return null;
}
return switch (((Poll) innerCommand).getPollOp()) {
return switch (poll.getPollOp()) {
case ACK -> PollAckFlow.class;
case REQUEST -> PollRequestFlow.class;
};
@@ -222,28 +226,51 @@ public class FlowPicker {
}};
/** Transfer flows have an {@link InnerCommand} of type {@link Transfer}. */
private static final FlowProvider TRANSFER_FLOW_PROVIDER = new FlowProvider() {
private final Table<Class<?>, TransferOp, Class<? extends Flow>> transferFlows = ImmutableTable
.<Class<?>, TransferOp, Class<? extends Flow>>builder()
.put(ContactCommand.Transfer.class, TransferOp.APPROVE, ContactTransferApproveFlow.class)
.put(ContactCommand.Transfer.class, TransferOp.CANCEL, ContactTransferCancelFlow.class)
.put(ContactCommand.Transfer.class, TransferOp.QUERY, ContactTransferQueryFlow.class)
.put(ContactCommand.Transfer.class, TransferOp.REJECT, ContactTransferRejectFlow.class)
.put(ContactCommand.Transfer.class, TransferOp.REQUEST, ContactTransferRequestFlow.class)
.put(DomainCommand.Transfer.class, TransferOp.APPROVE, DomainTransferApproveFlow.class)
.put(DomainCommand.Transfer.class, TransferOp.CANCEL, DomainTransferCancelFlow.class)
.put(DomainCommand.Transfer.class, TransferOp.QUERY, DomainTransferQueryFlow.class)
.put(DomainCommand.Transfer.class, TransferOp.REJECT, DomainTransferRejectFlow.class)
.put(DomainCommand.Transfer.class, TransferOp.REQUEST, DomainTransferRequestFlow.class)
.build();
private static final FlowProvider TRANSFER_FLOW_PROVIDER =
new FlowProvider() {
private final Table<Class<?>, TransferOp, Class<? extends Flow>> transferFlows =
ImmutableTable.<Class<?>, TransferOp, Class<? extends Flow>>builder()
.put(
ContactCommand.Transfer.class,
TransferOp.APPROVE,
ContactTransferApproveFlow.class)
.put(
ContactCommand.Transfer.class,
TransferOp.CANCEL,
ContactTransferCancelFlow.class)
.put(
ContactCommand.Transfer.class, TransferOp.QUERY, ContactTransferQueryFlow.class)
.put(
ContactCommand.Transfer.class,
TransferOp.REJECT,
ContactTransferRejectFlow.class)
.put(
ContactCommand.Transfer.class,
TransferOp.REQUEST,
ContactTransferRequestFlow.class)
.put(
DomainCommand.Transfer.class,
TransferOp.APPROVE,
DomainTransferApproveFlow.class)
.put(
DomainCommand.Transfer.class, TransferOp.CANCEL, DomainTransferCancelFlow.class)
.put(DomainCommand.Transfer.class, TransferOp.QUERY, DomainTransferQueryFlow.class)
.put(
DomainCommand.Transfer.class, TransferOp.REJECT, DomainTransferRejectFlow.class)
.put(
DomainCommand.Transfer.class,
TransferOp.REQUEST,
DomainTransferRequestFlow.class)
.build();
@Override
Class<? extends Flow> get(
EppInput eppInput, InnerCommand innerCommand, ResourceCommand resourceCommand) {
return resourceCommand != null && innerCommand instanceof Transfer
? transferFlows.get(resourceCommand.getClass(), ((Transfer) innerCommand).getTransferOp())
: null;
}};
@Override
Class<? extends Flow> get(
EppInput eppInput, InnerCommand innerCommand, ResourceCommand resourceCommand) {
return resourceCommand != null && innerCommand instanceof Transfer transfer
? transferFlows.get(resourceCommand.getClass(), transfer.getTransferOp())
: null;
}
};
private static final ImmutableList<FlowProvider> FLOW_PROVIDERS =
ImmutableList.of(

View File

@@ -19,22 +19,47 @@ import static google.registry.persistence.transaction.QueryComposer.Comparator.E
import static google.registry.persistence.transaction.QueryComposer.Comparator.LTE;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
import static google.registry.util.DateTimeUtils.plusYears;
import static google.registry.util.DateTimeUtils.toInstant;
import google.registry.model.poll.PollMessage;
import google.registry.persistence.transaction.QueryComposer;
import java.time.Instant;
import java.util.Optional;
import org.joda.time.DateTime;
/** Static utility functions for poll flows. */
public final class PollFlowUtils {
/** Returns the number of poll messages for the given registrar that are not in the future. */
/**
* Returns the number of poll messages for the given registrar that are not in the future.
*
* @deprecated Use {@link #getPollMessageCount(String, Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static int getPollMessageCount(String registrarId, DateTime now) {
return getPollMessageCount(registrarId, toInstant(now));
}
/** Returns the number of poll messages for the given registrar that are not in the future. */
public static int getPollMessageCount(String registrarId, Instant now) {
return (int) createPollMessageQuery(registrarId, now).count();
}
/** Returns the first (by event time) poll message not in the future for this registrar. */
/**
* Returns the first (by event time) poll message not in the future for this registrar.
*
* @deprecated Use {@link #getFirstPollMessage(String, Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static Optional<PollMessage> getFirstPollMessage(String registrarId, DateTime now) {
return getFirstPollMessage(registrarId, toInstant(now));
}
/** Returns the first (by event time) poll message not in the future for this registrar. */
public static Optional<PollMessage> getFirstPollMessage(String registrarId, Instant now) {
return createPollMessageQuery(registrarId, now).orderBy("eventTime").first();
}
@@ -47,22 +72,22 @@ public final class PollFlowUtils {
*/
public static void ackPollMessage(PollMessage pollMessage) {
checkArgument(
isBeforeOrAt(pollMessage.getEventTime(), tm().getTransactionTime()),
isBeforeOrAt(pollMessage.getEventTimeInstant(), tm().getTxTime()),
"Cannot ACK poll message with ID %s because its event time is in the future: %s",
pollMessage.getId(),
pollMessage.getEventTime());
pollMessage.getEventTimeInstant());
if (pollMessage instanceof PollMessage.OneTime) {
// One-time poll messages are deleted once acked.
tm().delete(pollMessage.createVKey());
} else if (pollMessage instanceof PollMessage.Autorenew autorenewPollMessage) {
// Move the eventTime of this autorenew poll message forward by a year.
DateTime nextEventTime = autorenewPollMessage.getEventTime().plusYears(1);
Instant nextEventTime = plusYears(autorenewPollMessage.getEventTimeInstant(), 1);
// If the next event falls within the bounds of the end time, then just update the eventTime
// and re-save it for future autorenew poll messages to be delivered. Otherwise, this
// autorenew poll message has no more events to deliver and should be deleted.
if (nextEventTime.isBefore(autorenewPollMessage.getAutorenewEndTime())) {
if (nextEventTime.isBefore(autorenewPollMessage.getAutorenewEndTimeInstant())) {
tm().put(autorenewPollMessage.asBuilder().setEventTime(nextEventTime).build());
} else {
tm().delete(autorenewPollMessage.createVKey());
@@ -75,9 +100,21 @@ public final class PollFlowUtils {
/**
* Returns the QueryComposer for poll messages from the given registrar that are not in the
* future.
*
* @deprecated Use {@link #createPollMessageQuery(String, Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static QueryComposer<PollMessage> createPollMessageQuery(
String registrarId, DateTime now) {
return createPollMessageQuery(registrarId, toInstant(now));
}
/**
* Returns the QueryComposer for poll messages from the given registrar that are not in the
* future.
*/
public static QueryComposer<PollMessage> createPollMessageQuery(String registrarId, Instant now) {
return tm().createQueryComposer(PollMessage.class)
.where("clientId", EQ, registrarId)
.where("eventTime", LTE, now);

View File

@@ -35,8 +35,8 @@ import google.registry.model.poll.PollMessageExternalKeyConverter;
import google.registry.persistence.IsolationLevel;
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
import jakarta.inject.Inject;
import java.time.Instant;
import java.util.Optional;
import org.joda.time.DateTime;
/**
* An EPP flow for requesting {@link PollMessage}s.
@@ -67,7 +67,7 @@ public final class PollRequestFlow implements TransactionalFlow {
}
// Return the oldest message from the queue.
DateTime now = tm().getTransactionTime();
Instant now = tm().getTxTime();
Optional<PollMessage> maybePollMessage = getFirstPollMessage(registrarId, now);
if (maybePollMessage.isEmpty()) {
return responseBuilder.setResultFromCode(SUCCESS_WITH_NO_MESSAGES).build();

View File

@@ -182,7 +182,7 @@ public class GcsUtils implements Serializable {
// These two methods are needed to check whether serialization is done correctly in tests.
@Override
public boolean equals(Object obj) {
return obj instanceof GcsUtils && ((GcsUtils) obj).storageOptions.equals(storageOptions);
return obj instanceof GcsUtils gcsUtils && gcsUtils.storageOptions.equals(storageOptions);
}
@Override

View File

@@ -68,7 +68,7 @@ public class DirectoryGroupsConnection implements GroupsConnection {
@VisibleForTesting
static Groups getDefaultGroupPermissions() {
Groups permissions = new Groups();
permissions.setAllowExternalMembers(Boolean.TRUE.toString());
permissions.setAllowExternalMembers("true");
permissions.setWhoCanPostMessage("ALL_MANAGERS_CAN_POST");
permissions.setWhoCanViewGroup("ALL_MANAGERS_CAN_VIEW");
permissions.setWhoCanViewMembership("ALL_MANAGERS_CAN_VIEW");

View File

@@ -174,8 +174,8 @@ public final class GmailClient {
@Override
public boolean test(Throwable e) {
if (e instanceof HttpResponseException) {
return testHttpResponse((HttpResponseException) e);
if (e instanceof HttpResponseException httpResponseException) {
return testHttpResponse(httpResponseException);
}
return true;
}

View File

@@ -14,6 +14,7 @@
package google.registry.keyring;
import com.google.common.collect.ImmutableList;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
@@ -21,7 +22,6 @@ import google.registry.config.RegistryConfig.Config;
import google.registry.keyring.api.Keyring;
import google.registry.keyring.secretmanager.SecretManagerKeyring;
import jakarta.inject.Singleton;
import java.util.Optional;
/** Dagger module for {@link Keyring} */
@Module
@@ -38,9 +38,10 @@ public abstract class KeyringModule {
}
@Provides
@Config("cloudSqlReplicaInstanceConnectionName")
public static Optional<String> provideCloudSqlReplicaInstanceConnectionName(Keyring keyring) {
return Optional.ofNullable(keyring.getSqlReplicaConnectionName());
@Config("cloudSqlReplicaInstanceConnectionNames")
public static ImmutableList<String> provideCloudSqlReplicaInstanceConnectionNames(
Keyring keyring) {
return ImmutableList.copyOf(keyring.getSqlReplicaConnectionNames());
}
@Provides

View File

@@ -14,6 +14,7 @@
package google.registry.keyring.api;
import com.google.common.collect.ImmutableList;
import javax.annotation.concurrent.ThreadSafe;
import org.bouncycastle.openpgp.PGPKeyPair;
import org.bouncycastle.openpgp.PGPPrivateKey;
@@ -151,9 +152,17 @@ public interface Keyring extends AutoCloseable {
/** Returns the Cloud SQL connection name of the primary database instance. */
String getSqlPrimaryConnectionName();
/** Returns the Cloud SQL connection name of the replica database instance. */
/**
* Returns the Cloud SQL connection name of the replica database instance.
*
* <p>Note: It is likely a better idea to use multiple replicas and {@link
* #getSqlReplicaConnectionNames()} instead.
*/
String getSqlReplicaConnectionName();
/** Returns the Cloud SQL connection names of the replica database instances. */
ImmutableList<String> getSqlReplicaConnectionNames();
// Don't throw so try-with-resources works better.
@Override
void close();

View File

@@ -17,6 +17,8 @@ package google.registry.keyring.secretmanager;
import static com.google.common.base.CaseFormat.LOWER_HYPHEN;
import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import google.registry.keyring.api.KeySerializer;
import google.registry.keyring.api.Keyring;
import google.registry.keyring.api.KeyringException;
@@ -66,7 +68,8 @@ public class SecretManagerKeyring implements Keyring {
RDE_SSH_CLIENT_PUBLIC_STRING,
SAFE_BROWSING_API_KEY,
SQL_PRIMARY_CONN_NAME,
SQL_REPLICA_CONN_NAME;
SQL_REPLICA_CONN_NAME,
SQL_REPLICA_CONN_NAMES;
String getLabel() {
return UPPER_UNDERSCORE.to(LOWER_HYPHEN, name());
@@ -157,7 +160,25 @@ public class SecretManagerKeyring implements Keyring {
@Override
public String getSqlReplicaConnectionName() {
return getString(StringKeyLabel.SQL_REPLICA_CONN_NAME);
try {
return getString(StringKeyLabel.SQL_REPLICA_CONN_NAME);
} catch (KeyringException e) {
return null;
}
}
@Override
public ImmutableList<String> getSqlReplicaConnectionNames() {
try {
String names = getString(StringKeyLabel.SQL_REPLICA_CONN_NAMES);
return ImmutableList.copyOf(
Splitter.on('\n').trimResults().omitEmptyStrings().splitToList(names));
} catch (KeyringException e) {
String replicaConnectionName = getSqlReplicaConnectionName();
return replicaConnectionName == null
? ImmutableList.of()
: ImmutableList.of(replicaConnectionName);
}
}
/** No persistent resources are maintained for this Keyring implementation. */

View File

@@ -34,6 +34,7 @@ import static google.registry.keyring.secretmanager.SecretManagerKeyring.StringK
import static google.registry.keyring.secretmanager.SecretManagerKeyring.StringKeyLabel.SAFE_BROWSING_API_KEY;
import static google.registry.keyring.secretmanager.SecretManagerKeyring.StringKeyLabel.SQL_PRIMARY_CONN_NAME;
import static google.registry.keyring.secretmanager.SecretManagerKeyring.StringKeyLabel.SQL_REPLICA_CONN_NAME;
import static google.registry.keyring.secretmanager.SecretManagerKeyring.StringKeyLabel.SQL_REPLICA_CONN_NAMES;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.google.common.flogger.FluentLogger;
@@ -134,6 +135,10 @@ public final class SecretManagerKeyringUpdater {
return setString(name, SQL_REPLICA_CONN_NAME);
}
public SecretManagerKeyringUpdater setSqlReplicaConnectionNames(String names) {
return setString(names, SQL_REPLICA_CONN_NAMES);
}
/**
* Persists the secrets in the Secret Manager.
*

View File

@@ -280,9 +280,9 @@ public class LoadTestAction implements Runnable {
"clientId",
registrarId,
"superuser",
Boolean.FALSE.toString(),
"false",
"dryRun",
Boolean.FALSE.toString(),
"false",
"xml",
xmls.get(i)))
.toBuilder()

View File

@@ -10,17 +10,20 @@
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the Licenseschema..
// limitations under the License.
package google.registry.model;
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.gson.annotations.Expose;
import google.registry.persistence.EntityCallbacksListener.RecursivePrePersist;
import google.registry.persistence.EntityCallbacksListener.RecursivePreUpdate;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import java.time.Instant;
import javax.annotation.Nullable;
import org.joda.time.DateTime;
@@ -30,25 +33,44 @@ public class CreateAutoTimestamp extends ImmutableObject implements UnsafeSerial
@Column(nullable = false)
@Expose
DateTime creationTime;
Instant creationTime;
@RecursivePrePersist
@RecursivePreUpdate
public void setTimestamp() {
if (creationTime == null) {
creationTime = tm().getTransactionTime();
creationTime = tm().getTxTime();
}
}
/** Returns the timestamp. */
@Nullable
public DateTime getTimestamp() {
public Instant getTimestamp() {
return creationTime;
}
public static CreateAutoTimestamp create(@Nullable DateTime creationTime) {
/**
* @deprecated Use {@link #getTimestamp()}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
@Nullable
public DateTime getTimestampDateTime() {
return toDateTime(creationTime);
}
public static CreateAutoTimestamp create(@Nullable Instant creationTime) {
CreateAutoTimestamp instance = new CreateAutoTimestamp();
instance.creationTime = creationTime;
return instance;
}
/**
* @deprecated Use {@link #create(Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static CreateAutoTimestamp create(@Nullable DateTime creationTime) {
return create(toInstant(creationTime));
}
}

View File

@@ -16,6 +16,8 @@ package google.registry.model;
import static com.google.common.collect.ImmutableSortedMap.toImmutableSortedMap;
import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet;
import static com.google.common.collect.Ordering.natural;
import static google.registry.util.DateTimeUtils.ISO_8601_FORMATTER;
import static google.registry.util.DateTimeUtils.parseInstant;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
@@ -38,6 +40,7 @@ import google.registry.model.tld.Tld.TldState;
import google.registry.persistence.VKey;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.Instant;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
@@ -46,7 +49,6 @@ import java.util.Set;
import java.util.SortedMap;
import org.joda.money.CurrencyUnit;
import org.joda.money.Money;
import org.joda.time.DateTime;
import org.joda.time.Duration;
/** A collection of static utility classes/functions to convert entities to/from YAML files. */
@@ -59,8 +61,11 @@ public class EntityYamlUtils {
SimpleModule module = new SimpleModule();
module.addSerializer(Money.class, new MoneySerializer());
module.addDeserializer(Money.class, new MoneyDeserializer());
module.addSerializer(CreateAutoTimestamp.class, new CreateAutoTimestampSerializer());
module.addDeserializer(CreateAutoTimestamp.class, new CreateAutoTimestampDeserializer());
module.addSerializer(Duration.class, new DurationSerializer());
module.addSerializer(TimedTransitionProperty.class, new TimedTransitionPropertySerializer());
module.addSerializer(Instant.class, new InstantSerializer());
module.addDeserializer(Instant.class, new InstantDeserializer());
ObjectMapper mapper =
JsonMapper.builder(new YAMLFactory().disable(Feature.WRITE_DOC_START_MARKER))
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
@@ -312,24 +317,6 @@ public class EntityYamlUtils {
}
}
/** A custom JSON serializer for a {@link TimedTransitionProperty} of {@link Enum} values. */
public static class TimedTransitionPropertySerializer<E extends Enum<E>>
extends StdSerializer<TimedTransitionProperty<E>> {
TimedTransitionPropertySerializer() {
super(null, true);
}
@Override
public void serialize(
TimedTransitionProperty<E> data,
JsonGenerator jsonGenerator,
SerializerProvider serializerProvider)
throws IOException {
jsonGenerator.writeObject(data.toValueMap());
}
}
/** A custom JSON deserializer for a {@link TimedTransitionProperty} of {@link TldState}. */
public static class TimedTransitionPropertyTldStateDeserializer
extends StdDeserializer<TimedTransitionProperty<TldState>> {
@@ -346,11 +333,13 @@ public class EntityYamlUtils {
public TimedTransitionProperty<TldState> deserialize(
JsonParser jp, DeserializationContext context) throws IOException {
SortedMap<String, String> valueMap = jp.readValueAs(SortedMap.class);
return TimedTransitionProperty.fromValueMap(
return TimedTransitionProperty.fromValueMapInstant(
valueMap.keySet().stream()
.collect(
toImmutableSortedMap(
natural(), DateTime::parse, key -> TldState.valueOf(valueMap.get(key)))));
natural(),
key -> parseInstant(key),
key -> TldState.valueOf(valueMap.get(key)))));
}
}
@@ -370,12 +359,12 @@ public class EntityYamlUtils {
public TimedTransitionProperty<Money> deserialize(JsonParser jp, DeserializationContext context)
throws IOException {
SortedMap<String, LinkedHashMap<String, Object>> valueMap = jp.readValueAs(SortedMap.class);
return TimedTransitionProperty.fromValueMap(
return TimedTransitionProperty.fromValueMapInstant(
valueMap.keySet().stream()
.collect(
toImmutableSortedMap(
natural(),
DateTime::parse,
key -> parseInstant(key),
key ->
Money.of(
CurrencyUnit.of(valueMap.get(key).get("currency").toString()),
@@ -400,16 +389,38 @@ public class EntityYamlUtils {
public TimedTransitionProperty<FeatureStatus> deserialize(
JsonParser jp, DeserializationContext context) throws IOException {
SortedMap<String, String> valueMap = jp.readValueAs(SortedMap.class);
return TimedTransitionProperty.fromValueMap(
return TimedTransitionProperty.fromValueMapInstant(
valueMap.keySet().stream()
.collect(
toImmutableSortedMap(
natural(),
DateTime::parse,
key -> parseInstant(key),
key -> FeatureStatus.valueOf(valueMap.get(key)))));
}
}
/** A custom JSON serializer for a {@link CreateAutoTimestamp}. */
public static class CreateAutoTimestampSerializer extends StdSerializer<CreateAutoTimestamp> {
public CreateAutoTimestampSerializer() {
this(null);
}
public CreateAutoTimestampSerializer(Class<CreateAutoTimestamp> t) {
super(t);
}
@Override
public void serialize(CreateAutoTimestamp value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
if (value.getTimestamp() == null) {
gen.writeNull();
} else {
gen.writeString(ISO_8601_FORMATTER.format(value.getTimestamp()));
}
}
}
/** A custom JSON deserializer for a {@link CreateAutoTimestamp}. */
public static class CreateAutoTimestampDeserializer extends StdDeserializer<CreateAutoTimestamp> {
@@ -424,8 +435,34 @@ public class EntityYamlUtils {
@Override
public CreateAutoTimestamp deserialize(JsonParser jp, DeserializationContext context)
throws IOException {
DateTime creationTime = jp.readValueAs(DateTime.class);
return CreateAutoTimestamp.create(creationTime);
String creationTime = jp.getText();
return CreateAutoTimestamp.create(parseInstant(creationTime));
}
}
/** A custom JSON serializer for {@link Instant}. */
public static class InstantSerializer extends StdSerializer<Instant> {
public InstantSerializer() {
super(Instant.class);
}
@Override
public void serialize(Instant value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
gen.writeString(ISO_8601_FORMATTER.format(value));
}
}
/** A custom JSON deserializer for {@link Instant}. */
public static class InstantDeserializer extends StdDeserializer<Instant> {
public InstantDeserializer() {
super(Instant.class);
}
@Override
public Instant deserialize(JsonParser jp, DeserializationContext context) throws IOException {
return parseInstant(jp.getText());
}
}
}

View File

@@ -24,7 +24,8 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
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.END_INSTANT;
import static google.registry.util.DateTimeUtils.toDateTime;
import static google.registry.util.DateTimeUtils.toInstant;
import com.github.benmanes.caffeine.cache.CacheLoader;
@@ -109,7 +110,7 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
// Need to override the default non-null column attribute.
@AttributeOverride(name = "creationTime", column = @Column)
@Expose
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create((Instant) null);
/**
* The time when this resource was or will be deleted.
@@ -124,7 +125,7 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
* out of the index at that time, as long as we query for resources whose deletion time is before
* now.
*/
DateTime deletionTime;
Instant deletionTime;
/**
* The time that this resource was last updated.
@@ -133,7 +134,7 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
* edits; it only includes EPP-visible modifications such as {@literal <update>}. Can be null if
* the resource has never been modified.
*/
@Expose DateTime lastEppUpdateTime;
@Expose Instant lastEppUpdateTime;
/** Status values associated with this resource. */
@Enumerated(EnumType.STRING)
@@ -157,7 +158,16 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
this.repoId = repoId;
}
/**
* @deprecated Use {@link #getCreationTimeInstant()}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public final DateTime getCreationTime() {
return creationTime.getTimestampDateTime();
}
public final Instant getCreationTimeInstant() {
return creationTime.getTimestamp();
}
@@ -166,12 +176,13 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
}
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public DateTime getLastEppUpdateDateTime() {
return lastEppUpdateTime;
return toDateTime(lastEppUpdateTime);
}
public Instant getLastEppUpdateTime() {
return toInstant(lastEppUpdateTime);
return lastEppUpdateTime;
}
public String getLastEppUpdateRegistrarId() {
@@ -193,12 +204,13 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
}
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public DateTime getDeletionDateTime() {
return deletionTime;
return toDateTime(deletionTime);
}
public Instant getDeletionTime() {
return toInstant(deletionTime);
return deletionTime;
}
/** Return a clone of the resource with timed status values modified using the given time. */
@@ -240,7 +252,7 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
* <p>Note: This can only be used if the creation time hasn't already been set, which it is in
* normal EPP flows.
*/
public B setCreationTime(DateTime creationTime) {
public B setCreationTime(Instant creationTime) {
checkState(
getInstance().creationTime.getTimestamp() == null,
"creationTime can only be set once for EppResource.");
@@ -248,19 +260,47 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
return thisCastToDerived();
}
/**
* @deprecated Use {@link #setCreationTime(Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public B setCreationTime(DateTime creationTime) {
return setCreationTime(toInstant(creationTime));
}
/** Set the time this resource was created. Should only be used in tests. */
@VisibleForTesting
public B setCreationTimeForTest(DateTime creationTime) {
public B setCreationTimeForTest(Instant creationTime) {
getInstance().creationTime = CreateAutoTimestamp.create(creationTime);
return thisCastToDerived();
}
/**
* @deprecated Use {@link #setCreationTimeForTest(Instant)}
*/
@Deprecated
@VisibleForTesting
@SuppressWarnings("InlineMeSuggester")
public B setCreationTimeForTest(DateTime creationTime) {
return setCreationTimeForTest(toInstant(creationTime));
}
/** Set the time after which this resource should be considered deleted. */
public B setDeletionTime(DateTime deletionTime) {
public B setDeletionTime(Instant deletionTime) {
getInstance().deletionTime = deletionTime;
return thisCastToDerived();
}
/**
* @deprecated Use {@link #setDeletionTime(Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public B setDeletionTime(DateTime deletionTime) {
return setDeletionTime(toInstant(deletionTime));
}
/** Set the current sponsoring registrar. */
public B setPersistedCurrentSponsorRegistrarId(String currentSponsorRegistrarId) {
getInstance().currentSponsorRegistrarId = currentSponsorRegistrarId;
@@ -274,11 +314,20 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
}
/** Set the time when a {@literal <update>} was performed on this resource. */
public B setLastEppUpdateTime(DateTime lastEppUpdateTime) {
public B setLastEppUpdateTime(Instant lastEppUpdateTime) {
getInstance().lastEppUpdateTime = lastEppUpdateTime;
return thisCastToDerived();
}
/**
* @deprecated Use {@link #setLastEppUpdateTime(Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public B setLastEppUpdateTime(DateTime lastEppUpdateTime) {
return setLastEppUpdateTime(toInstant(lastEppUpdateTime));
}
/** Set the registrar who last performed a {@literal <update>} on this resource. */
public B setLastEppUpdateRegistrarId(String lastEppUpdateRegistrarId) {
getInstance().lastEppUpdateRegistrarId = lastEppUpdateRegistrarId;
@@ -345,8 +394,8 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
if (getInstance().getStatusValues().isEmpty()) {
addStatusValue(StatusValue.OK);
}
// If there is no deletion time, set it to END_OF_TIME.
setDeletionTime(Optional.ofNullable(getInstance().deletionTime).orElse(END_OF_TIME));
// If there is no deletion time, set it to END_INSTANT.
setDeletionTime(Optional.ofNullable(getInstance().deletionTime).orElse(END_INSTANT));
return ImmutableObject.cloneEmptyToNull(super.build());
}
}

View File

@@ -17,9 +17,10 @@ package google.registry.model;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static google.registry.util.DateTimeUtils.START_INSTANT;
import static google.registry.util.DateTimeUtils.isAtOrAfter;
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
import static google.registry.util.DateTimeUtils.toInstant;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
@@ -39,7 +40,6 @@ import java.util.Comparator;
import java.util.function.Function;
import javax.annotation.Nullable;
import org.joda.time.DateTime;
import org.joda.time.Interval;
/** Utilities for working with {@link EppResource}. */
public final class EppResourceUtils {
@@ -79,22 +79,33 @@ public final class EppResourceUtils {
return (T resource) -> cloneProjectedAtTime(resource, now);
}
public static boolean isActive(EppResource resource, Instant time) {
return isAtOrAfter(time, resource.getCreationTimeInstant())
&& time.isBefore(resource.getDeletionTime());
}
/**
* The lifetime of a resource is from its creation time, inclusive, through its deletion time,
* exclusive, which happily maps to the behavior of Interval.
* @deprecated Use {@link #isActive(EppResource, Instant)}
*/
private static Interval getLifetime(EppResource resource) {
return new Interval(resource.getCreationTime(), resource.getDeletionDateTime());
}
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static boolean isActive(EppResource resource, DateTime time) {
return getLifetime(resource).contains(time);
return isActive(resource, toInstant(time));
}
public static boolean isDeleted(EppResource resource, DateTime time) {
public static boolean isDeleted(EppResource resource, Instant time) {
return !isActive(resource, time);
}
/**
* @deprecated Use {@link #isDeleted(EppResource, Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static boolean isDeleted(EppResource resource, DateTime time) {
return isDeleted(resource, toInstant(time));
}
/** Process an automatic transfer on a domain. */
public static void setAutomaticTransferSuccessProperties(
DomainBase.Builder<?, ?> builder, DomainTransferData transferData) {
@@ -132,7 +143,7 @@ public final class EppResourceUtils {
}
/**
* Rewinds an {@link EppResource} object to a given point in time.
* Returns the given resource as it was at a specific point in time.
*
* <p>This method costs nothing if {@code resource} is already current. Otherwise, it needs to
* perform a single fetch operation.
@@ -141,12 +152,12 @@ public final class EppResourceUtils {
* {@code resource} should be whatever's currently in SQL.
*
* @return the resource at {@code timestamp} or {@code null} if resource is deleted or not yet
* created
* created.
*/
public static <T extends EppResource> T loadAtPointInTime(
final T resource, final DateTime timestamp) {
final T resource, final Instant timestamp) {
// If we're before the resource creation time, don't try to find a "most recent revision".
if (timestamp.isBefore(resource.getCreationTime())) {
if (timestamp.isBefore(resource.getCreationTimeInstant())) {
return null;
}
// If the resource was not modified after the requested time, then use it as-is, otherwise find
@@ -159,23 +170,31 @@ public final class EppResourceUtils {
return (loadedResource == null)
? null
: (isActive(loadedResource, timestamp)
? cloneProjectedAtTime(loadedResource, timestamp)
? (T) loadedResource.cloneProjectedAtInstant(timestamp)
: null);
}
/**
* @deprecated Use {@link #loadAtPointInTime(EppResource, Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static <T extends EppResource> T loadAtPointInTime(
final T resource, final DateTime timestamp) {
return loadAtPointInTime(resource, toInstant(timestamp));
}
/**
* Returns the most recent revision of a given EppResource before or at the provided timestamp,
* falling back to using the resource as-is if there are no revisions.
*
* @see #loadAtPointInTime(EppResource, DateTime)
*/
private static <T extends EppResource> T loadMostRecentRevisionAtTime(
final T resource, final DateTime timestamp) {
final T resource, final Instant timestamp) {
@SuppressWarnings("unchecked")
T resourceAtPointInTime =
(T)
HistoryEntryDao.loadHistoryObjectsForResource(
resource.createVKey(), START_OF_TIME, timestamp)
resource.createVKey(), START_INSTANT, timestamp)
.stream()
.max(Comparator.comparing(HistoryEntry::getModificationTime))
.flatMap(HistoryEntry::getResourceAtPointInTime)
@@ -193,18 +212,27 @@ public final class EppResourceUtils {
* Returns a set of {@link VKey} for domains that reference a specified host.
*
* @param key the referent key
* @param now the logical time of the check
* @param limit the maximum number of returned keys, unlimited if null
* @param now the logical time of the check /** Returns the domains that are linked to this host
* at the given time.
* @deprecated Use {@link #getLinkedDomainKeys(VKey, Instant, Integer)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static ImmutableSet<VKey<Domain>> getLinkedDomainKeys(
VKey<Host> key, DateTime now, @Nullable Integer limit) {
return getLinkedDomainKeys(key, toInstant(now), limit);
}
/** Returns the domains that are linked to this host at the given time. */
public static ImmutableSet<VKey<Domain>> getLinkedDomainKeys(
VKey<Host> key, Instant now, @Nullable Integer limit) {
return tm().reTransact(
() -> {
Query query =
tm().getEntityManager()
.createNativeQuery(HOST_LINKED_DOMAIN_QUERY)
.setParameter("fkRepoId", key.getKey())
.setParameter("now", now.toDate());
.setParameter("now", now);
if (limit != null) {
query.setMaxResults(limit);
}
@@ -220,12 +248,18 @@ public final class EppResourceUtils {
}
/**
* Returns whether the given host is linked to (that is, referenced by) a domain.
* Returns whether this host is linked to any domains at the given time.
*
* @param key the referent key
* @param now the logical time of the check
* @deprecated Use {@link #isLinked(VKey, Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static boolean isLinked(VKey<Host> key, DateTime now) {
return isLinked(key, toInstant(now));
}
/** Returns whether this resource is linked to any domains at the given time. */
public static boolean isLinked(VKey<Host> key, Instant now) {
return !getLinkedDomainKeys(key, now, 1).isEmpty();
}

View File

@@ -20,6 +20,8 @@ import static google.registry.config.RegistryConfig.getEppResourceCachingDuratio
import static google.registry.config.RegistryConfig.getEppResourceMaxCachedEntries;
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.DateTimeUtils.toDateTime;
import static google.registry.util.DateTimeUtils.toInstant;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.LoadingCache;
@@ -63,7 +65,30 @@ public final class ForeignKeyUtils {
Domain.class, "domainName",
Host.class, "hostName");
public record MostRecentResource(String repoId, DateTime deletionTime) {}
public record MostRecentResource(String repoId, Instant deletionTime) {
/**
* @deprecated Use {@link #deletionTime()}
*/
@Deprecated
public DateTime getDeletionTime() {
return toDateTime(deletionTime);
}
}
/**
* Loads an optional {@link VKey} to an {@link EppResource} from the database by foreign key.
*
* <p>Returns empty if no resource with this foreign key was ever created, or if the most recently
* created resource was deleted before time "now".
*
* @deprecated Use {@link #loadKey(Class, String, Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static <E extends EppResource> Optional<VKey<E>> loadKey(
Class<E> clazz, String foreignKey, DateTime now) {
return loadKey(clazz, foreignKey, toInstant(now));
}
/**
* Loads an optional {@link VKey} to an {@link EppResource} from the database by foreign key.
@@ -72,7 +97,7 @@ public final class ForeignKeyUtils {
* created resource was deleted before time "now".
*/
public static <E extends EppResource> Optional<VKey<E>> loadKey(
Class<E> clazz, String foreignKey, DateTime now) {
Class<E> clazz, String foreignKey, Instant now) {
return Optional.ofNullable(loadKeys(clazz, ImmutableList.of(foreignKey), now).get(foreignKey));
}
@@ -81,13 +106,14 @@ public final class ForeignKeyUtils {
*
* <p>Returns null if no resource with this foreign key was ever created or if the most recently
* created resource was deleted before time "now".
*
* @deprecated Use {@link #loadResource(Class, String, Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static <E extends EppResource> Optional<E> loadResource(
Class<E> clazz, String foreignKey, DateTime now) {
// Note: no need to project to "now" because loadResources already does
return Optional.ofNullable(
loadResources(clazz, ImmutableList.of(foreignKey), now).get(foreignKey));
return loadResource(clazz, foreignKey, toInstant(now));
}
/**
@@ -109,9 +135,25 @@ public final class ForeignKeyUtils {
*
* <p>The returned map will omit any foreign keys for which the {@link EppResource} doesn't exist
* or has been soft-deleted.
*
* @deprecated Use {@link #loadKeys(Class, Collection, Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static <E extends EppResource> ImmutableMap<String, VKey<E>> loadKeys(
Class<E> clazz, Collection<String> foreignKeys, DateTime now) {
return loadKeys(clazz, foreignKeys, toInstant(now));
}
/**
* 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.
*
* <p>The returned map will omit any foreign keys for which the {@link EppResource} doesn't exist
* or has been soft-deleted.
*/
public static <E extends EppResource> ImmutableMap<String, VKey<E>> loadKeys(
Class<E> clazz, Collection<String> foreignKeys, Instant now) {
return loadMostRecentResources(clazz, foreignKeys, false).entrySet().stream()
.filter(e -> now.isBefore(e.getValue().deletionTime()))
.collect(toImmutableMap(Entry::getKey, e -> VKey.create(clazz, e.getValue().repoId())));
@@ -124,13 +166,11 @@ public final class ForeignKeyUtils {
* <p>The returned map will omit any foreign keys for which the {@link EppResource} doesn't exist
* or has been soft-deleted.
*/
@SuppressWarnings("unchecked")
@SuppressWarnings({"unchecked", "InlineMeSuggester"})
@Deprecated
public static <E extends EppResource> ImmutableMap<String, E> loadResources(
Class<E> clazz, Collection<String> foreignKeys, DateTime now) {
return loadMostRecentResourceObjects(clazz, foreignKeys, false).entrySet().stream()
.filter(e -> now.isBefore(e.getValue().getDeletionDateTime()))
.collect(toImmutableMap(Entry::getKey, e -> (E) e.getValue().cloneProjectedAtTime(now)));
return loadResources(clazz, foreignKeys, toInstant(now));
}
/**
@@ -181,7 +221,7 @@ public final class ForeignKeyUtils {
.collect(
toImmutableMap(
row -> (String) row[0],
row -> new MostRecentResource((String) row[1], (DateTime) row[2]))));
row -> new MostRecentResource((String) row[1], (Instant) row[2]))));
}
/** Method to load the most recent {@link EppResource}s for the given foreign keys. */
@@ -291,9 +331,28 @@ public final class ForeignKeyUtils {
*
* <p>Don't use the cached version of this method unless you really need it for performance
* reasons, and are OK with the trade-offs in loss of transactional consistency.
*
* @deprecated Use {@link #loadKeysByCacheIfEnabled(Class, Collection, Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static <E extends EppResource> ImmutableMap<String, VKey<E>> loadKeysByCacheIfEnabled(
Class<E> clazz, Collection<String> foreignKeys, DateTime now) {
return loadKeysByCacheIfEnabled(clazz, foreignKeys, toInstant(now));
}
/**
* Load a list of {@link VKey} to {@link EppResource} instances by class and foreign key strings
* that are active at or after the specified moment in time, using the cache if enabled.
*
* <p>The returned map will omit any keys for which the {@link EppResource} doesn't exist or has
* been soft-deleted.
*
* <p>Don't use the cached version of this method unless you really need it for performance
* reasons, and are OK with the trade-offs in loss of transactional consistency.
*/
public static <E extends EppResource> ImmutableMap<String, VKey<E>> loadKeysByCacheIfEnabled(
Class<E> clazz, Collection<String> foreignKeys, Instant now) {
if (!RegistryConfig.isEppResourceCachingEnabled()) {
return loadKeys(clazz, foreignKeys, now);
}
@@ -308,9 +367,21 @@ public final class ForeignKeyUtils {
e -> VKey.create(clazz, e.getValue().get().repoId())));
}
/** Loads an optional {@link VKey} to an {@link EppResource} using the cache. */
/**
* Loads an optional {@link VKey} to an {@link EppResource} using the cache.
*
* @deprecated Use {@link #loadKeyByCache(Class, String, Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static <E extends EppResource> Optional<VKey<E>> loadKeyByCache(
Class<E> clazz, String foreignKey, DateTime now) {
return loadKeyByCache(clazz, foreignKey, toInstant(now));
}
/** Loads an optional {@link VKey} to an {@link EppResource} using the cache. */
public static <E extends EppResource> Optional<VKey<E>> loadKeyByCache(
Class<E> clazz, String foreignKey, Instant now) {
return foreignKeyToRepoIdCache
.get(VKey.create(clazz, foreignKey))
.filter(mrr -> now.isBefore(mrr.deletionTime()))
@@ -407,14 +478,57 @@ public final class ForeignKeyUtils {
*
* <p>Do not call this cached version for anything that needs transactional consistency. It should
* only be used when it's OK if the data is potentially being out of date, e.g. RDAP.
*
* @deprecated Use {@link #loadResourceByCacheIfEnabled(Class, String, Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static <E extends EppResource> Optional<E> loadResourceByCacheIfEnabled(
Class<E> clazz, String foreignKey, DateTime now) {
return loadResourceByCacheIfEnabled(clazz, foreignKey, toInstant(now));
}
/**
* Loads the last created version of an {@link EppResource} from the database by foreign key,
* using a cache, if caching is enabled in config settings.
*
* <p>Returns null if no resource with this foreign key was ever created, or if the most recently
* created resource was deleted before time "now".
*
* <p>Loading an {@link EppResource} by itself is not sufficient to know its current state since
* it may have various expirable conditions and status values that might implicitly change its
* state as time progresses even if it has not been updated in the database. Rather, the resource
* must be combined with a timestamp to view its current state. We use a global last updated
* timestamp to guarantee monotonically increasing write times, and forward our projected time to
* the greater of this timestamp or "now". This guarantees that we're not projecting into the
* past.
*
* <p>Do not call this cached version for anything that needs transactional consistency. It should
* only be used when it's OK if the data is potentially being out of date, e.g. RDAP.
*/
public static <E extends EppResource> Optional<E> loadResourceByCacheIfEnabled(
Class<E> clazz, String foreignKey, Instant now) {
return RegistryConfig.isEppResourceCachingEnabled()
? loadResourceByCache(clazz, foreignKey, now)
: loadResource(clazz, foreignKey, now);
}
/**
* Loads the last created version of an {@link EppResource} from the replica database by foreign
* key, using a cache.
*
* <p>This method ignores the config setting for caching, and is reserved for use cases that can
* tolerate slightly stale data.
*
* @deprecated Use {@link #loadResourceByCache(Class, String, Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static <E extends EppResource> Optional<E> loadResourceByCache(
Class<E> clazz, String foreignKey, DateTime now) {
return loadResourceByCache(clazz, foreignKey, toInstant(now));
}
/**
* Loads the last created version of an {@link EppResource} from the replica database by foreign
* key, using a cache.
@@ -424,11 +538,11 @@ public final class ForeignKeyUtils {
*/
@SuppressWarnings("unchecked")
public static <E extends EppResource> Optional<E> loadResourceByCache(
Class<E> clazz, String foreignKey, DateTime now) {
Class<E> clazz, String foreignKey, Instant now) {
return (Optional<E>)
foreignKeyToResourceCache
.get(VKey.create(clazz, foreignKey))
.filter(e -> now.isBefore(e.getDeletionDateTime()))
.map(e -> e.cloneProjectedAtTime(now));
.filter(e -> now.isBefore(e.getDeletionTime()))
.map(e -> e.cloneProjectedAtInstant(now));
}
}

View File

@@ -16,6 +16,7 @@ package google.registry.model;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Maps.transformValues;
import static google.registry.util.DateTimeUtils.ISO_8601_FORMATTER;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static java.util.stream.Collectors.toCollection;
@@ -29,6 +30,7 @@ import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
@@ -92,7 +94,8 @@ public abstract class ImmutableObject implements Cloneable {
@Override
public boolean equals(Object other) {
return other instanceof ImmutableObject && equalsImmutableObject((ImmutableObject) other);
return other instanceof ImmutableObject immutableObject
&& equalsImmutableObject(immutableObject);
}
@Override
@@ -138,7 +141,11 @@ public abstract class ImmutableObject implements Cloneable {
public String toString() {
NavigableMap<String, Object> sortedFields = new TreeMap<>();
for (Entry<Field, Object> entry : getSignificantFields().entrySet()) {
sortedFields.put(entry.getKey().getName(), entry.getValue());
Object value = entry.getValue();
if (value instanceof Instant instant) {
value = ISO_8601_FORMATTER.format(instant);
}
sortedFields.put(entry.getKey().getName(), value);
}
return toStringHelper(sortedFields);
}
@@ -149,9 +156,14 @@ public abstract class ImmutableObject implements Cloneable {
NavigableMap<String, Object> sortedFields = new TreeMap<>();
for (Entry<Field, Object> entry : getSignificantFields().entrySet()) {
Field field = entry.getKey();
Object value = entry.getValue();
sortedFields.put(
field.getName(), field.isAnnotationPresent(DoNotHydrate.class) ? value : hydrate(value));
Object value =
field.isAnnotationPresent(DoNotHydrate.class)
? entry.getValue()
: hydrate(entry.getValue());
if (value instanceof Instant instant) {
value = ISO_8601_FORMATTER.format(instant);
}
sortedFields.put(field.getName(), value);
}
return toStringHelper(sortedFields);
}
@@ -165,14 +177,17 @@ public abstract class ImmutableObject implements Cloneable {
/** Helper function to recursively hydrate an ImmutableObject. */
private static Object hydrate(Object value) {
if (value instanceof Map) {
return transformValues((Map<?, ?>) value, ImmutableObject::hydrate);
if (value instanceof Map map) {
return transformValues(map, ImmutableObject::hydrate);
}
if (value instanceof Collection) {
return transform((Collection<?>) value, ImmutableObject::hydrate);
if (value instanceof Collection collection) {
return transform(collection, ImmutableObject::hydrate);
}
if (value instanceof ImmutableObject) {
return ((ImmutableObject) value).toHydratedString();
if (value instanceof ImmutableObject immutableObject) {
return immutableObject.toHydratedString();
}
if (value instanceof Instant instant) {
return ISO_8601_FORMATTER.format(instant);
}
return value;
}
@@ -181,22 +196,21 @@ public abstract class ImmutableObject implements Cloneable {
private static Object toMapRecursive(Object o) {
if (o == null) {
return null;
} else if (o instanceof ImmutableObject) {
} else if (o instanceof ImmutableObject immutableObject) {
// LinkedHashMap to preserve field ordering and because ImmutableMap forbids null
// values.
Map<String, Object> result = new LinkedHashMap<>();
for (Entry<Field, Object> entry : ((ImmutableObject) o).getSignificantFields().entrySet()) {
for (Entry<Field, Object> entry : immutableObject.getSignificantFields().entrySet()) {
Field field = entry.getKey();
if (!field.isAnnotationPresent(IgnoredInDiffableMap.class)) {
result.put(field.getName(), toMapRecursive(entry.getValue()));
}
}
return result;
} else if (o instanceof Map) {
return transformValues((Map<?, ?>) o, ImmutableObject::toMapRecursive);
} else if (o instanceof Set) {
return ((Set<?>) o)
.stream()
} else if (o instanceof Map map) {
return transformValues(map, ImmutableObject::toMapRecursive);
} else if (o instanceof Set<?> set) {
return set.stream()
.map(ImmutableObject::toMapRecursive)
// We can't use toImmutableSet here, because values can be null (especially since the
// original ImmutableObject might have been the result of a cloneEmptyToNull call).
@@ -204,13 +218,14 @@ public abstract class ImmutableObject implements Cloneable {
// We can't use toSet either, because we want to preserve order. So we use LinkedHashSet
// instead.
.collect(toCollection(LinkedHashSet::new));
} else if (o instanceof Collection) {
return ((Collection<?>) o)
.stream()
} else if (o instanceof Collection<?> collection) {
return collection.stream()
.map(ImmutableObject::toMapRecursive)
// We can't use toImmutableList here, because values can be null (especially since the
// original ImmutableObject might have been the result of a cloneEmptyToNull call).
.collect(toList());
} else if (o instanceof Instant instant) {
return ISO_8601_FORMATTER.format(instant);
} else if (o instanceof Number || o instanceof Boolean) {
return o;
} else {

View File

@@ -167,9 +167,9 @@ public class ModelUtils {
// Recurse into maps with ImmutableObject values.
return transformValues((Map<?, ?>) obj, ModelUtils::cloneEmptyToNullRecursive);
}
if (obj instanceof ImmutableObject) {
if (obj instanceof ImmutableObject immutableObject) {
// Recurse on the fields of an ImmutableObject.
ImmutableObject copy = ImmutableObject.clone((ImmutableObject) obj);
ImmutableObject copy = ImmutableObject.clone(immutableObject);
for (Field field : getAllFields(obj.getClass()).values()) {
Object oldValue = getFieldValue(obj, field);
Object newValue = cloneEmptyToNullRecursive(oldValue);

View File

@@ -17,6 +17,8 @@ package google.registry.model;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
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;
@@ -32,6 +34,7 @@ import google.registry.model.transfer.DomainTransferData;
import google.registry.model.transfer.TransferResponse;
import google.registry.model.transfer.TransferResponse.DomainTransferResponse;
import google.registry.model.transfer.TransferStatus;
import java.time.Instant;
import org.joda.time.DateTime;
/** Static utility functions for domain transfers. */
@@ -67,14 +70,28 @@ public final class ResourceTransferUtils {
* specified status and date.
*/
public static PendingActionNotificationResponse createPendingTransferNotificationResponse(
Domain domain, Trid transferRequestTrid, boolean actionResult, DateTime processedDate) {
Domain domain, Trid transferRequestTrid, boolean actionResult, Instant processedDate) {
return DomainPendingActionNotificationResponse.create(
domain.getDomainName(), actionResult, transferRequestTrid, processedDate);
}
/**
* Create a pending action notification response indicating the resolution of a transfer.
*
* @deprecated Use {@link #createPendingTransferNotificationResponse(Domain, Trid, boolean,
* Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static PendingActionNotificationResponse createPendingTransferNotificationResponse(
Domain domain, Trid transferRequestTrid, boolean actionResult, DateTime processedDate) {
return createPendingTransferNotificationResponse(
domain, transferRequestTrid, actionResult, toInstant(processedDate));
}
/** If there is a transfer out, delete the server-approve entities and enqueue a poll message. */
public static void handlePendingTransferOnDelete(
Domain domain, Domain newDomain, DateTime now, HistoryEntry historyEntry) {
Domain domain, Domain newDomain, Instant now, HistoryEntry historyEntry) {
if (!domain.getStatusValues().contains(StatusValue.PENDING_TRANSFER)) {
return;
}
@@ -94,6 +111,18 @@ public final class ResourceTransferUtils {
.build());
}
/**
* If there is a transfer out, delete the server-approve entities and enqueue a poll message.
*
* @deprecated Use {@link #handlePendingTransferOnDelete(Domain, Domain, Instant, HistoryEntry)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static void handlePendingTransferOnDelete(
Domain domain, Domain newDomain, DateTime now, HistoryEntry historyEntry) {
handlePendingTransferOnDelete(domain, newDomain, toInstant(now), historyEntry);
}
/**
* Turn a domain into a builder with its pending transfer resolved.
*
@@ -102,7 +131,7 @@ public final class ResourceTransferUtils {
* sets the expiration time of the last pending transfer to now.
*/
private static Domain.Builder resolvePendingTransfer(
Domain domain, TransferStatus transferStatus, DateTime now) {
Domain domain, TransferStatus transferStatus, Instant now) {
checkArgument(
domain.getStatusValues().contains(StatusValue.PENDING_TRANSFER),
"Domain is not in pending transfer status.");
@@ -116,7 +145,7 @@ public final class ResourceTransferUtils {
.getTransferData()
.copyConstantFieldsToBuilder()
.setTransferStatus(transferStatus)
.setPendingTransferExpirationTime(checkNotNull(now))
.setPendingTransferExpirationTime(toDateTime(checkNotNull(now)))
.build());
}
@@ -129,15 +158,27 @@ public final class ResourceTransferUtils {
* transfer to now.
*/
public static Domain approvePendingTransfer(
Domain domain, TransferStatus transferStatus, DateTime now) {
Domain domain, TransferStatus transferStatus, Instant now) {
checkArgument(transferStatus.isApproved(), "Not an approval transfer status");
Domain.Builder builder = resolvePendingTransfer(domain, transferStatus, now);
return builder
.setLastTransferTime(now)
.setLastTransferTime(toDateTime(now))
.setPersistedCurrentSponsorRegistrarId(domain.getTransferData().getGainingRegistrarId())
.build();
}
/**
* Resolve a pending transfer by awarding it to the gaining client.
*
* @deprecated Use {@link #approvePendingTransfer(Domain, TransferStatus, Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static Domain approvePendingTransfer(
Domain domain, TransferStatus transferStatus, DateTime now) {
return approvePendingTransfer(domain, transferStatus, toInstant(now));
}
/**
* Resolve a pending transfer by denying it.
*
@@ -147,11 +188,23 @@ public final class ResourceTransferUtils {
* and sets the last EPP update client id to the given client id.
*/
public static Domain denyPendingTransfer(
Domain domain, TransferStatus transferStatus, DateTime now, String lastEppUpdateRegistrarId) {
Domain domain, TransferStatus transferStatus, Instant now, String lastEppUpdateRegistrarId) {
checkArgument(transferStatus.isDenied(), "Not a denial transfer status");
return resolvePendingTransfer(domain, transferStatus, now)
.setLastEppUpdateTime(now)
.setLastEppUpdateTime(toDateTime(now))
.setLastEppUpdateRegistrarId(lastEppUpdateRegistrarId)
.build();
}
/**
* Resolve a pending transfer by denying it.
*
* @deprecated Use {@link #denyPendingTransfer(Domain, TransferStatus, Instant, String)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static Domain denyPendingTransfer(
Domain domain, TransferStatus transferStatus, DateTime now, String lastEppUpdateRegistrarId) {
return denyPendingTransfer(domain, transferStatus, toInstant(now), lastEppUpdateRegistrarId);
}
}

View File

@@ -15,12 +15,15 @@
package google.registry.model;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static google.registry.util.DateTimeUtils.START_INSTANT;
import static google.registry.util.DateTimeUtils.toDateTime;
import static google.registry.util.DateTimeUtils.toInstant;
import google.registry.persistence.EntityCallbacksListener.RecursivePrePersist;
import google.registry.persistence.EntityCallbacksListener.RecursivePreUpdate;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import java.time.Instant;
import java.util.Optional;
import javax.annotation.Nullable;
import org.joda.time.DateTime;
@@ -30,7 +33,7 @@ import org.joda.time.DateTime;
public class UpdateAutoTimestamp extends ImmutableObject implements UnsafeSerializable {
@Column(name = "updateTimestamp")
DateTime lastUpdateTime;
Instant lastUpdateTime;
// Unfortunately, we cannot use the @UpdateTimestamp annotation on "lastUpdateTime" in this class
// because Hibernate does not allow it to be used on @Embeddable classes, see
@@ -38,17 +41,35 @@ public class UpdateAutoTimestamp extends ImmutableObject implements UnsafeSerial
@RecursivePrePersist
@RecursivePreUpdate
public void setTimestamp() {
lastUpdateTime = tm().getTransactionTime();
lastUpdateTime = tm().getTxTime();
}
/** Returns the timestamp, or {@code START_OF_TIME} if it's null. */
public DateTime getTimestamp() {
return Optional.ofNullable(lastUpdateTime).orElse(START_OF_TIME);
/** Returns the timestamp, or {@code START_INSTANT} if it's null. */
public Instant getTimestamp() {
return Optional.ofNullable(lastUpdateTime).orElse(START_INSTANT);
}
public static UpdateAutoTimestamp create(@Nullable DateTime timestamp) {
/**
* @deprecated Use {@link #getTimestamp()}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public DateTime getTimestampDateTime() {
return toDateTime(getTimestamp());
}
public static UpdateAutoTimestamp create(@Nullable Instant timestamp) {
UpdateAutoTimestamp instance = new UpdateAutoTimestamp();
instance.lastUpdateTime = timestamp;
return instance;
}
/**
* @deprecated Use {@link #create(Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static UpdateAutoTimestamp create(@Nullable DateTime timestamp) {
return create(toInstant(timestamp));
}
}

View File

@@ -19,6 +19,7 @@ import jakarta.persistence.Access;
import jakarta.persistence.AccessType;
import jakarta.persistence.MappedSuperclass;
import jakarta.xml.bind.annotation.XmlTransient;
import java.time.Instant;
/**
* Base class for entities that contains an {@link UpdateAutoTimestamp} which is updated every time
@@ -38,7 +39,7 @@ public abstract class UpdateAutoTimestampEntity extends ImmutableObject
// Prevents subclasses from unexpectedly accessing as property (e.g., Host), which would
// require an unnecessary non-private setter method.
@Access(AccessType.FIELD)
UpdateAutoTimestamp updateTimestamp = UpdateAutoTimestamp.create(null);
UpdateAutoTimestamp updateTimestamp = UpdateAutoTimestamp.create((Instant) null);
/** Get the {@link UpdateAutoTimestamp} for this entity. */
public UpdateAutoTimestamp getUpdateTimestamp() {
@@ -62,7 +63,7 @@ public abstract class UpdateAutoTimestampEntity extends ImmutableObject
* object being persisted.
*/
protected void resetUpdateTimestamp() {
this.updateTimestamp = UpdateAutoTimestamp.create(null);
this.updateTimestamp = UpdateAutoTimestamp.create((Instant) null);
}
/**

View File

@@ -17,6 +17,8 @@ package google.registry.model.billing;
import static com.google.common.base.Preconditions.checkNotNull;
import static google.registry.util.CollectionUtils.forceEmptyToNull;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
import static google.registry.util.DateTimeUtils.toDateTime;
import static google.registry.util.DateTimeUtils.toInstant;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.google.common.collect.ImmutableSet;
@@ -33,6 +35,7 @@ import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
import java.time.Instant;
import java.util.Set;
import javax.annotation.Nullable;
import org.joda.time.DateTime;
@@ -143,7 +146,7 @@ public abstract class BillingBase extends ImmutableObject
/** When this event was created. For recurrence events, this is also the recurrence start time. */
@Column(nullable = false)
DateTime eventTime;
Instant eventTime;
/** The reason for the bill. */
@Enumerated(EnumType.STRING)
@@ -170,7 +173,16 @@ public abstract class BillingBase extends ImmutableObject
return domainRepoId;
}
/**
* @deprecated Use {@link #getEventTimeInstant()}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public DateTime getEventTime() {
return toDateTime(eventTime);
}
public Instant getEventTimeInstant() {
return eventTime;
}
@@ -226,7 +238,16 @@ public abstract class BillingBase extends ImmutableObject
return thisCastToDerived();
}
/**
* @deprecated Use {@link #setEventTime(Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public B setEventTime(DateTime eventTime) {
return setEventTime(toInstant(eventTime));
}
public B setEventTime(Instant eventTime) {
getInstance().eventTime = eventTime;
return thisCastToDerived();
}

View File

@@ -17,6 +17,8 @@ package google.registry.model.billing;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static google.registry.util.DateTimeUtils.toDateTime;
import static google.registry.util.DateTimeUtils.toInstant;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.token.AllocationToken;
@@ -29,6 +31,7 @@ import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import java.time.Instant;
import java.util.Optional;
import javax.annotation.Nullable;
import org.joda.money.Money;
@@ -61,7 +64,7 @@ public class BillingEvent extends BillingBase {
Money cost;
/** When the cost should be billed. */
DateTime billingTime;
Instant billingTime;
/**
* The period in years of the action being billed for, if applicable, otherwise null. Used for
@@ -75,7 +78,7 @@ public class BillingEvent extends BillingBase {
* needs to be undone, a query on this field will return the complete set of potentially bad
* events.
*/
DateTime syntheticCreationTime;
Instant syntheticCreationTime;
/**
* For {@link Flag#SYNTHETIC} events, a {@link VKey} to the {@link BillingRecurrence} from which
@@ -105,7 +108,16 @@ public class BillingEvent extends BillingBase {
return cost;
}
/**
* @deprecated Use {@link #getBillingTimeInstant()}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public DateTime getBillingTime() {
return toDateTime(billingTime);
}
public Instant getBillingTimeInstant() {
return billingTime;
}
@@ -113,7 +125,16 @@ public class BillingEvent extends BillingBase {
return periodYears;
}
/**
* @deprecated Use {@link #getSyntheticCreationTimeInstant()}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public DateTime getSyntheticCreationTime() {
return toDateTime(syntheticCreationTime);
}
public Instant getSyntheticCreationTimeInstant() {
return syntheticCreationTime;
}
@@ -165,12 +186,30 @@ public class BillingEvent extends BillingBase {
return this;
}
/**
* @deprecated Use {@link #setBillingTime(Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public Builder setBillingTime(DateTime billingTime) {
return setBillingTime(toInstant(billingTime));
}
public Builder setBillingTime(Instant billingTime) {
getInstance().billingTime = billingTime;
return this;
}
/**
* @deprecated Use {@link #setSyntheticCreationTime(Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public Builder setSyntheticCreationTime(DateTime syntheticCreationTime) {
return setSyntheticCreationTime(toInstant(syntheticCreationTime));
}
public Builder setSyntheticCreationTime(Instant syntheticCreationTime) {
getInstance().syntheticCreationTime = syntheticCreationTime;
return this;
}
@@ -197,7 +236,7 @@ public class BillingEvent extends BillingBase {
checkState(!instance.cost.isNegative(), "Costs should be non-negative.");
// TODO(mcilwain): Enforce this check on all billing events (not just more recent ones)
// post-migration after we add the missing period years values in SQL.
if (instance.eventTime.isAfter(DateTime.parse("2019-01-01T00:00:00Z"))) {
if (instance.eventTime.isAfter(Instant.parse("2019-01-01T00:00:00Z"))) {
checkState(
instance.reason.hasPeriodYears() == (instance.periodYears != null),
"Period years must be set if and only if reason is "

View File

@@ -16,7 +16,10 @@ package google.registry.model.billing;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.END_INSTANT;
import static google.registry.util.DateTimeUtils.minusYears;
import static google.registry.util.DateTimeUtils.toDateTime;
import static google.registry.util.DateTimeUtils.toInstant;
import google.registry.model.common.TimeOfYear;
import google.registry.persistence.VKey;
@@ -30,6 +33,7 @@ import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import java.time.Instant;
import java.util.Optional;
import javax.annotation.Nullable;
import org.joda.money.Money;
@@ -61,17 +65,17 @@ public class BillingRecurrence extends BillingBase {
* The billing event recurs every year between {@link #eventTime} and this time on the [month,
* day, time] specified in {@link #recurrenceTimeOfYear}.
*/
DateTime recurrenceEndTime;
Instant recurrenceEndTime;
/**
* The most recent {@link DateTime} when this recurrence was expanded.
* The most recent {@link Instant} when this recurrence was expanded.
*
* <p>We only bother checking recurrences for potential expansion if this is at least one year in
* the past. If it's more recent than that, it means that the recurrence was already expanded too
* recently to need to be checked again (as domains autorenew each year).
*/
@Column(nullable = false)
DateTime recurrenceLastExpansion;
Instant recurrenceLastExpansion;
/**
* The eventTime recurs every year on this [month, day, time] between {@link #eventTime} and
@@ -109,11 +113,29 @@ public class BillingRecurrence extends BillingBase {
@Column(name = "renewalPriceBehavior", nullable = false)
RenewalPriceBehavior renewalPriceBehavior = RenewalPriceBehavior.DEFAULT;
/**
* @deprecated Use {@link #getRecurrenceEndTimeInstant()}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public DateTime getRecurrenceEndTime() {
return toDateTime(recurrenceEndTime);
}
public Instant getRecurrenceEndTimeInstant() {
return recurrenceEndTime;
}
/**
* @deprecated Use {@link #getRecurrenceLastExpansionInstant()}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public DateTime getRecurrenceLastExpansion() {
return toDateTime(recurrenceLastExpansion);
}
public Instant getRecurrenceLastExpansionInstant() {
return recurrenceLastExpansion;
}
@@ -155,12 +177,30 @@ public class BillingRecurrence extends BillingBase {
super(instance);
}
/**
* @deprecated Use {@link #setRecurrenceEndTime(Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public Builder setRecurrenceEndTime(DateTime recurrenceEndTime) {
return setRecurrenceEndTime(toInstant(recurrenceEndTime));
}
public Builder setRecurrenceEndTime(Instant recurrenceEndTime) {
getInstance().recurrenceEndTime = recurrenceEndTime;
return this;
}
/**
* @deprecated Use {@link #setRecurrenceLastExpansion(Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public Builder setRecurrenceLastExpansion(DateTime recurrenceLastExpansion) {
return setRecurrenceLastExpansion(toInstant(recurrenceLastExpansion));
}
public Builder setRecurrenceLastExpansion(Instant recurrenceLastExpansion) {
getInstance().recurrenceLastExpansion = recurrenceLastExpansion;
return this;
}
@@ -187,15 +227,15 @@ public class BillingRecurrence extends BillingBase {
// ensures that it will be expanded on 2/28 next year and included in the February invoice.
instance.recurrenceLastExpansion =
Optional.ofNullable(instance.recurrenceLastExpansion)
.orElse(instance.eventTime.minusYears(1));
.orElse(minusYears(instance.eventTime, 1));
checkArgument(
instance.renewalPriceBehavior == RenewalPriceBehavior.SPECIFIED
^ instance.renewalPrice == null,
"Renewal price can have a value if and only if the renewal price behavior is"
+ " SPECIFIED");
instance.recurrenceTimeOfYear = TimeOfYear.fromDateTime(instance.eventTime);
instance.recurrenceTimeOfYear = TimeOfYear.fromInstant(instance.eventTime);
instance.recurrenceEndTime =
Optional.ofNullable(instance.recurrenceEndTime).orElse(END_OF_TIME);
Optional.ofNullable(instance.recurrenceEndTime).orElse(END_INSTANT);
return super.build();
}
}

View File

@@ -16,7 +16,9 @@ package google.registry.model.common;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static google.registry.util.DateTimeUtils.START_INSTANT;
import static google.registry.util.DateTimeUtils.toDateTime;
import static google.registry.util.DateTimeUtils.toInstant;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
@@ -31,6 +33,7 @@ import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import jakarta.persistence.IdClass;
import java.time.Instant;
import java.util.Optional;
import org.joda.time.DateTime;
@@ -123,7 +126,7 @@ public class Cursor extends UpdateAutoTimestampEntity {
String scope;
@Column(nullable = false)
DateTime cursorTime = START_OF_TIME;
Instant cursorTime = START_INSTANT;
@Override
public VKey<Cursor> createVKey() {
@@ -143,10 +146,18 @@ public class Cursor extends UpdateAutoTimestampEntity {
return VKey.create(Cursor.class, new CursorId(type, scope));
}
public DateTime getLastUpdateTime() {
public Instant getLastUpdateTime() {
return getUpdateTimestamp().getTimestamp();
}
/**
* @deprecated Use {@link #getLastUpdateTime()}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public DateTime getLastUpdateDateTime() {
return toDateTime(getUpdateTimestamp().getTimestamp());
}
public String getScope() {
return scope;
@@ -168,21 +179,39 @@ public class Cursor extends UpdateAutoTimestampEntity {
}
/** Creates a new global cursor instance. */
public static Cursor createGlobal(CursorType cursorType, DateTime cursorTime) {
public static Cursor createGlobal(CursorType cursorType, Instant cursorTime) {
return create(cursorType, cursorTime, GLOBAL);
}
/**
* @deprecated Use {@link #createGlobal(CursorType, Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static Cursor createGlobal(CursorType cursorType, DateTime cursorTime) {
return createGlobal(cursorType, toInstant(cursorTime));
}
/** Creates a new cursor instance with a given {@link Tld} scope. */
public static Cursor createScoped(CursorType cursorType, DateTime cursorTime, Tld scope) {
public static Cursor createScoped(CursorType cursorType, Instant cursorTime, Tld scope) {
checkNotNull(scope, "Cursor scope cannot be null");
return create(cursorType, cursorTime, scope.getTldStr());
}
/**
* @deprecated Use {@link #createScoped(CursorType, Instant, Tld)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static Cursor createScoped(CursorType cursorType, DateTime cursorTime, Tld scope) {
return createScoped(cursorType, toInstant(cursorTime), scope);
}
/**
* Creates a new cursor instance with a given TLD scope, or global if the scope is {@link
* #GLOBAL}.
*/
private static Cursor create(CursorType cursorType, DateTime cursorTime, String scope) {
private static Cursor create(CursorType cursorType, Instant cursorTime, String scope) {
checkNotNull(cursorTime, "Cursor time cannot be null");
checkValidCursorTypeForScope(cursorType, scope);
Cursor instance = new Cursor();
@@ -193,13 +222,31 @@ public class Cursor extends UpdateAutoTimestampEntity {
}
/**
* Returns the current time for a given cursor, or {@code START_OF_TIME} if the cursor is null.
* Returns the current time for a given cursor, or {@code START_INSTANT} if the cursor is null.
*/
public static DateTime getCursorTimeOrStartOfTime(Optional<Cursor> cursor) {
return cursor.map(Cursor::getCursorTime).orElse(START_OF_TIME);
public static Instant getCursorTimeOrStartOfTimeInstant(Optional<Cursor> cursor) {
return cursor.map(Cursor::getCursorTimeInstant).orElse(START_INSTANT);
}
/**
* @deprecated Use {@link #getCursorTimeOrStartOfTimeInstant(Optional)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static DateTime getCursorTimeOrStartOfTime(Optional<Cursor> cursor) {
return toDateTime(getCursorTimeOrStartOfTimeInstant(cursor));
}
/**
* @deprecated Use {@link #getCursorTimeInstant()}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public DateTime getCursorTime() {
return toDateTime(cursorTime);
}
public Instant getCursorTimeInstant() {
return cursorTime;
}

View File

@@ -20,6 +20,7 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.config.RegistryConfig.getSingletonCacheRefreshDuration;
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.DateTimeUtils.toInstant;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
@@ -31,6 +32,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
import google.registry.model.Buildable;
import google.registry.model.CacheUtils;
import google.registry.model.EntityYamlUtils.TimedTransitionPropertyFeatureStatusDeserializer;
@@ -42,6 +44,7 @@ import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import java.time.Instant;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -180,6 +183,10 @@ public class FeatureFlag extends ImmutableObject implements Buildable {
}
public FeatureStatus getStatus(DateTime time) {
return getStatus(toInstant(time));
}
public FeatureStatus getStatus(Instant time) {
return status.getValueAtTime(time);
}
@@ -188,7 +195,7 @@ public class FeatureFlag extends ImmutableObject implements Buildable {
*/
public static boolean isActiveNow(FeatureName featureName) {
tm().assertInTransaction();
return isActiveAt(featureName, tm().getTransactionTime());
return isActiveAt(featureName, tm().getTxTime());
}
/**
@@ -196,11 +203,19 @@ public class FeatureFlag extends ImmutableObject implements Buildable {
* doesn't exist.
*/
public static boolean isActiveAt(FeatureName featureName, DateTime dateTime) {
return isActiveAt(featureName, toInstant(dateTime));
}
/**
* Returns whether the flag is active at the given time, or else the flag's default value if it
* doesn't exist.
*/
public static boolean isActiveAt(FeatureName featureName, Instant instant) {
tm().assertInTransaction();
return CACHE
.get(featureName)
.map(flag -> flag.getStatus(dateTime).equals(ACTIVE))
.orElse(featureName.getDefaultStatus().equals(ACTIVE));
.map(flag -> flag.getStatus(instant).equals(ACTIVE))
.orElseGet(() -> featureName.getDefaultStatus().equals(ACTIVE));
}
@Override
@@ -230,7 +245,15 @@ public class FeatureFlag extends ImmutableObject implements Buildable {
}
public Builder setStatusMap(ImmutableSortedMap<DateTime, FeatureStatus> statusMap) {
getInstance().status = TimedTransitionProperty.fromValueMap(statusMap);
return setStatusMapInstant(
statusMap.entrySet().stream()
.collect(
ImmutableSortedMap.toImmutableSortedMap(
Ordering.natural(), e -> toInstant(e.getKey()), Map.Entry::getValue)));
}
public Builder setStatusMapInstant(ImmutableSortedMap<Instant, FeatureStatus> statusMap) {
getInstance().status = TimedTransitionProperty.fromValueMapInstant(statusMap);
return this;
}
}

View File

@@ -16,10 +16,16 @@ package google.registry.model.common;
import static com.google.common.collect.DiscreteDomain.integers;
import static com.google.common.collect.ImmutableList.toImmutableList;
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.isAtOrAfter;
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
import static google.registry.util.DateTimeUtils.minusYears;
import static google.registry.util.DateTimeUtils.plusYears;
import static google.registry.util.DateTimeUtils.toDateTime;
import static google.registry.util.DateTimeUtils.toInstant;
import static org.joda.time.DateTimeZone.UTC;
import com.google.common.base.Splitter;
@@ -28,6 +34,11 @@ import com.google.common.collect.Range;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
import jakarta.persistence.Embeddable;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.List;
import org.joda.time.DateTime;
@@ -56,25 +67,44 @@ public class TimeOfYear extends ImmutableObject implements UnsafeSerializable {
*
* <p>This handles leap years in an intentionally peculiar way by always treating February 29 as
* February 28. It is impossible to construct a {@link TimeOfYear} for February 29th.
*
* @deprecated Use {@link #fromInstant(Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static TimeOfYear fromDateTime(DateTime dateTime) {
DateTime nextYear = dateTime.plusYears(1); // This turns February 29 into February 28.
return fromInstant(toInstant(dateTime));
}
/**
* Constructs a {@link TimeOfYear} from an {@link Instant}.
*
* <p>This handles leap years in an intentionally peculiar way by always treating February 29 as
* February 28. It is impossible to construct a {@link TimeOfYear} for February 29th.
*/
public static TimeOfYear fromInstant(Instant instant) {
ZonedDateTime zdt = ZonedDateTime.ofInstant(instant, ZoneOffset.UTC);
int month = zdt.getMonthValue();
int day = zdt.getDayOfMonth();
if (month == 2 && day == 29) {
day = 28;
}
TimeOfYear instance = new TimeOfYear();
instance.timeString = String.format(
"%02d %02d %08d",
nextYear.getMonthOfYear(),
nextYear.getDayOfMonth(),
nextYear.getMillisOfDay());
instance.timeString =
String.format("%02d %02d %08d", month, day, zdt.toLocalTime().toNanoOfDay() / 1000000);
return instance;
}
/**
* Returns an {@link Iterable} of {@link DateTime}s of every recurrence of this particular
* time of year within a given {@link Range} (usually one spanning many years).
* Returns an {@link Iterable} of {@link DateTime}s of every recurrence of this particular time of
* year within a given {@link Range} (usually one spanning many years).
*
* <p>WARNING: This can return a potentially very large {@link Iterable} if {@code END_OF_TIME}
* is used as the upper endpoint of the range.
* <p>WARNING: This can return a potentially very large {@link Iterable} if {@code END_OF_TIME} is
* used as the upper endpoint of the range.
*
* @deprecated Use {@link #getInstancesInRangeInstant(Range)}
*/
@Deprecated
public Iterable<DateTime> getInstancesInRange(Range<DateTime> range) {
// In registry world, all dates are within START_OF_TIME and END_OF_TIME, so restrict any
// ranges without bounds to our notion of zero-to-infinity.
@@ -89,16 +119,77 @@ public class TimeOfYear extends ImmutableObject implements UnsafeSerializable {
.collect(toImmutableList());
}
/** Get the first {@link DateTime} with this month/day/millis that is at or after the start. */
public DateTime getNextInstanceAtOrAfter(DateTime start) {
DateTime withSameYear = getDateTimeWithYear(start.getYear());
return isAtOrAfter(withSameYear, start) ? withSameYear : withSameYear.plusYears(1);
/**
* Returns an {@link Iterable} of {@link Instant}s of every recurrence of this particular time of
* year within a given {@link Range} (usually one spanning many years).
*
* <p>WARNING: This can return a potentially very large {@link Iterable} if {@code END_INSTANT} is
* used as the upper endpoint of the range.
*/
public Iterable<Instant> getInstancesInRangeInstant(Range<Instant> range) {
// In registry world, all dates are within START_INSTANT and END_INSTANT, so restrict any
// ranges without bounds to our notion of zero-to-infinity.
Range<Instant> normalizedRange = range.intersection(Range.closed(START_INSTANT, END_INSTANT));
Range<Integer> yearRange =
Range.closed(
ZonedDateTime.ofInstant(normalizedRange.lowerEndpoint(), java.time.ZoneOffset.UTC)
.getYear(),
ZonedDateTime.ofInstant(normalizedRange.upperEndpoint(), java.time.ZoneOffset.UTC)
.getYear());
return ContiguousSet.create(yearRange, integers()).stream()
.map(this::toInstantWithYear)
.filter(normalizedRange)
.collect(toImmutableList());
}
/** Get the first {@link DateTime} with this month/day/millis that is at or before the end. */
/**
* Return a new instant with the same year as the parameter but projected to the month, day, and
* time of day of this object.
*/
private Instant toInstantWithYear(int year) {
List<String> monthDayMillis = Splitter.on(' ').splitToList(timeString);
int month = Integer.parseInt(monthDayMillis.get(0));
int day = Integer.parseInt(monthDayMillis.get(1));
int millis = Integer.parseInt(monthDayMillis.get(2));
return LocalDate.of(year, month, day)
.atTime(LocalTime.ofNanoOfDay(millis * 1000000L))
.toInstant(ZoneOffset.UTC);
}
/**
* Get the first {@link DateTime} with this month/day/millis that is at or after the start.
*
* @deprecated Use {@link #getNextInstanceAtOrAfterInstant(Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public DateTime getNextInstanceAtOrAfter(DateTime start) {
return toDateTime(getNextInstanceAtOrAfterInstant(toInstant(start)));
}
/** Get the first {@link Instant} with this month/day/millis that is at or after the start. */
public Instant getNextInstanceAtOrAfterInstant(Instant start) {
Instant withSameYear =
toInstantWithYear(ZonedDateTime.ofInstant(start, java.time.ZoneOffset.UTC).getYear());
return isAtOrAfter(withSameYear, start) ? withSameYear : plusYears(withSameYear, 1);
}
/**
* Get the first {@link DateTime} with this month/day/millis that is at or before the end.
*
* @deprecated Use {@link #getLastInstanceBeforeOrAtInstant(Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public DateTime getLastInstanceBeforeOrAt(DateTime end) {
DateTime withSameYear = getDateTimeWithYear(end.getYear());
return isBeforeOrAt(withSameYear, end) ? withSameYear : withSameYear.minusYears(1);
return toDateTime(getLastInstanceBeforeOrAtInstant(toInstant(end)));
}
/** Get the first {@link Instant} with this month/day/millis that is at or before the end. */
public Instant getLastInstanceBeforeOrAtInstant(Instant end) {
Instant withSameYear =
toInstantWithYear(ZonedDateTime.ofInstant(end, java.time.ZoneOffset.UTC).getYear());
return isBeforeOrAt(withSameYear, end) ? withSameYear : minusYears(withSameYear, 1);
}
/**

View File

@@ -16,17 +16,22 @@ package google.registry.model.common;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static google.registry.util.CollectionUtils.nullToEmpty;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static com.google.common.collect.ImmutableSortedMap.toImmutableSortedMap;
import static google.registry.util.DateTimeUtils.ISO_8601_FORMATTER;
import static google.registry.util.DateTimeUtils.START_INSTANT;
import static google.registry.util.DateTimeUtils.latestOf;
import static google.registry.util.DateTimeUtils.toDateTime;
import static google.registry.util.DateTimeUtils.toInstant;
import com.fasterxml.jackson.annotation.JsonValue;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Ordering;
import google.registry.model.UnsafeSerializable;
import java.io.Serializable;
import java.util.Iterator;
import java.util.NavigableMap;
import java.time.Instant;
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.joda.time.DateTime;
@@ -34,7 +39,7 @@ import org.joda.time.DateTime;
/**
* An entity property whose value transitions over time. Each value it takes on becomes active at a
* corresponding instant, and remains active until the next transition occurs. At least one "start
* of time" value (corresponding to {@code START_OF_TIME}, i.e. the Unix epoch) must be provided so
* of time" value (corresponding to {@code START_INSTANT}, i.e. the Unix epoch) must be provided so
* that the property will have a value for all possible times.
*/
// Implementation note: this class used to implement the Guava ForwardingMap. This breaks in
@@ -45,125 +50,212 @@ public class TimedTransitionProperty<V extends Serializable> implements UnsafeSe
private static final long serialVersionUID = -7274659848856323290L;
/**
* Returns a new immutable {@link TimedTransitionProperty} representing the given map of {@link
* DateTime} to value {@link V}.
*/
public static <V extends Serializable> TimedTransitionProperty<V> fromValueMap(
ImmutableSortedMap<DateTime, V> valueMap) {
checkArgument(
Ordering.natural().equals(valueMap.comparator()),
"Timed transition value map must have transition time keys in chronological order");
return new TimedTransitionProperty<>(valueMap);
}
/** The map of all the transitions that have been defined for this property. */
private final ImmutableSortedMap<Instant, V> backingMap;
/**
* Returns a new immutable {@link TimedTransitionProperty} with an initial value at {@code
* START_OF_TIME}.
*/
public static <V extends Serializable> TimedTransitionProperty<V> withInitialValue(
V initialValue) {
return fromValueMap(ImmutableSortedMap.of(START_OF_TIME, initialValue));
}
/**
* Validates a new set of transitions and returns the resulting {@link TimedTransitionProperty}.
* Returns a map of the transitions, with the keys formatted as ISO-8601 strings.
*
* @param newTransitions map from {@link DateTime} to transition value {@link V}
* @param allowedTransitions optional map of all possible state-to-state transitions
* @param allowedTransitionMapName optional transition map description string for error messages
* @param initialValue optional initial value; if present, the first transition must have this
* value
* @param badInitialValueErrorMessage option error message string if the initial value is wrong
* <p>This is used for JSON/YAML serialization.
*/
public static <V extends Serializable> TimedTransitionProperty<V> make(
ImmutableSortedMap<DateTime, V> newTransitions,
ImmutableMultimap<V, V> allowedTransitions,
String allowedTransitionMapName,
V initialValue,
String badInitialValueErrorMessage) {
validateTimedTransitionMap(newTransitions, allowedTransitions, allowedTransitionMapName);
checkArgument(
newTransitions.firstEntry().getValue() == initialValue, badInitialValueErrorMessage);
return fromValueMap(newTransitions);
@JsonValue
public ImmutableSortedMap<String, V> getTransitions() {
return backingMap.entrySet().stream()
.collect(
toImmutableSortedMap(
Ordering.natural(),
e -> ISO_8601_FORMATTER.format(e.getKey()),
Map.Entry::getValue));
}
/**
* Validates that a transition map is not null or empty, starts at {@code START_OF_TIME}, and has
* transitions which move from one value to another in allowed ways.
*/
public static <V extends Serializable> void validateTimedTransitionMap(
@Nullable NavigableMap<DateTime, V> transitionMap,
ImmutableMultimap<V, V> allowedTransitions,
String mapName) {
private TimedTransitionProperty(ImmutableSortedMap<Instant, V> backingMap) {
checkArgument(
!nullToEmpty(transitionMap).isEmpty(), "%s map cannot be null or empty.", mapName);
checkArgument(
transitionMap.firstKey().equals(START_OF_TIME),
"%s map must start at START_OF_TIME.",
mapName);
// Check that all transitions between states are allowed.
Iterator<V> it = transitionMap.values().iterator();
V currentState = it.next();
while (it.hasNext()) {
checkArgument(
allowedTransitions.containsKey(currentState),
"%s map cannot transition from %s.",
mapName,
currentState);
V nextState = it.next();
checkArgument(
allowedTransitions.containsEntry(currentState, nextState),
"%s map cannot transition from %s to %s.",
mapName,
currentState,
nextState);
currentState = nextState;
}
}
/** The backing map of {@link DateTime} to the value {@link V} that transitions over time. */
private final ImmutableSortedMap<DateTime, V> backingMap;
/** Returns a new {@link TimedTransitionProperty} backed by the provided map instance. */
private TimedTransitionProperty(NavigableMap<DateTime, V> backingMap) {
checkArgument(
backingMap.get(START_OF_TIME) != null,
backingMap.containsKey(START_INSTANT),
"Must provide transition entry for the start of time (Unix Epoch)");
this.backingMap = ImmutableSortedMap.copyOfSorted(backingMap);
}
/** Returns an empty {@link TimedTransitionProperty}. */
public static <V extends Serializable> TimedTransitionProperty<V> forEmptyMap() {
return new TimedTransitionProperty<>(ImmutableSortedMap.of());
}
/**
* Checks whether this {@link TimedTransitionProperty} is in a valid state, i.e. whether it has a
* transition entry for {@code START_OF_TIME}, and throws {@link IllegalStateException} if not.
* Returns a {@link TimedTransitionProperty} that starts with the given value at {@code
* START_INSTANT}.
*/
public static <V extends Serializable> TimedTransitionProperty<V> withInitialValue(V value) {
return fromValueMapInstant(ImmutableSortedMap.of(START_INSTANT, value));
}
/**
* Returns a {@link TimedTransitionProperty} that contains the transition values and times defined
* in the given map.
*
* <p>The map must contain a value for {@code START_INSTANT}.
*
* @deprecated Use {@link #fromValueMapInstant(ImmutableSortedMap)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static <V extends Serializable> TimedTransitionProperty<V> fromValueMap(
ImmutableSortedMap<DateTime, V> valueMap) {
return fromValueMapInstant(toInstantMap(valueMap));
}
/**
* Returns a {@link TimedTransitionProperty} that contains the transition values and times defined
* in the given map.
*
* <p>The map must contain a value for {@code START_INSTANT}.
*/
public static <V extends Serializable> TimedTransitionProperty<V> fromValueMapInstant(
ImmutableSortedMap<Instant, V> valueMap) {
return new TimedTransitionProperty<>(valueMap);
}
/**
* Returns a {@link TimedTransitionProperty} that contains the transition values and times defined
* in the given map.
*
* <p>The map must contain a value for {@code START_OF_TIME}. The map is also validated against a
* set of allowed transitions.
*
* @deprecated Use {@link #makeInstant(ImmutableSortedMap, ImmutableMultimap, String,
* Serializable, String)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static <V extends Serializable> TimedTransitionProperty<V> make(
ImmutableSortedMap<DateTime, V> valueMap,
ImmutableMultimap<V, V> allowedTransitions,
String mapName,
V initialValue,
String initialValueErrorMessage) {
return makeInstant(
toInstantMap(valueMap),
allowedTransitions,
mapName,
initialValue,
initialValueErrorMessage);
}
/**
* Returns a {@link TimedTransitionProperty} that contains the transition values and times defined
* in the given map.
*
* <p>The map must contain a value for {@code START_INSTANT}. The map is also validated against a
* set of allowed transitions.
*/
public static <V extends Serializable> TimedTransitionProperty<V> makeInstant(
ImmutableSortedMap<Instant, V> valueMap,
ImmutableMultimap<V, V> allowedTransitions,
String mapName,
V initialValue,
String initialValueErrorMessage) {
validateTimedTransitionMapInstant(valueMap, allowedTransitions, mapName);
checkArgument(valueMap.firstEntry().getValue().equals(initialValue), initialValueErrorMessage);
return fromValueMapInstant(valueMap);
}
/**
* Validates a timed transition map.
*
* @deprecated Use {@link #validateTimedTransitionMapInstant(ImmutableSortedMap,
* ImmutableMultimap, String)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static <V extends Serializable> void validateTimedTransitionMap(
ImmutableSortedMap<DateTime, V> valueMap,
ImmutableMultimap<V, V> allowedTransitions,
String mapName) {
validateTimedTransitionMapInstant(toInstantMap(valueMap), allowedTransitions, mapName);
}
/** Validates a timed transition map. */
public static <V extends Serializable> void validateTimedTransitionMapInstant(
ImmutableSortedMap<Instant, V> valueMap,
ImmutableMultimap<V, V> allowedTransitions,
String mapName) {
checkArgument(
Ordering.natural().equals(valueMap.comparator()),
"Timed transition value map must have transition time keys in chronological order");
checkArgument(!valueMap.isEmpty(), "%s map cannot be null or empty.", mapName);
checkArgument(
valueMap.firstKey().equals(START_INSTANT), "%s map must start at START_OF_TIME.", mapName);
V lastValue = null;
for (V value : valueMap.values()) {
if (lastValue != null && !allowedTransitions.containsEntry(lastValue, value)) {
if (allowedTransitions.get(lastValue).isEmpty()) {
throw new IllegalArgumentException(
String.format("%s map cannot transition from %s.", mapName, lastValue));
} else {
throw new IllegalArgumentException(
String.format("%s map cannot transition from %s to %s.", mapName, lastValue, value));
}
}
lastValue = value;
}
}
private static <V> ImmutableSortedMap<Instant, V> toInstantMap(
ImmutableSortedMap<DateTime, V> valueMap) {
checkArgument(
Ordering.natural().equals(valueMap.comparator()),
"Timed transition value map must have transition time keys in chronological order");
return valueMap.entrySet().stream()
.collect(
toImmutableSortedMap(
Ordering.natural(), e -> toInstant(e.getKey()), Map.Entry::getValue));
}
/** Checks whether the property is valid. */
public void checkValidity() {
checkState(
backingMap.get(START_OF_TIME) != null,
backingMap.containsKey(START_INSTANT),
"Timed transition values missing required entry for the start of time (Unix Epoch)");
}
/** Exposes the underlying {@link ImmutableSortedMap}. */
/** Returns the value of the property that is active at the given time. */
public V getValueAtTime(DateTime time) {
return getValueAtTime(toInstant(time));
}
/** Returns the value of the property that is active at the given time. */
public V getValueAtTime(Instant time) {
return backingMap.floorEntry(latestOf(START_INSTANT, time)).getValue();
}
/** Returns the map of all the transitions that have been defined for this property. */
public ImmutableSortedMap<DateTime, V> toValueMap() {
return backingMap.entrySet().stream()
.collect(
toImmutableSortedMap(
Ordering.natural(), e -> toDateTime(e.getKey()), Map.Entry::getValue));
}
/** Returns the map of all the transitions that have been defined for this property. */
public ImmutableSortedMap<Instant, V> toValueMapInstant() {
return backingMap;
}
/**
* Returns the value of the property that is active at the specified time. The active value for a
* time before {@code START_OF_TIME} is extrapolated to be the value that is active at {@code
* START_OF_TIME}.
* Returns the time of the next transition after the given time. Returns null if there is no
* subsequent transition.
*/
public V getValueAtTime(DateTime time) {
// Retrieve the current value by finding the latest transition before or at the given time,
// where any given time earlier than START_OF_TIME is replaced by START_OF_TIME.
return backingMap.floorEntry(latestOf(START_OF_TIME, time)).getValue();
@Nullable
public DateTime getNextTransitionAfter(DateTime time) {
Instant nextTransition = getNextTransitionAfter(toInstant(time));
return nextTransition == null ? null : toDateTime(nextTransition);
}
/** Returns the time of the next transition. Returns null if there is no subsequent transition. */
@Nullable
public DateTime getNextTransitionAfter(DateTime time) {
return backingMap.higherKey(latestOf(START_OF_TIME, time));
public Instant getNextTransitionAfter(Instant time) {
return backingMap.higherKey(latestOf(START_INSTANT, time));
}
public int size() {
@@ -188,6 +280,8 @@ public class TimedTransitionProperty<V extends Serializable> implements UnsafeSe
@Override
public String toString() {
return this.backingMap.toString();
return backingMap.entrySet().stream()
.map(e -> ISO_8601_FORMATTER.format(e.getKey()) + "=" + e.getValue())
.collect(Collectors.joining(", ", "{", "}"));
}
}

View File

@@ -14,6 +14,7 @@
package google.registry.model.console;
import static google.registry.util.DateTimeUtils.toDateTime;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import google.registry.model.Buildable;
@@ -27,6 +28,7 @@ import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import java.time.Instant;
import java.util.Optional;
import java.util.UUID;
import org.joda.time.DateTime;
@@ -57,7 +59,7 @@ public class PasswordResetRequest extends ImmutableObject implements Buildable {
name = "creationTime",
column = @Column(name = "requestTime", nullable = false))
})
CreateAutoTimestamp requestTime = CreateAutoTimestamp.create(null);
CreateAutoTimestamp requestTime = CreateAutoTimestamp.create((Instant) null);
@Column(nullable = false)
String requester;
@@ -78,10 +80,19 @@ public class PasswordResetRequest extends ImmutableObject implements Buildable {
return type;
}
public DateTime getRequestTime() {
public Instant getRequestTime() {
return requestTime.getTimestamp();
}
/**
* @deprecated Use {@link #getRequestTime()}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public DateTime getRequestDateTime() {
return toDateTime(requestTime.getTimestamp());
}
public String getRequester() {
return requester;
}

View File

@@ -28,10 +28,9 @@ import static google.registry.util.CollectionUtils.forceEmptyToNull;
import static google.registry.util.CollectionUtils.nullToEmpty;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
import static google.registry.util.CollectionUtils.union;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.END_INSTANT;
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;
@@ -39,7 +38,6 @@ 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;
@@ -84,7 +82,9 @@ import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.Transient;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
@@ -183,7 +183,7 @@ public class DomainBase extends EppResource {
@Expose Set<String> subordinateHosts;
/** When this domain's registration will expire. */
@Expose DateTime registrationExpirationTime;
@Expose Instant registrationExpirationTime;
/**
* The poll message associated with this domain being deleted.
@@ -238,12 +238,12 @@ public class DomainBase extends EppResource {
*
* <p>Can be null if the resource has never been transferred.
*/
@Expose DateTime lastTransferTime;
@Expose Instant lastTransferTime;
/**
* When the domain's autorenewal status will expire.
*
* <p>This will be {@link DateTimeUtils#END_OF_TIME} for the vast majority of domains because all
* <p>This will be {@link DateTimeUtils#END_INSTANT} for the vast majority of domains because all
* domains autorenew indefinitely by default and autorenew can only be countermanded by
* administrators, typically for reasons of the URS process or termination of a registrar for
* nonpayment.
@@ -255,7 +255,7 @@ public class DomainBase extends EppResource {
* difference domains that have reached their life and must be deleted now, and domains that
* happen to be in the autorenew grace period now but should be deleted in roughly a year.
*/
DateTime autorenewEndTime;
Instant autorenewEndTime;
/**
* Which Lordn phase the domain is in after it is created but before the Nordn upload has
@@ -290,13 +290,17 @@ public class DomainBase extends EppResource {
return nullToEmptyImmutableCopy(subordinateHosts);
}
/**
* @deprecated Use {@link #getRegistrationExpirationTime()}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public DateTime getRegistrationExpirationDateTime() {
return registrationExpirationTime;
return toDateTime(registrationExpirationTime);
}
public Instant getRegistrationExpirationTime() {
return toInstant(registrationExpirationTime);
return registrationExpirationTime;
}
public VKey<OneTime> getDeletePollMessage() {
@@ -326,19 +330,37 @@ public class DomainBase extends EppResource {
/**
* Returns the autorenew end time if there is one, otherwise empty.
*
* <p>Note that {@link DateTimeUtils#END_OF_TIME} is used as a sentinel value in the database
* <p>Note that {@link DateTimeUtils#END_INSTANT} is used as a sentinel value in the database
* representation to signify that autorenew doesn't end, and is mapped to empty here for the
* purposes of more legible business logic.
*
* @deprecated Use {@link #getAutorenewEndTimeInstant()}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public Optional<DateTime> getAutorenewEndTime() {
return Optional.ofNullable(autorenewEndTime.equals(END_OF_TIME) ? null : autorenewEndTime);
return getAutorenewEndTimeInstant().map(DateTimeUtils::toDateTime);
}
/** Returns the autorenew end time if there is one, otherwise empty. */
public Optional<Instant> getAutorenewEndTimeInstant() {
return Optional.ofNullable(autorenewEndTime.equals(END_INSTANT) ? null : autorenewEndTime);
}
public DomainTransferData getTransferData() {
return Optional.ofNullable(transferData).orElse(DomainTransferData.EMPTY);
}
/**
* @deprecated Use {@link #getLastTransferTimeInstant()}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public DateTime getLastTransferTime() {
return toDateTime(lastTransferTime);
}
public Instant getLastTransferTimeInstant() {
return lastTransferTime;
}
@@ -507,8 +529,9 @@ public class DomainBase extends EppResource {
GracePeriod.create(
GracePeriodStatus.TRANSFER,
domain.getRepoId(),
toDateTime(transferExpirationTime)
.plus(Tld.get(domain.getTld()).getTransferGracePeriodLength()),
transferExpirationTime.plus(
Duration.ofMillis(
Tld.get(domain.getTld()).getTransferGracePeriodLength().getMillis())),
transferData.getGainingRegistrarId(),
transferData.getServerApproveBillingEvent())));
} else {
@@ -530,12 +553,13 @@ public class DomainBase extends EppResource {
Builder builder = domain.asBuilder();
if (isBeforeOrAt(domain.getRegistrationExpirationTime(), now)
&& END_OF_TIME.equals(domain.getDeletionDateTime())) {
&& END_INSTANT.equals(domain.getDeletionTime())) {
// Autorenew by the number of years between the old expiration time and now.
Instant lastAutorenewTime =
leapSafeAddYears(
plusYears(
domain.getRegistrationExpirationTime(),
YEARS.between(domain.getRegistrationExpirationTime().atZone(UTC), now.atZone(UTC)));
ChronoUnit.YEARS.between(
domain.getRegistrationExpirationTime().atZone(UTC), now.atZone(UTC)));
Instant newExpirationTime = plusYears(lastAutorenewTime, 1);
builder
.setRegistrationExpirationTime(toDateTime(newExpirationTime))
@@ -543,8 +567,9 @@ public class DomainBase extends EppResource {
GracePeriod.createForRecurrence(
GracePeriodStatus.AUTO_RENEW,
domain.getRepoId(),
toDateTime(lastAutorenewTime)
.plus(Tld.get(domain.getTld()).getAutoRenewGracePeriodLength()),
lastAutorenewTime.plus(
Duration.ofMillis(
Tld.get(domain.getTld()).getAutoRenewGracePeriodLength().getMillis())),
domain.getCurrentSponsorRegistrarId(),
domain.getAutorenewBillingEvent()));
newLastEppUpdateTime = Optional.of(lastAutorenewTime);
@@ -587,9 +612,8 @@ public class DomainBase extends EppResource {
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(
currentExpirationTime, Optional.ofNullable(extendedRegistrationYears).orElse(0)),
leapSafeAddYears(now, MAX_REGISTRATION_YEARS));
plusYears(currentExpirationTime, Optional.ofNullable(extendedRegistrationYears).orElse(0)),
plusYears(now, MAX_REGISTRATION_YEARS));
}
/** Loads and returns the fully qualified host names of all linked nameservers. */
@@ -645,8 +669,8 @@ public class DomainBase extends EppResource {
} else { // There are nameservers, so make sure INACTIVE isn't there.
removeStatusValue(StatusValue.INACTIVE);
}
// If there is no autorenew end time, set it to END_OF_TIME.
instance.autorenewEndTime = firstNonNull(getInstance().autorenewEndTime, END_OF_TIME);
// If there is no autorenew end time, set it to END_INSTANT.
instance.autorenewEndTime = firstNonNull(getInstance().autorenewEndTime, END_INSTANT);
checkArgumentNotNull(emptyToNull(instance.domainName), "Missing domainName");
instance.tld = getTldFromDomainName(instance.domainName);
@@ -741,11 +765,20 @@ public class DomainBase extends EppResource {
CollectionUtils.difference(getInstance().getSubordinateHosts(), hostToRemove)));
}
public B setRegistrationExpirationTime(DateTime registrationExpirationTime) {
public B setRegistrationExpirationTime(Instant registrationExpirationTime) {
getInstance().registrationExpirationTime = registrationExpirationTime;
return thisCastToDerived();
}
/**
* @deprecated Use {@link #setRegistrationExpirationTime(Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public B setRegistrationExpirationTime(DateTime registrationExpirationTime) {
return setRegistrationExpirationTime(toInstant(registrationExpirationTime));
}
public B setDeletePollMessage(VKey<OneTime> deletePollMessage) {
getInstance().deletePollMessage = deletePollMessage;
return thisCastToDerived();
@@ -797,8 +830,17 @@ public class DomainBase extends EppResource {
* representation to signify that autorenew doesn't end, and is mapped to empty here for the
* purposes of more legible business logic.
*/
/**
* @deprecated Use {@link #setAutorenewEndTimeInstant(Optional)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public B setAutorenewEndTime(Optional<DateTime> autorenewEndTime) {
getInstance().autorenewEndTime = autorenewEndTime.orElse(END_OF_TIME);
return setAutorenewEndTimeInstant(autorenewEndTime.map(DateTimeUtils::toInstant));
}
public B setAutorenewEndTimeInstant(Optional<Instant> autorenewEndTime) {
getInstance().autorenewEndTime = autorenewEndTime.orElse(END_INSTANT);
return thisCastToDerived();
}
@@ -807,11 +849,20 @@ public class DomainBase extends EppResource {
return thisCastToDerived();
}
public B setLastTransferTime(DateTime lastTransferTime) {
public B setLastTransferTime(Instant lastTransferTime) {
getInstance().lastTransferTime = lastTransferTime;
return thisCastToDerived();
}
/**
* @deprecated Use {@link #setLastTransferTime(Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public B setLastTransferTime(DateTime lastTransferTime) {
return setLastTransferTime(toInstant(lastTransferTime));
}
public B setCurrentBulkToken(@Nullable VKey<AllocationToken> currentBulkToken) {
if (currentBulkToken == null) {
getInstance().currentBulkToken = currentBulkToken;

View File

@@ -14,10 +14,16 @@
package google.registry.model.domain;
import static google.registry.util.DateTimeUtils.toDateTime;
import static google.registry.util.DateTimeUtils.toInstant;
import google.registry.model.eppoutput.EppResponse.ResponseData;
import google.registry.xml.UtcInstantAdapter;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlType;
import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.time.Instant;
import org.joda.time.DateTime;
/** The {@link ResponseData} returned when renewing a domain. */
@@ -28,12 +34,36 @@ public class DomainRenewData implements ResponseData {
String name;
@XmlElement(name = "exDate")
DateTime expirationDate;
@XmlJavaTypeAdapter(UtcInstantAdapter.class)
Instant expirationDate;
public static DomainRenewData create(String name, DateTime expirationDate) {
public static DomainRenewData create(String name, Instant expirationDate) {
DomainRenewData instance = new DomainRenewData();
instance.name = name;
instance.expirationDate = expirationDate;
return instance;
}
/**
* @deprecated Use {@link #create(String, Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static DomainRenewData create(String name, DateTime expirationDate) {
return create(name, toInstant(expirationDate));
}
/** Returns the expiration date. */
public Instant getExpirationDate() {
return expirationDate;
}
/**
* @deprecated Use {@link #getExpirationDate()}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public DateTime getExpirationDateTime() {
return toDateTime(expirationDate);
}
}

View File

@@ -16,6 +16,7 @@ package google.registry.model.domain;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.DateTimeUtils.toInstant;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.google.common.annotations.VisibleForTesting;
@@ -30,6 +31,7 @@ import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import java.time.Instant;
import javax.annotation.Nullable;
import org.joda.time.DateTime;
@@ -58,7 +60,7 @@ public class GracePeriod extends GracePeriodBase {
private static GracePeriod createInternal(
GracePeriodStatus type,
String domainRepoId,
DateTime expirationTime,
Instant expirationTime,
String registrarId,
@Nullable VKey<BillingEvent> billingEvent,
@Nullable VKey<BillingRecurrence> billingRecurrence,
@@ -91,13 +93,29 @@ public class GracePeriod extends GracePeriodBase {
public static GracePeriod create(
GracePeriodStatus type,
String domainRepoId,
DateTime expirationTime,
Instant expirationTime,
String registrarId,
@Nullable VKey<BillingEvent> billingEventOneTime) {
return createInternal(
type, domainRepoId, expirationTime, registrarId, billingEventOneTime, null, null);
}
/**
* Creates a GracePeriod for an (optional) OneTime billing event.
*
* @deprecated Use {@link #create(GracePeriodStatus, String, Instant, String, VKey)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static GracePeriod create(
GracePeriodStatus type,
String domainRepoId,
DateTime expirationTime,
String registrarId,
@Nullable VKey<BillingEvent> billingEventOneTime) {
return create(type, domainRepoId, toInstant(expirationTime), registrarId, billingEventOneTime);
}
/**
* Creates a GracePeriod for an (optional) OneTime billing event and a given {@link
* #gracePeriodId}.
@@ -110,7 +128,7 @@ public class GracePeriod extends GracePeriodBase {
public static GracePeriod create(
GracePeriodStatus type,
String domainRepoId,
DateTime expirationTime,
Instant expirationTime,
String registrarId,
@Nullable VKey<BillingEvent> billingEventOneTime,
@Nullable Long gracePeriodId) {
@@ -118,6 +136,31 @@ public class GracePeriod extends GracePeriodBase {
type, domainRepoId, expirationTime, registrarId, billingEventOneTime, null, gracePeriodId);
}
/**
* Creates a GracePeriod for an (optional) OneTime billing event and a given {@link
* #gracePeriodId}.
*
* @deprecated Use {@link #create(GracePeriodStatus, String, Instant, String, VKey, Long)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
@VisibleForTesting
public static GracePeriod create(
GracePeriodStatus type,
String domainRepoId,
DateTime expirationTime,
String registrarId,
@Nullable VKey<BillingEvent> billingEventOneTime,
@Nullable Long gracePeriodId) {
return create(
type,
domainRepoId,
toInstant(expirationTime),
registrarId,
billingEventOneTime,
gracePeriodId);
}
public static GracePeriod createFromHistory(GracePeriodHistory history) {
return createInternal(
history.type,
@@ -133,7 +176,7 @@ public class GracePeriod extends GracePeriodBase {
public static GracePeriod createForRecurrence(
GracePeriodStatus type,
String domainRepoId,
DateTime expirationTime,
Instant expirationTime,
String registrarId,
VKey<BillingRecurrence> billingEventRecurrence) {
checkArgumentNotNull(billingEventRecurrence, "billingEventRecurrence cannot be null");
@@ -141,12 +184,29 @@ public class GracePeriod extends GracePeriodBase {
type, domainRepoId, expirationTime, registrarId, null, billingEventRecurrence, null);
}
/**
* Creates a GracePeriod for a Recurrence billing event.
*
* @deprecated Use {@link #createForRecurrence(GracePeriodStatus, String, Instant, String, VKey)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static GracePeriod createForRecurrence(
GracePeriodStatus type,
String domainRepoId,
DateTime expirationTime,
String registrarId,
VKey<BillingRecurrence> billingEventRecurrence) {
return createForRecurrence(
type, domainRepoId, toInstant(expirationTime), registrarId, billingEventRecurrence);
}
/** Creates a GracePeriod for a Recurrence billing event and a given {@link #gracePeriodId}. */
@VisibleForTesting
public static GracePeriod createForRecurrence(
GracePeriodStatus type,
String domainRepoId,
DateTime expirationTime,
Instant expirationTime,
String registrarId,
VKey<BillingRecurrence> billingEventRecurrence,
@Nullable Long gracePeriodId) {
@@ -161,19 +221,56 @@ public class GracePeriod extends GracePeriodBase {
gracePeriodId);
}
/**
* Creates a GracePeriod for a Recurrence billing event and a given {@link #gracePeriodId}.
*
* @deprecated Use {@link #createForRecurrence(GracePeriodStatus, String, Instant, String, VKey,
* Long)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
@VisibleForTesting
public static GracePeriod createForRecurrence(
GracePeriodStatus type,
String domainRepoId,
DateTime expirationTime,
String registrarId,
VKey<BillingRecurrence> billingEventRecurrence,
@Nullable Long gracePeriodId) {
return createForRecurrence(
type,
domainRepoId,
toInstant(expirationTime),
registrarId,
billingEventRecurrence,
gracePeriodId);
}
/** Creates a GracePeriod with no billing event. */
public static GracePeriod createWithoutBillingEvent(
GracePeriodStatus type, String domainRepoId, DateTime expirationTime, String registrarId) {
GracePeriodStatus type, String domainRepoId, Instant expirationTime, String registrarId) {
return createInternal(type, domainRepoId, expirationTime, registrarId, null, null, null);
}
/**
* Creates a GracePeriod with no billing event.
*
* @deprecated Use {@link #createWithoutBillingEvent(GracePeriodStatus, String, Instant, String)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static GracePeriod createWithoutBillingEvent(
GracePeriodStatus type, String domainRepoId, DateTime expirationTime, String registrarId) {
return createWithoutBillingEvent(type, domainRepoId, toInstant(expirationTime), registrarId);
}
/** Constructs a GracePeriod of the given type from the provided one-time BillingEvent. */
public static GracePeriod forBillingEvent(
GracePeriodStatus type, String domainRepoId, BillingEvent billingEvent) {
return create(
type,
domainRepoId,
billingEvent.getBillingTime(),
billingEvent.getBillingTimeInstant(),
billingEvent.getRegistrarId(),
billingEvent.createVKey());
}
@@ -201,6 +298,7 @@ public class GracePeriod extends GracePeriodBase {
return new HistoryEntryId(getDomainRepoId(), domainHistoryRevisionId);
}
@SuppressWarnings("InlineMeSuggester")
static GracePeriodHistory createFrom(long historyRevisionId, GracePeriod gracePeriod) {
GracePeriodHistory instance = new GracePeriodHistory();
instance.gracePeriodHistoryRevisionId = tm().reTransact(tm()::allocateId);
@@ -208,7 +306,7 @@ public class GracePeriod extends GracePeriodBase {
instance.gracePeriodId = gracePeriod.gracePeriodId;
instance.type = gracePeriod.type;
instance.domainRepoId = gracePeriod.domainRepoId;
instance.expirationTime = gracePeriod.expirationTime;
instance.expirationTime = gracePeriod.getExpirationTime();
instance.clientId = gracePeriod.clientId;
instance.billingEvent = gracePeriod.billingEvent;
instance.billingRecurrence = gracePeriod.billingRecurrence;

View File

@@ -14,7 +14,7 @@
package google.registry.model.domain;
import static google.registry.util.DateTimeUtils.toInstant;
import static google.registry.util.DateTimeUtils.toDateTime;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
@@ -54,7 +54,7 @@ public class GracePeriodBase extends ImmutableObject implements UnsafeSerializab
/** When the grace period ends. */
@Column(nullable = false)
DateTime expirationTime;
Instant expirationTime;
/** The registrar to bill. */
@Column(name = "registrarId", nullable = false)
@@ -94,12 +94,13 @@ public class GracePeriodBase extends ImmutableObject implements UnsafeSerializab
}
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public DateTime getExpirationDateTime() {
return expirationTime;
return toDateTime(expirationTime);
}
public Instant getExpirationTime() {
return toInstant(expirationTime);
return expirationTime;
}
public String getRegistrarId() {

View File

@@ -16,6 +16,8 @@ package google.registry.model.domain;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
import static google.registry.util.DateTimeUtils.toDateTime;
import static google.registry.util.DateTimeUtils.toInstant;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.google.gson.annotations.Expose;
@@ -36,6 +38,7 @@ import jakarta.persistence.Index;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import java.time.Instant;
import java.util.Optional;
import javax.annotation.Nullable;
import org.joda.time.DateTime;
@@ -118,7 +121,7 @@ public final class RegistryLock extends UpdateAutoTimestampEntity implements Bui
column = @Column(name = "lockRequestTime", nullable = false))
})
@Expose
private final CreateAutoTimestamp lockRequestTime = CreateAutoTimestamp.create(null);
private final CreateAutoTimestamp lockRequestTime = CreateAutoTimestamp.create((Instant) null);
/** When the unlock is first requested. */
@Expose private DateTime unlockRequestTime;
@@ -173,10 +176,19 @@ public final class RegistryLock extends UpdateAutoTimestampEntity implements Bui
return registryLockEmail;
}
public DateTime getLockRequestTime() {
public Instant getLockRequestTime() {
return lockRequestTime.getTimestamp();
}
/**
* @deprecated Use {@link #getLockRequestTime()}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public DateTime getLockRequestDateTime() {
return toDateTime(lockRequestTime.getTimestamp());
}
/** Returns the unlock request timestamp or null if an unlock has not been requested yet. */
public Optional<DateTime> getUnlockRequestTime() {
return Optional.ofNullable(unlockRequestTime);
@@ -203,7 +215,7 @@ public final class RegistryLock extends UpdateAutoTimestampEntity implements Bui
}
public DateTime getLastUpdateTime() {
return getUpdateTimestamp().getTimestamp();
return getUpdateTimestamp().getTimestampDateTime();
}
public Long getRevisionId() {
@@ -229,18 +241,41 @@ public final class RegistryLock extends UpdateAutoTimestampEntity implements Bui
return lockCompletionTime != null && unlockCompletionTime == null;
}
/** Returns true iff the lock was requested &gt;= 1 hour ago and has not been verified. */
/**
* Returns true iff the lock was requested &gt;= 1 hour ago and has not been verified.
*
* @deprecated Use {@link #isLockRequestExpired(Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public boolean isLockRequestExpired(DateTime now) {
return isLockRequestExpired(toInstant(now));
}
/** Returns true iff the lock was requested &gt;= 1 hour ago and has not been verified. */
public boolean isLockRequestExpired(Instant now) {
return getLockCompletionTime().isEmpty()
&& isBeforeOrAt(getLockRequestTime(), now.minusHours(1));
&& isBeforeOrAt(getLockRequestTime(), now.minus(java.time.Duration.ofHours(1)));
}
/**
* Returns true iff the unlock was requested &gt;= 1 hour ago and has not been verified.
*
* @deprecated Use {@link #isUnlockRequestExpired(Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public boolean isUnlockRequestExpired(DateTime now) {
return isUnlockRequestExpired(toInstant(now));
}
/** Returns true iff the unlock was requested &gt;= 1 hour ago and has not been verified. */
public boolean isUnlockRequestExpired(DateTime now) {
public boolean isUnlockRequestExpired(Instant now) {
Optional<DateTime> unlockRequestTimestamp = getUnlockRequestTime();
return unlockRequestTimestamp.isPresent()
&& getUnlockCompletionTime().isEmpty()
&& isBeforeOrAt(unlockRequestTimestamp.get(), now.minusHours(1));
&& isBeforeOrAt(
toInstant(unlockRequestTimestamp.get()), now.minus(java.time.Duration.ofHours(1)));
}
@Override

View File

@@ -18,6 +18,7 @@ import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.common.base.Ascii;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Range;
@@ -29,6 +30,7 @@ import jakarta.xml.bind.annotation.XmlTransient;
import jakarta.xml.bind.annotation.XmlValue;
import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.math.BigDecimal;
import java.time.Instant;
import java.util.stream.Stream;
import org.joda.time.DateTime;
import org.joda.time.Period;
@@ -104,6 +106,8 @@ public abstract class BaseFee extends ImmutableObject {
@XmlTransient Range<DateTime> validDateRange;
@XmlTransient Range<Instant> validDateRangeInstant;
@XmlTransient boolean isPremium;
public String getDescription() {
@@ -143,13 +147,44 @@ public abstract class BaseFee extends ImmutableObject {
return type;
}
@JsonIgnore
public boolean hasValidDateRange() {
return validDateRange != null;
return validDateRange != null || validDateRangeInstant != null;
}
@JsonIgnore
public Range<DateTime> getValidDateRange() {
checkState(hasValidDateRange());
return validDateRange;
if (validDateRange != null) {
return validDateRange;
}
return convertRange(validDateRangeInstant, google.registry.util.DateTimeUtils::toDateTime);
}
@JsonIgnore
public Range<Instant> getValidDateRangeInstant() {
checkState(hasValidDateRange());
if (validDateRangeInstant != null) {
return validDateRangeInstant;
}
return convertRange(validDateRange, google.registry.util.DateTimeUtils::toInstant);
}
private static <S extends Comparable<? super S>, T extends Comparable<? super T>>
Range<T> convertRange(Range<S> range, java.util.function.Function<S, T> converter) {
if (range.hasLowerBound() && range.hasUpperBound()) {
return Range.range(
converter.apply(range.lowerEndpoint()),
range.lowerBoundType(),
converter.apply(range.upperEndpoint()),
range.upperBoundType());
} else if (range.hasLowerBound()) {
return Range.downTo(converter.apply(range.lowerEndpoint()), range.lowerBoundType());
} else if (range.hasUpperBound()) {
return Range.upTo(converter.apply(range.upperEndpoint()), range.upperBoundType());
} else {
return Range.all();
}
}
public boolean hasZeroCost() {

View File

@@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Range;
import google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension;
import java.math.BigDecimal;
import java.time.Instant;
import org.joda.time.DateTime;
/**
@@ -50,6 +51,18 @@ public class Fee extends BaseFee {
return instance;
}
/** Creates a Fee for the given cost, type, and valid date range with the default description. */
public static Fee createInstant(
BigDecimal cost,
FeeType type,
boolean isPremium,
Range<Instant> validDateRange,
Object... descriptionArgs) {
Fee instance = create(cost, type, isPremium, descriptionArgs);
instance.validDateRangeInstant = validDateRange;
return instance;
}
/** Creates a Fee for the given cost and type with a custom description. */
private static Fee createWithCustomDescription(
BigDecimal cost, FeeType type, boolean isPremium, String description) {

View File

@@ -50,8 +50,8 @@ public class FeeCheckCommandExtensionV06 extends ImmutableObject
ImmutableList<? extends FeeCheckResponseExtensionItem> items) {
ImmutableList.Builder<FeeCheckResponseExtensionItemV06> builder = new ImmutableList.Builder<>();
for (FeeCheckResponseExtensionItem item : items) {
if (item instanceof FeeCheckResponseExtensionItemV06) {
builder.add((FeeCheckResponseExtensionItemV06) item);
if (item instanceof FeeCheckResponseExtensionItemV06 feeCheckResponseExtensionItemV06) {
builder.add(feeCheckResponseExtensionItemV06);
}
}
return FeeCheckResponseExtensionV06.create(builder.build());

View File

@@ -54,8 +54,8 @@ public class FeeCheckCommandExtensionV12 extends ImmutableObject
ImmutableList<? extends FeeCheckResponseExtensionItem> items) {
ImmutableList.Builder<FeeCheckResponseExtensionItemV12> builder = new ImmutableList.Builder<>();
for (FeeCheckResponseExtensionItem item : items) {
if (item instanceof FeeCheckResponseExtensionItemV12) {
builder.add((FeeCheckResponseExtensionItemV12) item);
if (item instanceof FeeCheckResponseExtensionItemV12 feeCheckResponseExtensionItemV12) {
builder.add(feeCheckResponseExtensionItemV12);
}
}
return FeeCheckResponseExtensionV12.create(currency, builder.build());

View File

@@ -17,8 +17,9 @@ package google.registry.model.domain.launch;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.hash.Hashing.crc32;
import static com.google.common.io.BaseEncoding.base16;
import static google.registry.util.DateTimeUtils.toDateTime;
import static google.registry.util.DateTimeUtils.toInstant;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import com.google.common.base.Ascii;
import com.google.common.base.CharMatcher;
@@ -31,6 +32,7 @@ import jakarta.xml.bind.annotation.XmlAttribute;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlType;
import jakarta.xml.bind.annotation.XmlValue;
import java.time.Instant;
import java.util.Optional;
import org.joda.time.DateTime;
@@ -71,20 +73,38 @@ public class LaunchNotice extends ImmutableObject implements UnsafeSerializable
NoticeIdType noticeId;
@XmlElement(name = "notAfter")
DateTime expirationTime;
Instant expirationTime;
@XmlElement(name = "acceptedDate")
DateTime acceptedTime;
Instant acceptedTime;
public NoticeIdType getNoticeId() {
return Optional.ofNullable(noticeId).orElse(EMPTY_NOTICE_ID);
}
/**
* @deprecated Use {@link #getExpirationTimeInstant()}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public DateTime getExpirationTime() {
return toDateTime(expirationTime);
}
public Instant getExpirationTimeInstant() {
return expirationTime;
}
/**
* @deprecated Use {@link #getAcceptedTimeInstant()}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public DateTime getAcceptedTime() {
return toDateTime(acceptedTime);
}
public Instant getAcceptedTimeInstant() {
return acceptedTime;
}
@@ -103,8 +123,7 @@ public class LaunchNotice extends ImmutableObject implements UnsafeSerializable
checkArgument(CharMatcher.inRange('0', '9').matchesAllOf(noticeId));
// The checksum in the first 8 chars must match the crc32 of label + expiration + notice id.
String stringToHash =
domainLabel + MILLISECONDS.toSeconds(getExpirationTime().getMillis()) + noticeId;
String stringToHash = domainLabel + getExpirationTimeInstant().getEpochSecond() + noticeId;
int computedChecksum = crc32().hashString(stringToHash, UTF_8).asInt();
if (checksum != computedChecksum) {
throw new InvalidChecksumException();
@@ -115,7 +134,7 @@ public class LaunchNotice extends ImmutableObject implements UnsafeSerializable
public static class InvalidChecksumException extends Exception {}
public static LaunchNotice create(
String tcnId, String validatorId, DateTime expirationTime, DateTime acceptedTime) {
String tcnId, String validatorId, Instant expirationTime, Instant acceptedTime) {
LaunchNotice instance = new LaunchNotice();
instance.noticeId = new NoticeIdType();
instance.noticeId.tcnId = tcnId;
@@ -124,4 +143,14 @@ public class LaunchNotice extends ImmutableObject implements UnsafeSerializable
instance.acceptedTime = acceptedTime;
return instance;
}
/**
* @deprecated Use {@link #create(String, String, Instant, Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public static LaunchNotice create(
String tcnId, String validatorId, DateTime expirationTime, DateTime acceptedTime) {
return create(tcnId, validatorId, toInstant(expirationTime), toInstant(acceptedTime));
}
}

View File

@@ -89,9 +89,9 @@ public class LaunchPhase extends ImmutableObject {
/** A special equals implementation that only considers the string value. */
@Override
public boolean equals(Object other) {
return other instanceof LaunchPhase
&& Objects.equals(phase, ((LaunchPhase) other).phase)
&& Objects.equals(subphase, ((LaunchPhase) other).subphase);
return other instanceof LaunchPhase launchPhase
&& Objects.equals(phase, launchPhase.phase)
&& Objects.equals(subphase, launchPhase.subphase);
}
/** A special hashCode implementation that only considers the string value. */

View File

@@ -17,13 +17,18 @@
xmlns = @XmlNs(prefix = "launch", namespaceURI = "urn:ietf:params:xml:ns:launch-1.0"),
elementFormDefault = XmlNsForm.QUALIFIED)
@XmlAccessorType(XmlAccessType.FIELD)
@XmlJavaTypeAdapter(UtcDateTimeAdapter.class)
@XmlJavaTypeAdapters({
@XmlJavaTypeAdapter(UtcDateTimeAdapter.class),
@XmlJavaTypeAdapter(UtcInstantAdapter.class)
})
package google.registry.model.domain.launch;
import google.registry.xml.UtcDateTimeAdapter;
import google.registry.xml.UtcInstantAdapter;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlNs;
import jakarta.xml.bind.annotation.XmlNsForm;
import jakarta.xml.bind.annotation.XmlSchema;
import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapters;

View File

@@ -18,12 +18,15 @@
elementFormDefault = XmlNsForm.QUALIFIED)
@XmlAccessorType(XmlAccessType.FIELD)
@XmlJavaTypeAdapters({
@XmlJavaTypeAdapter(UtcDateTimeAdapter.class),
@XmlJavaTypeAdapter(DateAdapter.class)})
@XmlJavaTypeAdapter(UtcDateTimeAdapter.class),
@XmlJavaTypeAdapter(UtcInstantAdapter.class),
@XmlJavaTypeAdapter(DateAdapter.class)
})
package google.registry.model.domain;
import google.registry.xml.DateAdapter;
import google.registry.xml.UtcDateTimeAdapter;
import google.registry.xml.UtcInstantAdapter;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlNs;

View File

@@ -26,8 +26,11 @@ import static google.registry.model.domain.token.AllocationToken.TokenType.REGIS
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.CollectionUtils.forceEmptyToNull;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
import static google.registry.util.DateTimeUtils.toDateTime;
import static google.registry.util.DateTimeUtils.toInstant;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.annotations.VisibleForTesting;
@@ -37,6 +40,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Ordering;
import com.google.common.collect.Range;
import google.registry.flows.EppException;
import google.registry.flows.domain.DomainFlowUtils;
@@ -60,6 +64,7 @@ import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import java.time.Instant;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -208,7 +213,7 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
@Nullable String domainName;
/** When this token was created. */
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create((Instant) null);
/** Allowed registrar client IDs for this token, or null if all registrars are allowed. */
@Column(name = "allowedRegistrarIds")
@@ -300,7 +305,7 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
return Optional.ofNullable(domainName);
}
public Optional<DateTime> getCreationTime() {
public Optional<Instant> getCreationTime() {
return Optional.ofNullable(creationTime.getTimestamp());
}
@@ -334,10 +339,30 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
return tokenType;
}
/**
* @deprecated Use {@link #getCreationTime()}
*/
@JsonIgnore
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public Optional<DateTime> getCreationDateTime() {
return Optional.ofNullable(toDateTime(creationTime.getTimestamp()));
}
@JsonIgnore
public TimedTransitionProperty<TokenStatus> getTokenStatusTransitions() {
return tokenStatusTransitions;
}
public ImmutableSortedMap<DateTime, TokenStatus> getTokenStatusTransitionsMap() {
return tokenStatusTransitions.toValueMap();
}
@JsonIgnore
public ImmutableSortedMap<Instant, TokenStatus> getTokenStatusTransitionsMapInstant() {
return tokenStatusTransitions.toValueMapInstant();
}
public ImmutableSet<CommandName> getAllowedEppActions() {
return nullToEmptyImmutableCopy(allowedEppActions);
}
@@ -514,6 +539,11 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
@VisibleForTesting
public Builder setCreationTimeForTest(DateTime creationTime) {
return setCreationTimeForTest(toInstant(creationTime));
}
@VisibleForTesting
public Builder setCreationTimeForTest(Instant creationTime) {
checkState(
getInstance().creationTime.getTimestamp() == null, "Creation time can only be set once");
getInstance().creationTime = CreateAutoTimestamp.create(creationTime);
@@ -559,8 +589,17 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
public Builder setTokenStatusTransitions(
ImmutableSortedMap<DateTime, TokenStatus> transitions) {
return setTokenStatusTransitionsInstant(
transitions.entrySet().stream()
.collect(
ImmutableSortedMap.toImmutableSortedMap(
Ordering.natural(), e -> toInstant(e.getKey()), Map.Entry::getValue)));
}
public Builder setTokenStatusTransitionsInstant(
ImmutableSortedMap<Instant, TokenStatus> transitions) {
getInstance().tokenStatusTransitions =
TimedTransitionProperty.make(
TimedTransitionProperty.makeInstant(
transitions,
VALID_TOKEN_STATUS_TRANSITIONS,
"tokenStatusTransitions",

View File

@@ -16,7 +16,9 @@ package google.registry.model.domain.token;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.END_INSTANT;
import static google.registry.util.DateTimeUtils.toDateTime;
import static google.registry.util.DateTimeUtils.toInstant;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import google.registry.model.Buildable;
@@ -31,6 +33,7 @@ import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.time.Instant;
import java.util.Optional;
import javax.annotation.Nullable;
import org.joda.money.Money;
@@ -75,13 +78,13 @@ public class BulkPricingPackage extends ImmutableObject implements Buildable {
/** The next billing date of the bulk pricing package. */
@Column(nullable = false)
DateTime nextBillingDate = END_OF_TIME;
Instant nextBillingDate = END_INSTANT;
/**
* Date the last warning email was sent that the bulk pricing package has exceeded the maxDomains
* limit.
*/
@Nullable DateTime lastNotificationSent;
@Nullable Instant lastNotificationSent;
public long getId() {
return bulkPricingId;
@@ -103,11 +106,29 @@ public class BulkPricingPackage extends ImmutableObject implements Buildable {
return bulkPrice;
}
/**
* @deprecated Use {@link #getNextBillingDateInstant()}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public DateTime getNextBillingDate() {
return toDateTime(nextBillingDate);
}
public Instant getNextBillingDateInstant() {
return nextBillingDate;
}
/**
* @deprecated Use {@link #getLastNotificationSentInstant()}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public Optional<DateTime> getLastNotificationSent() {
return Optional.ofNullable(toDateTime(lastNotificationSent));
}
public Optional<Instant> getLastNotificationSentInstant() {
return Optional.ofNullable(lastNotificationSent);
}
@@ -175,13 +196,31 @@ public class BulkPricingPackage extends ImmutableObject implements Buildable {
return this;
}
/**
* @deprecated Use {@link #setNextBillingDate(Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public Builder setNextBillingDate(DateTime nextBillingDate) {
return setNextBillingDate(toInstant(nextBillingDate));
}
public Builder setNextBillingDate(Instant nextBillingDate) {
checkArgumentNotNull(nextBillingDate, "Next billing date must not be null");
getInstance().nextBillingDate = nextBillingDate;
return this;
}
/**
* @deprecated Use {@link #setLastNotificationSent(Instant)}
*/
@Deprecated
@SuppressWarnings("InlineMeSuggester")
public Builder setLastNotificationSent(@Nullable DateTime lastNotificationSent) {
return setLastNotificationSent(toInstant(lastNotificationSent));
}
public Builder setLastNotificationSent(@Nullable Instant lastNotificationSent) {
getInstance().lastNotificationSent = lastNotificationSent;
return this;
}

Some files were not shown because too many files have changed in this diff Show More