diff --git a/core/src/main/java/google/registry/model/domain/DomainBase.java b/core/src/main/java/google/registry/model/domain/DomainBase.java
index 1e69c0a01..0565f3161 100644
--- a/core/src/main/java/google/registry/model/domain/DomainBase.java
+++ b/core/src/main/java/google/registry/model/domain/DomainBase.java
@@ -16,7 +16,6 @@ package google.registry.model.domain;
import com.googlecode.objectify.Key;
-import com.googlecode.objectify.annotation.Entity;
import google.registry.model.EppResource;
import google.registry.model.EppResource.ForeignKeyedEppResource;
import google.registry.model.annotations.ExternalMessagingName;
@@ -31,10 +30,13 @@ import javax.persistence.AccessType;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
import javax.persistence.FetchType;
+import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.OneToMany;
+import javax.persistence.Table;
import org.joda.time.DateTime;
/**
@@ -47,16 +49,17 @@ import org.joda.time.DateTime;
* @see RFC 5731
*/
@ReportedOn
-@Entity
-@javax.persistence.Entity(name = "Domain")
-@javax.persistence.Table(
+@com.googlecode.objectify.annotation.Entity
+@Entity(name = "Domain")
+@Table(
name = "Domain",
indexes = {
- @javax.persistence.Index(columnList = "creationTime"),
- @javax.persistence.Index(columnList = "currentSponsorRegistrarId"),
- @javax.persistence.Index(columnList = "deletionTime"),
- @javax.persistence.Index(columnList = "domainName"),
- @javax.persistence.Index(columnList = "tld")
+ @Index(columnList = "creationTime"),
+ @Index(columnList = "currentSponsorRegistrarId"),
+ @Index(columnList = "deletionTime"),
+ @Index(columnList = "domainName"),
+ @Index(columnList = "tld"),
+ @Index(columnList = "autorenewEndTime")
})
@WithStringVKey
@ExternalMessagingName("domain")
diff --git a/core/src/main/java/google/registry/model/domain/DomainContent.java b/core/src/main/java/google/registry/model/domain/DomainContent.java
index ac8ba30a5..e48ebf459 100644
--- a/core/src/main/java/google/registry/model/domain/DomainContent.java
+++ b/core/src/main/java/google/registry/model/domain/DomainContent.java
@@ -252,11 +252,27 @@ public class DomainContent extends EppResource
*/
DateTime lastTransferTime;
+ /**
+ * When the domain's autorenewal status will expire.
+ *
+ * This will be null 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.
+ *
+ *
When a domain is scheduled to not autorenew, this field is set to the current value of its
+ * {@link #registrationExpirationTime}, after which point the next invocation of a periodic
+ * cronjob will explicitly delete the domain. This field is a DateTime and not a boolean because
+ * of edge cases that occur during the autorenew grace period. We need to be able to tell the
+ * 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.
+ */
+ @Nullable @Index DateTime autorenewEndTime;
+
@OnLoad
void load() {
// Reconstitute all of the contacts so that they have VKeys.
allContacts =
- allContacts.stream().map(contact -> contact.reconstitute()).collect(toImmutableSet());
+ allContacts.stream().map(DesignatedContact::reconstitute).collect(toImmutableSet());
setContactFields(allContacts, true);
// We have to return the cloned object here because the original object's
@@ -275,8 +291,7 @@ public class DomainContent extends EppResource
@PostLoad
void postLoad() {
// Reconstitute the contact list.
- ImmutableSet.Builder contactsBuilder =
- new ImmutableSet.Builder();
+ ImmutableSet.Builder contactsBuilder = new ImmutableSet.Builder<>();
if (registrantContact != null) {
contactsBuilder.add(
@@ -323,6 +338,10 @@ public class DomainContent extends EppResource
return smdId;
}
+ public Optional getAutorenewEndTime() {
+ return Optional.ofNullable(autorenewEndTime);
+ }
+
@Override
public DomainTransferData getTransferData() {
return Optional.ofNullable(transferData).orElse(DomainTransferData.EMPTY);
@@ -835,6 +854,11 @@ public class DomainContent extends EppResource
return thisCastToDerived();
}
+ public B setAutorenewEndTime(Optional autorenewEndTime) {
+ getInstance().autorenewEndTime = autorenewEndTime.orElse(null);
+ return thisCastToDerived();
+ }
+
@Override
public B setTransferData(DomainTransferData transferData) {
getInstance().transferData = transferData;
diff --git a/core/src/main/java/google/registry/tools/GetSchemaCommand.java b/core/src/main/java/google/registry/tools/GetSchemaCommand.java
index 54b5c9c29..5e1c628fc 100644
--- a/core/src/main/java/google/registry/tools/GetSchemaCommand.java
+++ b/core/src/main/java/google/registry/tools/GetSchemaCommand.java
@@ -14,14 +14,33 @@
package google.registry.tools;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import google.registry.model.SchemaVersion;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
/** Generates the schema file used for model versioning. */
@Parameters(commandDescription = "Generate a model schema file")
final class GetSchemaCommand implements Command {
+
+ @Parameter(
+ names = {"-o", "--out_file"},
+ description = "Name of the output file.")
+ String outFile;
+
@Override
- public void run() {
- System.out.println(SchemaVersion.getSchema());
+ public void run() throws IOException {
+ String schema = SchemaVersion.getSchema();
+ if (outFile == null) {
+ System.out.println(schema);
+ } else {
+ File file = new File(outFile);
+ file.createNewFile(); // Create the output file if it doesn't already exist.
+ Files.write(file.toPath(), schema.getBytes(UTF_8));
+ }
}
}
diff --git a/core/src/test/java/google/registry/model/domain/DomainBaseTest.java b/core/src/test/java/google/registry/model/domain/DomainBaseTest.java
index 864e54d13..1e069c9cf 100644
--- a/core/src/test/java/google/registry/model/domain/DomainBaseTest.java
+++ b/core/src/test/java/google/registry/model/domain/DomainBaseTest.java
@@ -55,6 +55,7 @@ import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.DomainTransferData;
import google.registry.model.transfer.TransferStatus;
import google.registry.persistence.VKey;
+import java.util.Optional;
import org.joda.money.Money;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
@@ -66,12 +67,11 @@ public class DomainBaseTest extends EntityTestCase {
private DomainBase domain;
private VKey oneTimeBillKey;
private VKey recurringBillKey;
- private VKey domainKey;
@BeforeEach
void setUp() {
createTld("com");
- domainKey = VKey.from(Key.create(null, DomainBase.class, "4-COM"));
+ VKey domainKey = VKey.from(Key.create(null, DomainBase.class, "4-COM"));
VKey hostKey =
persistResource(
new HostResource.Builder()
@@ -158,6 +158,7 @@ public class DomainBaseTest extends EntityTestCase {
fakeClock.nowUtc().plusDays(1),
"registrar",
null))
+ .setAutorenewEndTime(Optional.of(fakeClock.nowUtc().plusYears(2)))
.build()));
}
@@ -176,7 +177,8 @@ public class DomainBaseTest extends EntityTestCase {
"nsHosts",
"currentSponsorClientId",
"deletionTime",
- "tld");
+ "tld",
+ "autorenewEndTime");
}
@Test
diff --git a/core/src/test/resources/google/registry/model/schema.txt b/core/src/test/resources/google/registry/model/schema.txt
index 304d36eed..1b3667043 100644
--- a/core/src/test/resources/google/registry/model/schema.txt
+++ b/core/src/test/resources/google/registry/model/schema.txt
@@ -185,6 +185,7 @@ class google.registry.model.domain.DomainBase {
java.util.Set status;
java.util.Set> nsHosts;
java.util.Set subordinateHosts;
+ org.joda.time.DateTime autorenewEndTime;
org.joda.time.DateTime deletionTime;
org.joda.time.DateTime lastEppUpdateTime;
org.joda.time.DateTime lastTransferTime;
@@ -765,4 +766,4 @@ enum google.registry.model.transfer.TransferStatus {
PENDING;
SERVER_APPROVED;
SERVER_CANCELLED;
-}
+}
\ No newline at end of file
diff --git a/db/src/main/resources/sql/flyway/V48__domain_add_autorenew_end_time_column.sql b/db/src/main/resources/sql/flyway/V48__domain_add_autorenew_end_time_column.sql
new file mode 100644
index 000000000..50ee6d2cd
--- /dev/null
+++ b/db/src/main/resources/sql/flyway/V48__domain_add_autorenew_end_time_column.sql
@@ -0,0 +1,17 @@
+-- Copyright 2020 The Nomulus Authors. All Rights Reserved.
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+ALTER TABLE "Domain" ADD COLUMN autorenew_end_time timestamptz;
+ALTER TABLE "DomainHistory" ADD COLUMN autorenew_end_time timestamptz;
+CREATE INDEX IDXlrq7v63pc21uoh3auq6eybyhl ON "Domain" (autorenew_end_time);
diff --git a/db/src/main/resources/sql/schema/db-schema.sql.generated b/db/src/main/resources/sql/schema/db-schema.sql.generated
index 4c6ff4c6a..4c22b0cb4 100644
--- a/db/src/main/resources/sql/schema/db-schema.sql.generated
+++ b/db/src/main/resources/sql/schema/db-schema.sql.generated
@@ -233,6 +233,7 @@ create sequence history_id_sequence start 1 increment 1;
auth_info_repo_id text,
auth_info_value text,
billing_recurrence_id int8,
+ autorenew_end_time timestamptz,
autorenew_poll_message_id int8,
billing_contact text,
deletion_poll_message_id int8,
@@ -283,6 +284,7 @@ create sequence history_id_sequence start 1 increment 1;
auth_info_repo_id text,
auth_info_value text,
billing_recurrence_id int8,
+ autorenew_end_time timestamptz,
autorenew_poll_message_id int8,
billing_contact text,
deletion_poll_message_id int8,
@@ -591,6 +593,7 @@ create index IDXhsjqiy2lyobfymplb28nm74lm on "Domain" (current_sponsor_registrar
create index IDX5mnf0wn20tno4b9do88j61klr on "Domain" (deletion_time);
create index IDXc5aw4pk1vkd6ymhvkpanmoadv on "Domain" (domain_name);
create index IDXrwl38wwkli1j7gkvtywi9jokq on "Domain" (tld);
+create index IDXlrq7v63pc21uoh3auq6eybyhl on "Domain" (autorenew_end_time);
create index IDXrh4xmrot9bd63o382ow9ltfig on "DomainHistory" (creation_time);
create index IDXaro1omfuaxjwmotk3vo00trwm on "DomainHistory" (history_registrar_id);
create index IDXsu1nam10cjes9keobapn5jvxj on "DomainHistory" (history_type);
diff --git a/db/src/main/resources/sql/schema/nomulus.golden.sql b/db/src/main/resources/sql/schema/nomulus.golden.sql
index 69be2f619..1bdd3124a 100644
--- a/db/src/main/resources/sql/schema/nomulus.golden.sql
+++ b/db/src/main/resources/sql/schema/nomulus.golden.sql
@@ -401,7 +401,8 @@ CREATE TABLE public."Domain" (
update_timestamp timestamp with time zone,
billing_recurrence_id bigint,
autorenew_poll_message_id bigint,
- deletion_poll_message_id bigint
+ deletion_poll_message_id bigint,
+ autorenew_end_time timestamp with time zone
);
@@ -464,7 +465,8 @@ CREATE TABLE public."DomainHistory" (
last_epp_update_time timestamp with time zone,
statuses text[],
update_timestamp timestamp with time zone,
- domain_repo_id text NOT NULL
+ domain_repo_id text NOT NULL,
+ autorenew_end_time timestamp with time zone
);
@@ -1386,6 +1388,13 @@ CREATE INDEX idxkjt9yaq92876dstimd93hwckh ON public."Domain" USING btree (curren
CREATE INDEX idxknk8gmj7s47q56cwpa6rmpt5l ON public."HostHistory" USING btree (history_type);
+--
+-- Name: idxlrq7v63pc21uoh3auq6eybyhl; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX idxlrq7v63pc21uoh3auq6eybyhl ON public."Domain" USING btree (autorenew_end_time);
+
+
--
-- Name: idxn1f711wicdnooa2mqb7g1m55o; Type: INDEX; Schema: public; Owner: -
--