diff --git a/java/google/registry/model/ofy/Ofy.java b/java/google/registry/model/ofy/Ofy.java index 9d9d4518e..e94024d24 100644 --- a/java/google/registry/model/ofy/Ofy.java +++ b/java/google/registry/model/ofy/Ofy.java @@ -309,6 +309,14 @@ public class Ofy { throw new AssertionError(); // How on earth did we get here? } + public void transactNewReadOnly(Runnable work) { + transactNewReadOnly( + () -> { + work.run(); + return null; + }); + } + /** Execute some work in a transactionless context. */ public R doTransactionless(Work work) { try { diff --git a/java/google/registry/model/registrar/RegistrarContact.java b/java/google/registry/model/registrar/RegistrarContact.java index 4c93e9c6d..3faef7fbf 100644 --- a/java/google/registry/model/registrar/RegistrarContact.java +++ b/java/google/registry/model/registrar/RegistrarContact.java @@ -26,7 +26,6 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Streams; import com.googlecode.objectify.Key; -import com.googlecode.objectify.VoidWork; import com.googlecode.objectify.annotation.Entity; import com.googlecode.objectify.annotation.Id; import com.googlecode.objectify.annotation.Index; @@ -156,22 +155,15 @@ public class RegistrarContact extends ImmutableObject implements Jsonifiable { final Registrar registrar, final Set contacts) { ofy() .transact( - new VoidWork() { - @Override - public void vrun() { - ofy() - .delete() - .keys( - difference( - ImmutableSet.copyOf( - ofy() - .load() - .type(RegistrarContact.class) - .ancestor(registrar) - .keys()), - contacts.stream().map(Key::create).collect(toImmutableSet()))); - ofy().save().entities(contacts); - } + () -> { + ofy() + .delete() + .keys( + difference( + ImmutableSet.copyOf( + ofy().load().type(RegistrarContact.class).ancestor(registrar).keys()), + contacts.stream().map(Key::create).collect(toImmutableSet()))); + ofy().save().entities(contacts); }); } diff --git a/java/google/registry/model/registry/label/PremiumListUtils.java b/java/google/registry/model/registry/label/PremiumListUtils.java index 12dda0800..eb5c0c46b 100644 --- a/java/google/registry/model/registry/label/PremiumListUtils.java +++ b/java/google/registry/model/registry/label/PremiumListUtils.java @@ -36,7 +36,6 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Streams; import com.googlecode.objectify.Key; -import com.googlecode.objectify.VoidWork; import google.registry.model.registry.Registry; import google.registry.model.registry.label.DomainLabelMetrics.PremiumListCheckOutcome; import google.registry.model.registry.label.PremiumList.PremiumListEntry; @@ -196,11 +195,7 @@ public final class PremiumListUtils { /** Deletes the PremiumList and all of its child entities. */ public static void deletePremiumList(final PremiumList premiumList) { - ofy().transactNew(new VoidWork() { - @Override - public void vrun() { - ofy().delete().entity(premiumList); - }}); + ofy().transactNew(() -> ofy().delete().entity(premiumList)); deleteRevisionAndEntriesOfPremiumList(premiumList); cachePremiumLists.invalidate(premiumList.getName()); } @@ -209,20 +204,13 @@ public final class PremiumListUtils { if (premiumList.getRevisionKey() == null) { return; } - for (final List> batch : partition( - ofy().load().type(PremiumListEntry.class).ancestor(premiumList.revisionKey).keys(), - TRANSACTION_BATCH_SIZE)) { - ofy().transactNew(new VoidWork() { - @Override - public void vrun() { - ofy().delete().keys(batch); - }}); + for (final List> batch : + partition( + ofy().load().type(PremiumListEntry.class).ancestor(premiumList.revisionKey).keys(), + TRANSACTION_BATCH_SIZE)) { + ofy().transactNew(() -> ofy().delete().keys(batch)); } - ofy().transactNew(new VoidWork() { - @Override - public void vrun() { - ofy().delete().key(premiumList.getRevisionKey()); - }}); + ofy().transactNew(() -> ofy().delete().key(premiumList.getRevisionKey())); } /** Returns whether a PremiumList of the given name exists, bypassing the cache. */ diff --git a/java/google/registry/model/server/Lock.java b/java/google/registry/model/server/Lock.java index f2ed0a62b..b39e2d31c 100644 --- a/java/google/registry/model/server/Lock.java +++ b/java/google/registry/model/server/Lock.java @@ -22,7 +22,6 @@ import com.google.auto.value.AutoValue; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; import com.google.common.flogger.FluentLogger; -import com.googlecode.objectify.VoidWork; import com.googlecode.objectify.annotation.Entity; import com.googlecode.objectify.annotation.Id; import google.registry.model.ImmutableObject; @@ -216,30 +215,27 @@ public class Lock extends ImmutableObject { // Just use the default clock because we aren't actually doing anything that will use the clock. ofy() .transact( - new VoidWork() { - @Override - public void vrun() { - // To release a lock, check that no one else has already obtained it and if not - // delete it. If the lock in Datastore was different then this lock is gone already; - // this can happen if release() is called around the expiration time and the lock - // expires underneath us. - Lock loadedLock = ofy().load().type(Lock.class).id(lockId).now(); - if (Lock.this.equals(loadedLock)) { - // Use noBackupOfy() so that we don't create a commit log entry for deleting the - // lock. - logger.atInfo().log("Deleting lock: %s", lockId); - ofy().deleteWithoutBackup().entity(Lock.this); - lockMetrics.recordRelease( - resourceName, tld, new Duration(acquiredTime, ofy().getTransactionTime())); - } else { - logger.atSevere().log( - "The lock we acquired was transferred to someone else before we" - + " released it! Did action take longer than lease length?" - + " Our lock: %s, current lock: %s", - Lock.this, loadedLock); - logger.atInfo().log( - "Not deleting lock: %s - someone else has it: %s", lockId, loadedLock); - } + () -> { + // To release a lock, check that no one else has already obtained it and if not + // delete it. If the lock in Datastore was different then this lock is gone already; + // this can happen if release() is called around the expiration time and the lock + // expires underneath us. + Lock loadedLock = ofy().load().type(Lock.class).id(lockId).now(); + if (Lock.this.equals(loadedLock)) { + // Use noBackupOfy() so that we don't create a commit log entry for deleting the + // lock. + logger.atInfo().log("Deleting lock: %s", lockId); + ofy().deleteWithoutBackup().entity(Lock.this); + lockMetrics.recordRelease( + resourceName, tld, new Duration(acquiredTime, ofy().getTransactionTime())); + } else { + logger.atSevere().log( + "The lock we acquired was transferred to someone else before we" + + " released it! Did action take longer than lease length?" + + " Our lock: %s, current lock: %s", + Lock.this, loadedLock); + logger.atInfo().log( + "Not deleting lock: %s - someone else has it: %s", lockId, loadedLock); } }); } diff --git a/java/google/registry/rde/imports/RdeContactImportAction.java b/java/google/registry/rde/imports/RdeContactImportAction.java index f5b496bcc..bcde3caa7 100644 --- a/java/google/registry/rde/imports/RdeContactImportAction.java +++ b/java/google/registry/rde/imports/RdeContactImportAction.java @@ -24,7 +24,6 @@ import com.google.appengine.tools.cloudstorage.RetryParams; import com.google.appengine.tools.mapreduce.Mapper; import com.google.common.collect.ImmutableList; import com.google.common.flogger.FluentLogger; -import com.googlecode.objectify.VoidWork; import google.registry.config.RegistryConfig.Config; import google.registry.config.RegistryConfig.ConfigModule; import google.registry.gcs.GcsUtils; @@ -139,14 +138,13 @@ public class RdeContactImportAction implements Runnable { // Record number of attempted map operations getContext().incrementCounter("contact imports attempted"); logger.atInfo().log("Saving contact %s", xjcContact.getId()); - ofy().transact(new VoidWork() { - @Override - public void vrun() { - ContactResource contact = - XjcToContactResourceConverter.convertContact(xjcContact); - getImportUtils().importEppResource(contact); - } - }); + ofy() + .transact( + () -> { + ContactResource contact = + XjcToContactResourceConverter.convertContact(xjcContact); + getImportUtils().importEppResource(contact); + }); // Record number of contacts imported getContext().incrementCounter("contacts saved"); logger.atInfo().log("Contact %s was imported successfully", xjcContact.getId()); diff --git a/java/google/registry/rde/imports/RdeDomainImportAction.java b/java/google/registry/rde/imports/RdeDomainImportAction.java index 76a58c989..638f65dc9 100644 --- a/java/google/registry/rde/imports/RdeDomainImportAction.java +++ b/java/google/registry/rde/imports/RdeDomainImportAction.java @@ -35,7 +35,6 @@ import com.google.appengine.tools.mapreduce.Mapper; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.flogger.FluentLogger; -import com.googlecode.objectify.VoidWork; import google.registry.config.RegistryConfig.Config; import google.registry.config.RegistryConfig.ConfigModule; import google.registry.dns.DnsQueue; @@ -171,120 +170,10 @@ public class RdeDomainImportAction implements Runnable { try { // Record number of attempted map operations getContext().incrementCounter("domain imports attempted"); - logger.atInfo().log("Saving domain %s", xjcDomain.getName()); - ofy() - .transact( - new VoidWork() { - @Override - public void vrun() { - HistoryEntry historyEntry = createHistoryEntryForDomainImport(xjcDomain); - BillingEvent.Recurring autorenewBillingEvent = - createAutoRenewBillingEventForDomainImport(xjcDomain, historyEntry); - PollMessage.Autorenew autorenewPollMessage = - createAutoRenewPollMessageForDomainImport(xjcDomain, historyEntry); - DomainResource domain = - XjcToDomainResourceConverter.convertDomain( - xjcDomain, autorenewBillingEvent, autorenewPollMessage); - getDnsQueue().addDomainRefreshTask(domain.getFullyQualifiedDomainName()); - // Keep a list of "extra objects" that need to be saved along with the domain - // and add to it if necessary. - ImmutableSet extraEntitiesToSave = - getImportUtils().createIndexesForEppResource(domain); - // Create speculative server approval entities for pending transfers - if (domain.getTransferData().getTransferStatus() == TransferStatus.PENDING) { - TransferData transferData = domain.getTransferData(); - checkArgumentNotNull( - transferData, - "Domain %s is in pending transfer but has no transfer data", - domain.getFullyQualifiedDomainName()); - Money transferCost = - getDomainRenewCost( - domain.getFullyQualifiedDomainName(), - transferData.getPendingTransferExpirationTime(), - 1); - DateTime automaticTransferTime = - transferData.getPendingTransferExpirationTime(); - // If the transfer will occur within the autorenew grace period, it should - // subsume the autorenew, so we don't add the normal extra year. See the - // original logic in DomainTransferRequestFlow (which is very similar) for - // more information. That said, note that here we stop 1 millisecond before - // the actual transfer time to avoid hitting the transfer-handling part of - // cloneProjectedAtTime(), since unlike in the DomainTransferRequestFlow case, - // this domain already has a pending transfer. - DomainResource domainAtTransferTime = - domain.cloneProjectedAtTime(automaticTransferTime.minusMillis(1)); - boolean inAutorenewGraceAtTransfer = - !domainAtTransferTime - .getGracePeriodsOfType(GracePeriodStatus.AUTO_RENEW) - .isEmpty(); - int extraYears = inAutorenewGraceAtTransfer ? 0 : 1; - // Construct the capped new expiration time. - DateTime serverApproveNewExpirationTime = - extendRegistrationWithCap( - automaticTransferTime, - domainAtTransferTime.getRegistrationExpirationTime(), - extraYears); - // Create speculative entities in anticipation of an automatic server - // approval. - ImmutableSet serverApproveEntities = - createTransferServerApproveEntities( - automaticTransferTime, - serverApproveNewExpirationTime, - historyEntry, - domain, - historyEntry.getTrid(), - transferData.getGainingClientId(), - Optional.of(transferCost), - transferData.getTransferRequestTime()); - transferData = - createPendingTransferData( - transferData.asBuilder(), - serverApproveEntities, - Period.create(1, Unit.YEARS)); - // Create a poll message to notify the losing registrar that a transfer was - // requested. - PollMessage requestPollMessage = - createLosingTransferPollMessage( - domain.getRepoId(), - transferData, - serverApproveNewExpirationTime, - historyEntry) - .asBuilder() - .setEventTime(transferData.getTransferRequestTime()) - .build(); - domain = domain.asBuilder().setTransferData(transferData).build(); - autorenewBillingEvent = - autorenewBillingEvent - .asBuilder() - .setRecurrenceEndTime(transferData.getPendingTransferExpirationTime()) - .build(); - autorenewPollMessage = - autorenewPollMessage - .asBuilder() - .setAutorenewEndTime(transferData.getPendingTransferExpirationTime()) - .build(); - extraEntitiesToSave = - new ImmutableSet.Builder<>() - .add(requestPollMessage) - .addAll(extraEntitiesToSave) - .addAll(serverApproveEntities) - .build(); - } // End pending transfer check - ofy() - .save() - .entities( - new ImmutableSet.Builder<>() - .add( - domain, - historyEntry, - autorenewBillingEvent, - autorenewPollMessage) - .addAll(extraEntitiesToSave) - .build()) - .now(); - } - }); + + ofy().transact(() -> saveDomain(xjcDomain)); + // Record the number of domains imported getContext().incrementCounter("domains saved"); logger.atInfo().log("Domain %s was imported successfully", xjcDomain.getName()); @@ -298,5 +187,101 @@ public class RdeDomainImportAction implements Runnable { "Error processing domain %s; xml=%s", xjcDomain.getName(), xjcDomain); } } + + private void saveDomain(XjcRdeDomain xjcDomain) { + HistoryEntry historyEntry = createHistoryEntryForDomainImport(xjcDomain); + BillingEvent.Recurring autorenewBillingEvent = + createAutoRenewBillingEventForDomainImport(xjcDomain, historyEntry); + PollMessage.Autorenew autorenewPollMessage = + createAutoRenewPollMessageForDomainImport(xjcDomain, historyEntry); + DomainResource domain = + XjcToDomainResourceConverter.convertDomain( + xjcDomain, autorenewBillingEvent, autorenewPollMessage); + getDnsQueue().addDomainRefreshTask(domain.getFullyQualifiedDomainName()); + // Keep a list of "extra objects" that need to be saved along with the domain + // and add to it if necessary. + ImmutableSet extraEntitiesToSave = + getImportUtils().createIndexesForEppResource(domain); + // Create speculative server approval entities for pending transfers + if (domain.getTransferData().getTransferStatus() == TransferStatus.PENDING) { + TransferData transferData = domain.getTransferData(); + checkArgumentNotNull( + transferData, + "Domain %s is in pending transfer but has no transfer data", + domain.getFullyQualifiedDomainName()); + Money transferCost = + getDomainRenewCost( + domain.getFullyQualifiedDomainName(), + transferData.getPendingTransferExpirationTime(), + 1); + DateTime automaticTransferTime = transferData.getPendingTransferExpirationTime(); + // If the transfer will occur within the autorenew grace period, it should + // subsume the autorenew, so we don't add the normal extra year. See the + // original logic in DomainTransferRequestFlow (which is very similar) for + // more information. That said, note that here we stop 1 millisecond before + // the actual transfer time to avoid hitting the transfer-handling part of + // cloneProjectedAtTime(), since unlike in the DomainTransferRequestFlow case, + // this domain already has a pending transfer. + DomainResource domainAtTransferTime = + domain.cloneProjectedAtTime(automaticTransferTime.minusMillis(1)); + boolean inAutorenewGraceAtTransfer = + !domainAtTransferTime.getGracePeriodsOfType(GracePeriodStatus.AUTO_RENEW).isEmpty(); + int extraYears = inAutorenewGraceAtTransfer ? 0 : 1; + // Construct the capped new expiration time. + DateTime serverApproveNewExpirationTime = + extendRegistrationWithCap( + automaticTransferTime, + domainAtTransferTime.getRegistrationExpirationTime(), + extraYears); + // Create speculative entities in anticipation of an automatic server + // approval. + ImmutableSet serverApproveEntities = + createTransferServerApproveEntities( + automaticTransferTime, + serverApproveNewExpirationTime, + historyEntry, + domain, + historyEntry.getTrid(), + transferData.getGainingClientId(), + Optional.of(transferCost), + transferData.getTransferRequestTime()); + transferData = + createPendingTransferData( + transferData.asBuilder(), serverApproveEntities, Period.create(1, Unit.YEARS)); + // Create a poll message to notify the losing registrar that a transfer was + // requested. + PollMessage requestPollMessage = + createLosingTransferPollMessage( + domain.getRepoId(), transferData, serverApproveNewExpirationTime, historyEntry) + .asBuilder() + .setEventTime(transferData.getTransferRequestTime()) + .build(); + domain = domain.asBuilder().setTransferData(transferData).build(); + autorenewBillingEvent = + autorenewBillingEvent + .asBuilder() + .setRecurrenceEndTime(transferData.getPendingTransferExpirationTime()) + .build(); + autorenewPollMessage = + autorenewPollMessage + .asBuilder() + .setAutorenewEndTime(transferData.getPendingTransferExpirationTime()) + .build(); + extraEntitiesToSave = + new ImmutableSet.Builder<>() + .add(requestPollMessage) + .addAll(extraEntitiesToSave) + .addAll(serverApproveEntities) + .build(); + } // End pending transfer check + ofy() + .save() + .entities( + new ImmutableSet.Builder<>() + .add(domain, historyEntry, autorenewBillingEvent, autorenewPollMessage) + .addAll(extraEntitiesToSave) + .build()) + .now(); + } } } diff --git a/java/google/registry/rde/imports/RdeHostImportAction.java b/java/google/registry/rde/imports/RdeHostImportAction.java index 8e67a6ec4..2e593e4df 100644 --- a/java/google/registry/rde/imports/RdeHostImportAction.java +++ b/java/google/registry/rde/imports/RdeHostImportAction.java @@ -24,7 +24,6 @@ import com.google.appengine.tools.cloudstorage.RetryParams; import com.google.appengine.tools.mapreduce.Mapper; import com.google.common.collect.ImmutableList; import com.google.common.flogger.FluentLogger; -import com.googlecode.objectify.VoidWork; import google.registry.config.RegistryConfig.Config; import google.registry.config.RegistryConfig.ConfigModule; import google.registry.gcs.GcsUtils; @@ -124,14 +123,12 @@ public class RdeHostImportAction implements Runnable { // Record number of attempted map operations getContext().incrementCounter("host imports attempted"); logger.atInfo().log("Saving host %s", xjcHost.getName()); - ofy().transact(new VoidWork() { - - @Override - public void vrun() { - HostResource host = XjcToHostResourceConverter.convert(xjcHost); - getImportUtils().importEppResource(host); - } - }); + ofy() + .transact( + () -> { + HostResource host = XjcToHostResourceConverter.convert(xjcHost); + getImportUtils().importEppResource(host); + }); // Record number of hosts imported getContext().incrementCounter("hosts saved"); logger.atInfo().log("Host %s was imported successfully", xjcHost.getName()); diff --git a/java/google/registry/tools/AllocateDomainCommand.java b/java/google/registry/tools/AllocateDomainCommand.java index 5141c8d14..7edc31a7d 100644 --- a/java/google/registry/tools/AllocateDomainCommand.java +++ b/java/google/registry/tools/AllocateDomainCommand.java @@ -30,10 +30,10 @@ import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; import com.google.common.base.Ascii; import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.template.soy.data.SoyMapData; import com.googlecode.objectify.Key; -import com.googlecode.objectify.VoidWork; import google.registry.flows.EppException; import google.registry.model.domain.DesignatedContact; import google.registry.model.domain.DomainApplication; @@ -99,98 +99,92 @@ final class AllocateDomainCommand extends MutatingEppToolCommand { protected void initMutatingEppToolCommand() { checkArgument(superuser, "This command MUST be run as --superuser."); setSoyTemplate(DomainAllocateSoyInfo.getInstance(), DomainAllocateSoyInfo.CREATE); - ofy() - .transactNewReadOnly( - new VoidWork() { - @Override - public void vrun() { - Iterable> keys = - transform( - Splitter.on(',').split(ids), - applicationId -> Key.create(DomainApplication.class, applicationId)); - for (DomainApplication application : ofy().load().keys(keys).values()) { - // If the application is already allocated print a warning but do not fail. - if (application.getApplicationStatus() == ApplicationStatus.ALLOCATED) { - System.err.printf( - "Application %s has already been allocated\n", application.getRepoId()); - continue; - } - // Ensure domain doesn't already have a final status which it shouldn't be updated - // from. - checkState( - !application.getApplicationStatus().isFinalStatus(), - "Application has final status %s", - application.getApplicationStatus()); - try { - HistoryEntry history = - checkNotNull( - ofy() - .load() - .type(HistoryEntry.class) - .ancestor(checkNotNull(application)) - .order("modificationTime") - .first() - .now(), - "Could not find any history entries for domain application %s", - application.getRepoId()); - String clientTransactionId = - emptyToNull(history.getTrid().getClientTransactionId().orElse(null)); - Period period = checkNotNull(extractPeriodFromXml(history.getXmlBytes())); - checkArgument(period.getUnit() == Period.Unit.YEARS); - ImmutableMap.Builder contactsMapBuilder = - new ImmutableMap.Builder<>(); - for (DesignatedContact contact : application.getContacts()) { - contactsMapBuilder.put( - Ascii.toLowerCase(contact.getType().toString()), - ofy().load().key(contact.getContactKey()).now().getForeignKey()); - } - LaunchNotice launchNotice = application.getLaunchNotice(); - addSoyRecord( - application.getCurrentSponsorClientId(), - new SoyMapData( - "name", application.getFullyQualifiedDomainName(), - "period", period.getValue(), - "nameservers", application.loadNameserverFullyQualifiedHostNames(), - "registrant", - ofy().load().key(application.getRegistrant()).now().getForeignKey(), - "contacts", contactsMapBuilder.build(), - "authInfo", application.getAuthInfo().getPw().getValue(), - "smdId", - application.getEncodedSignedMarks().isEmpty() - ? null - : unmarshal( - SignedMark.class, - application.getEncodedSignedMarks().get(0).getBytes()) - .getId(), - "applicationRoid", application.getRepoId(), - "applicationTime", application.getCreationTime().toString(), - "launchNotice", - launchNotice == null - ? null - : ImmutableMap.of( - "noticeId", launchNotice.getNoticeId().getTcnId(), - "expirationTime", - launchNotice.getExpirationTime().toString(), - "acceptedTime", launchNotice.getAcceptedTime().toString()), - "dsRecords", - application - .getDsData() - .stream() - .map( - dsData -> - ImmutableMap.of( - "keyTag", dsData.getKeyTag(), - "algorithm", dsData.getAlgorithm(), - "digestType", dsData.getDigestType(), - "digest", base16().encode(dsData.getDigest()))) - .collect(toImmutableList()), - "clTrid", clientTransactionId)); - applicationKeys.add(Key.create(application)); - } catch (EppException e) { - throw new RuntimeException(e); - } - } - } - }); + ofy().transactNewReadOnly(this::initAllocateDomainCommand); + } + + private void initAllocateDomainCommand() { + Iterable> keys = + transform( + Splitter.on(',').split(ids), + applicationId -> Key.create(DomainApplication.class, applicationId)); + for (DomainApplication application : ofy().load().keys(keys).values()) { + // If the application is already allocated print a warning but do not fail. + if (application.getApplicationStatus() == ApplicationStatus.ALLOCATED) { + System.err.printf("Application %s has already been allocated\n", application.getRepoId()); + continue; + } + // Ensure domain doesn't already have a final status which it shouldn't be updated + // from. + checkState( + !application.getApplicationStatus().isFinalStatus(), + "Application has final status %s", + application.getApplicationStatus()); + try { + HistoryEntry history = + checkNotNull( + ofy() + .load() + .type(HistoryEntry.class) + .ancestor(checkNotNull(application)) + .order("modificationTime") + .first() + .now(), + "Could not find any history entries for domain application %s", + application.getRepoId()); + String clientTransactionId = + emptyToNull(history.getTrid().getClientTransactionId().orElse(null)); + Period period = checkNotNull(extractPeriodFromXml(history.getXmlBytes())); + checkArgument(period.getUnit() == Period.Unit.YEARS); + ImmutableMap.Builder contactsMapBuilder = new ImmutableMap.Builder<>(); + for (DesignatedContact contact : application.getContacts()) { + contactsMapBuilder.put( + Ascii.toLowerCase(contact.getType().toString()), + ofy().load().key(contact.getContactKey()).now().getForeignKey()); + } + LaunchNotice launchNotice = application.getLaunchNotice(); + String smdId = + application.getEncodedSignedMarks().isEmpty() + ? null + : unmarshal(SignedMark.class, application.getEncodedSignedMarks().get(0).getBytes()) + .getId(); + ImmutableMap launchNoticeMap = + (launchNotice == null) + ? null + : ImmutableMap.of( + "noticeId", launchNotice.getNoticeId().getTcnId(), + "expirationTime", launchNotice.getExpirationTime().toString(), + "acceptedTime", launchNotice.getAcceptedTime().toString()); + ImmutableList> dsRecords = + application + .getDsData() + .stream() + .map( + dsData -> + ImmutableMap.of( + "keyTag", dsData.getKeyTag(), + "algorithm", dsData.getAlgorithm(), + "digestType", dsData.getDigestType(), + "digest", base16().encode(dsData.getDigest()))) + .collect(toImmutableList()); + addSoyRecord( + application.getCurrentSponsorClientId(), + new SoyMapData( + "name", application.getFullyQualifiedDomainName(), + "period", period.getValue(), + "nameservers", application.loadNameserverFullyQualifiedHostNames(), + "registrant", ofy().load().key(application.getRegistrant()).now().getForeignKey(), + "contacts", contactsMapBuilder.build(), + "authInfo", application.getAuthInfo().getPw().getValue(), + "smdId", smdId, + "applicationRoid", application.getRepoId(), + "applicationTime", application.getCreationTime().toString(), + "launchNotice", launchNoticeMap, + "dsRecords", dsRecords, + "clTrid", clientTransactionId)); + applicationKeys.add(Key.create(application)); + } catch (EppException e) { + throw new RuntimeException(e); + } + } } } diff --git a/java/google/registry/tools/DeleteTldCommand.java b/java/google/registry/tools/DeleteTldCommand.java index c63b6f3bf..b6c3741b5 100644 --- a/java/google/registry/tools/DeleteTldCommand.java +++ b/java/google/registry/tools/DeleteTldCommand.java @@ -19,7 +19,6 @@ import static google.registry.model.ofy.ObjectifyService.ofy; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; -import com.googlecode.objectify.VoidWork; import google.registry.model.domain.DomainResource; import google.registry.model.registrar.Registrar; import google.registry.model.registry.Registry; @@ -78,11 +77,7 @@ final class DeleteTldCommand extends ConfirmingCommand implements RemoteApiComma @Override protected String execute() { - ofy().transactNew(new VoidWork() { - @Override - public void vrun() { - ofy().delete().entity(registry).now(); - }}); + ofy().transactNew(() -> ofy().delete().entity(registry).now()); registry.invalidateInCache(); return String.format("Deleted TLD '%s'.\n", tld); } diff --git a/java/google/registry/tools/MutatingCommand.java b/java/google/registry/tools/MutatingCommand.java index 823891ab0..7650f9d63 100644 --- a/java/google/registry/tools/MutatingCommand.java +++ b/java/google/registry/tools/MutatingCommand.java @@ -30,7 +30,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.googlecode.objectify.Key; -import com.googlecode.objectify.VoidWork; import google.registry.model.ImmutableObject; import google.registry.tools.Command.RemoteApiCommand; import java.util.ArrayList; @@ -142,39 +141,33 @@ public abstract class MutatingCommand extends ConfirmingCommand implements Remot @Override protected String execute() throws Exception { for (final List batch : getCollatedEntityChangeBatches()) { - ofy().transact(new VoidWork() { - @Override - public void vrun() { - for (EntityChange change : batch) { - // Load the key of the entity to mutate and double-check that it hasn't been - // modified from the version that existed when the change was prepared. - ImmutableObject existingEntity = ofy().load().key(change.key).now(); - checkState( - Objects.equals(change.oldEntity, existingEntity), - "Entity changed since init() was called.\n%s", - prettyPrintEntityDeepDiff( - change.oldEntity == null ? ImmutableMap.of() - : change.oldEntity.toDiffableFieldMap(), - existingEntity == null ? ImmutableMap.of() - : existingEntity.toDiffableFieldMap())); - switch (change.type) { - case CREATE: // Fall through. - case UPDATE: - ofy().save().entity(change.newEntity).now(); - break; - case DELETE: - ofy().delete().key(change.key).now(); - break; - default: - throw new UnsupportedOperationException( - "Unknown entity change type: " + change.type); - } - } - }}); + ofy().transact(() -> batch.forEach(this::executeChange)); } return String.format("Updated %d entities.\n", changedEntitiesMap.size()); } + private void executeChange(EntityChange change) { + // Load the key of the entity to mutate and double-check that it hasn't been + // modified from the version that existed when the change was prepared. + ImmutableObject existingEntity = ofy().load().key(change.key).now(); + checkState( + Objects.equals(change.oldEntity, existingEntity), + "Entity changed since init() was called.\n%s", + prettyPrintEntityDeepDiff( + (change.oldEntity == null) ? ImmutableMap.of() : change.oldEntity.toDiffableFieldMap(), + (existingEntity == null) ? ImmutableMap.of() : existingEntity.toDiffableFieldMap())); + switch (change.type) { + case CREATE: // Fall through. + case UPDATE: + ofy().save().entity(change.newEntity).now(); + return; + case DELETE: + ofy().delete().key(change.key).now(); + return; + } + throw new UnsupportedOperationException("Unknown entity change type: " + change.type); + } + /** * Returns a set of lists of EntityChange actions to commit. Each list should be executed in * order inside a single transaction. diff --git a/java/google/registry/tools/server/DeleteEntityAction.java b/java/google/registry/tools/server/DeleteEntityAction.java index f5d8df65d..fd83a8913 100644 --- a/java/google/registry/tools/server/DeleteEntityAction.java +++ b/java/google/registry/tools/server/DeleteEntityAction.java @@ -25,7 +25,6 @@ import com.google.appengine.api.datastore.KeyFactory; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import com.google.common.flogger.FluentLogger; -import com.googlecode.objectify.VoidWork; import com.googlecode.objectify.impl.EntityMetadata; import google.registry.request.Action; import google.registry.request.HttpException.BadRequestException; @@ -90,11 +89,7 @@ public class DeleteEntityAction implements Runnable { getDatastoreService().delete(rawDeletions); // Delete ofy entities. final ImmutableList ofyDeletions = ofyDeletionsBuilder.build(); - ofy().transactNew(new VoidWork() { - @Override - public void vrun() { - ofy().delete().entities(ofyDeletions).now(); - }}); + ofy().transactNew(() -> ofy().delete().entities(ofyDeletions).now()); String message = String.format( "Deleted %d raw entities and %d registered entities", rawDeletions.size(), diff --git a/javatests/google/registry/model/common/GaeUserIdConverterTest.java b/javatests/google/registry/model/common/GaeUserIdConverterTest.java index 39e830899..8d5094702 100644 --- a/javatests/google/registry/model/common/GaeUserIdConverterTest.java +++ b/javatests/google/registry/model/common/GaeUserIdConverterTest.java @@ -17,7 +17,6 @@ package google.registry.model.common; import static com.google.common.truth.Truth.assertThat; import static google.registry.model.ofy.ObjectifyService.ofy; -import com.googlecode.objectify.VoidWork; import google.registry.testing.AppEngineRule; import org.junit.After; import org.junit.Rule; @@ -47,11 +46,10 @@ public class GaeUserIdConverterTest { @Test public void testSuccess_inTransaction() { - ofy().transactNew(new VoidWork() { - @Override - public void vrun() { - assertThat(GaeUserIdConverter.convertEmailAddressToGaeUserId("example@example.com")) - .matches("[0-9]+"); - }}); + ofy() + .transactNew( + () -> + assertThat(GaeUserIdConverter.convertEmailAddressToGaeUserId("example@example.com")) + .matches("[0-9]+")); } } diff --git a/javatests/google/registry/model/ofy/OfyTest.java b/javatests/google/registry/model/ofy/OfyTest.java index 96d679bb9..ced4d285d 100644 --- a/javatests/google/registry/model/ofy/OfyTest.java +++ b/javatests/google/registry/model/ofy/OfyTest.java @@ -83,13 +83,13 @@ public class OfyTest { // This can't be initialized earlier because namespaces need the AppEngineRule to work. } - private void doBackupGroupRootTimestampInversionTest(VoidWork work) { + private void doBackupGroupRootTimestampInversionTest(Runnable runnable) { DateTime groupTimestamp = ofy().load().key(someObject.getParent()).now() .getUpdateAutoTimestamp().getTimestamp(); // Set the clock in Ofy to the same time as the backup group root's save time. Ofy ofy = new Ofy(new FakeClock(groupTimestamp)); TimestampInversionException thrown = - assertThrows(TimestampInversionException.class, () -> ofy.transact(work)); + assertThrows(TimestampInversionException.class, () -> ofy.transact(runnable)); assertThat(thrown) .hasMessageThat() .contains( @@ -101,20 +101,12 @@ public class OfyTest { @Test public void testBackupGroupRootTimestampsMustIncreaseOnSave() { - doBackupGroupRootTimestampInversionTest(new VoidWork() { - @Override - public void vrun() { - ofy().save().entity(someObject); - }}); + doBackupGroupRootTimestampInversionTest(() -> ofy().save().entity(someObject)); } @Test public void testBackupGroupRootTimestampsMustIncreaseOnDelete() { - doBackupGroupRootTimestampInversionTest(new VoidWork() { - @Override - public void vrun() { - ofy().delete().entity(someObject); - }}); + doBackupGroupRootTimestampInversionTest(() -> ofy().delete().entity(someObject)); } @Test @@ -125,12 +117,9 @@ public class OfyTest { () -> ofy() .transact( - new VoidWork() { - @Override - public void vrun() { - ofy().save().entity(someObject); - ofy().save().entity(someObject); - } + () -> { + ofy().save().entity(someObject); + ofy().save().entity(someObject); })); assertThat(thrown).hasMessageThat().contains("Multiple entries with same key"); } @@ -143,12 +132,9 @@ public class OfyTest { () -> ofy() .transact( - new VoidWork() { - @Override - public void vrun() { - ofy().delete().entity(someObject); - ofy().delete().entity(someObject); - } + () -> { + ofy().delete().entity(someObject); + ofy().delete().entity(someObject); })); assertThat(thrown).hasMessageThat().contains("Multiple entries with same key"); } @@ -161,12 +147,9 @@ public class OfyTest { () -> ofy() .transact( - new VoidWork() { - @Override - public void vrun() { - ofy().save().entity(someObject); - ofy().delete().entity(someObject); - } + () -> { + ofy().save().entity(someObject); + ofy().delete().entity(someObject); })); assertThat(thrown).hasMessageThat().contains("Multiple entries with same key"); } @@ -179,12 +162,9 @@ public class OfyTest { () -> ofy() .transact( - new VoidWork() { - @Override - public void vrun() { - ofy().delete().entity(someObject); - ofy().save().entity(someObject); - } + () -> { + ofy().delete().entity(someObject); + ofy().save().entity(someObject); })); assertThat(thrown).hasMessageThat().contains("Multiple entries with same key"); } @@ -193,15 +173,7 @@ public class OfyTest { public void testSavingKeyTwiceInOneCall() { assertThrows( IllegalArgumentException.class, - () -> - ofy() - .transact( - new VoidWork() { - @Override - public void vrun() { - ofy().save().entities(someObject, someObject); - } - })); + () -> ofy().transact(() -> ofy().save().entities(someObject, someObject))); } /** Simple entity class with lifecycle callbacks. */ @@ -241,11 +213,7 @@ public class OfyTest { public void testLifecycleCallbacks_loadFromDatastore() { ofy().factory().register(LifecycleObject.class); final LifecycleObject object = new LifecycleObject(); - ofy().transact(new VoidWork() { - @Override - public void vrun() { - ofy().save().entity(object).now(); - }}); + ofy().transact(() -> ofy().save().entity(object).now()); assertThat(object.onSaveCalled).isTrue(); ofy().clearSessionCache(); assertThat(ofy().load().entity(object).now().onLoadCalled).isTrue(); diff --git a/javatests/google/registry/model/rde/RdeRevisionTest.java b/javatests/google/registry/model/rde/RdeRevisionTest.java index f11b4e995..924f02576 100644 --- a/javatests/google/registry/model/rde/RdeRevisionTest.java +++ b/javatests/google/registry/model/rde/RdeRevisionTest.java @@ -22,7 +22,6 @@ import static google.registry.model.rde.RdeRevision.saveRevision; import static google.registry.testing.JUnitBackports.assertThrows; import com.google.common.base.VerifyException; -import com.googlecode.objectify.VoidWork; import google.registry.testing.AppEngineRule; import org.joda.time.DateTime; import org.junit.Rule; @@ -51,17 +50,12 @@ public class RdeRevisionTest { @Test public void testSaveRevision_objectDoesntExist_newRevisionIsZero_nextRevIsOne() { - ofy().transact(new VoidWork() { - @Override - public void vrun() { - saveRevision("despondency", DateTime.parse("1984-12-18TZ"), FULL, 0); - }}); - ofy().transact(new VoidWork() { - @Override - public void vrun() { - assertThat(getNextRevision("despondency", DateTime.parse("1984-12-18TZ"), FULL)) - .isEqualTo(1); - }}); + ofy().transact(() -> saveRevision("despondency", DateTime.parse("1984-12-18TZ"), FULL, 0)); + ofy() + .transact( + () -> + assertThat(getNextRevision("despondency", DateTime.parse("1984-12-18TZ"), FULL)) + .isEqualTo(1)); } @Test @@ -72,12 +66,8 @@ public class RdeRevisionTest { () -> ofy() .transact( - new VoidWork() { - @Override - public void vrun() { - saveRevision("despondency", DateTime.parse("1984-12-18TZ"), FULL, 1); - } - })); + () -> + saveRevision("despondency", DateTime.parse("1984-12-18TZ"), FULL, 1))); assertThat(thrown).hasMessageThat().contains("object missing"); } @@ -90,29 +80,19 @@ public class RdeRevisionTest { () -> ofy() .transact( - new VoidWork() { - @Override - public void vrun() { - saveRevision("melancholy", DateTime.parse("1984-12-18TZ"), FULL, 0); - } - })); + () -> saveRevision("melancholy", DateTime.parse("1984-12-18TZ"), FULL, 0))); assertThat(thrown).hasMessageThat().contains("object already created"); } @Test public void testSaveRevision_objectExistsAtZero_newRevisionIsOne_nextRevIsTwo() { save("melancholy", DateTime.parse("1984-12-18TZ"), FULL, 0); - ofy().transact(new VoidWork() { - @Override - public void vrun() { - saveRevision("melancholy", DateTime.parse("1984-12-18TZ"), FULL, 1); - }}); - ofy().transact(new VoidWork() { - @Override - public void vrun() { - assertThat(getNextRevision("melancholy", DateTime.parse("1984-12-18TZ"), FULL)) - .isEqualTo(2); - }}); + ofy().transact(() -> saveRevision("melancholy", DateTime.parse("1984-12-18TZ"), FULL, 1)); + ofy() + .transact( + () -> + assertThat(getNextRevision("melancholy", DateTime.parse("1984-12-18TZ"), FULL)) + .isEqualTo(2)); } @Test @@ -124,12 +104,7 @@ public class RdeRevisionTest { () -> ofy() .transact( - new VoidWork() { - @Override - public void vrun() { - saveRevision("melancholy", DateTime.parse("1984-12-18TZ"), FULL, 2); - } - })); + () -> saveRevision("melancholy", DateTime.parse("1984-12-18TZ"), FULL, 2))); assertThat(thrown).hasMessageThat().contains("should be at 1 "); } @@ -141,12 +116,8 @@ public class RdeRevisionTest { () -> ofy() .transact( - new VoidWork() { - @Override - public void vrun() { - saveRevision("melancholy", DateTime.parse("1984-12-18TZ"), FULL, -1); - } - })); + () -> + saveRevision("melancholy", DateTime.parse("1984-12-18TZ"), FULL, -1))); assertThat(thrown).hasMessageThat().contains("Negative revision"); } diff --git a/javatests/google/registry/model/registrar/RegistrarTest.java b/javatests/google/registry/model/registrar/RegistrarTest.java index 64652fa8b..066660683 100644 --- a/javatests/google/registry/model/registrar/RegistrarTest.java +++ b/javatests/google/registry/model/registrar/RegistrarTest.java @@ -33,7 +33,6 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import com.googlecode.objectify.Key; -import com.googlecode.objectify.VoidWork; import google.registry.model.EntityTestCase; import google.registry.model.common.EntityGroupRoot; import google.registry.model.registrar.Registrar.State; @@ -418,15 +417,16 @@ public class RegistrarTest extends EntityTestCase { @Test public void testLoadByClientIdCached_isTransactionless() { - ofy().transact(new VoidWork() { - @Override - public void vrun() { - assertThat(Registrar.loadByClientIdCached("registrar")).isPresent(); - // Load something as a control to make sure we are seeing loaded keys in the session cache. - ofy().load().entity(abuseAdminContact).now(); - assertThat(ofy().getSessionKeys()).contains(Key.create(abuseAdminContact)); - assertThat(ofy().getSessionKeys()).doesNotContain(Key.create(registrar)); - }}); + ofy() + .transact( + () -> { + assertThat(Registrar.loadByClientIdCached("registrar")).isPresent(); + // Load something as a control to make sure we are seeing loaded keys in the session + // cache. + ofy().load().entity(abuseAdminContact).now(); + assertThat(ofy().getSessionKeys()).contains(Key.create(abuseAdminContact)); + assertThat(ofy().getSessionKeys()).doesNotContain(Key.create(registrar)); + }); ofy().clearSessionCache(); // Conversely, loads outside of a transaction should end up in the session cache. assertThat(Registrar.loadByClientIdCached("registrar")).isPresent(); diff --git a/javatests/google/registry/model/registry/label/PremiumListUtilsTest.java b/javatests/google/registry/model/registry/label/PremiumListUtilsTest.java index ae8ea06d2..0feb5e9b4 100644 --- a/javatests/google/registry/model/registry/label/PremiumListUtilsTest.java +++ b/javatests/google/registry/model/registry/label/PremiumListUtilsTest.java @@ -41,7 +41,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.googlecode.objectify.Key; -import com.googlecode.objectify.VoidWork; import google.registry.dns.writer.VoidDnsWriter; import google.registry.model.pricing.StaticPremiumListPricingEngine; import google.registry.model.registry.Registry; @@ -193,18 +192,14 @@ public class PremiumListUtilsTest { // Remove one of the premium list entries from behind the Bloom filter's back. ofy() .transactNew( - new VoidWork() { - @Override - public void vrun() { + () -> ofy() .delete() .keys( Key.create( PremiumList.getCached("tld").get().getRevisionKey(), PremiumListEntry.class, - "rich")); - } - }); + "rich"))); ofy().clearSessionCache(); assertThat(getPremiumPrice("rich", Registry.get("tld"))).isEmpty(); diff --git a/javatests/google/registry/testing/DatastoreHelper.java b/javatests/google/registry/testing/DatastoreHelper.java index 74aed39a8..70e20427c 100644 --- a/javatests/google/registry/testing/DatastoreHelper.java +++ b/javatests/google/registry/testing/DatastoreHelper.java @@ -53,7 +53,6 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Streams; import com.google.common.net.InetAddresses; import com.googlecode.objectify.Key; -import com.googlecode.objectify.VoidWork; import com.googlecode.objectify.cmd.Saver; import google.registry.dns.writer.VoidDnsWriter; import google.registry.model.Buildable; @@ -366,11 +365,7 @@ public class DatastoreHelper { final DomainResource persistedDomain = persistResource(domain); // Calls {@link LordnTask#enqueueDomainResourceTask} wrapped in an ofy transaction so that the // transaction time is set correctly. - ofy().transactNew(new VoidWork() { - @Override - public void vrun() { - LordnTask.enqueueDomainResourceTask(persistedDomain); - }}); + ofy().transactNew(() -> LordnTask.enqueueDomainResourceTask(persistedDomain)); return persistedDomain; } @@ -947,11 +942,7 @@ public class DatastoreHelper { assertWithMessage("Attempting to persist a Builder is almost certainly an error in test code") .that(resource) .isNotInstanceOf(Buildable.Builder.class); - ofy().transact(new VoidWork() { - @Override - public void vrun() { - saveResource(resource, wantBackup); - }}); + ofy().transact(() -> saveResource(resource, wantBackup)); // Force the session cache to be cleared so that when we read the resource back, we read from // Datastore and not from the session cache. This is needed to trigger Objectify's load process // (unmarshalling entity protos to POJOs, nulling out empty collections, calling @OnLoad @@ -964,13 +955,13 @@ public class DatastoreHelper { public static R persistEppResourceInFirstBucket(final R resource) { final EppResourceIndex eppResourceIndex = EppResourceIndex.create(Key.create(EppResourceIndexBucket.class, 1), Key.create(resource)); - ofy().transact(new VoidWork() { - @Override - public void vrun() { - Saver saver = ofy().save(); - saver.entity(resource); - persistEppResourceExtras(resource, eppResourceIndex, saver); - }}); + ofy() + .transact( + () -> { + Saver saver = ofy().save(); + saver.entity(resource); + persistEppResourceExtras(resource, eppResourceIndex, saver); + }); ofy().clearSessionCache(); return ofy().load().entity(resource).now(); } @@ -987,13 +978,7 @@ public class DatastoreHelper { } // Persist domains ten at a time, to avoid exceeding the entity group limit. for (final List chunk : Iterables.partition(resources, 10)) { - ofy().transact(new VoidWork() { - @Override - public void vrun() { - for (R resource : chunk) { - saveResource(resource, wantBackup); - } - }}); + ofy().transact(() -> chunk.forEach(resource -> saveResource(resource, wantBackup))); } // Force the session to be cleared so that when we read it back, we read from Datastore // and not from the transaction's session cache. @@ -1015,18 +1000,20 @@ public class DatastoreHelper { */ public static R persistEppResource(final R resource) { checkState(!ofy().inTransaction()); - ofy().transact(new VoidWork() { - @Override - public void vrun() { - ofy().save().entities( - resource, - new HistoryEntry.Builder() - .setParent(resource) - .setType(getHistoryEntryType(resource)) - .setModificationTime(ofy().getTransactionTime()) - .build()); - ofy().save().entity(ForeignKeyIndex.create(resource, resource.getDeletionTime())); - }}); + ofy() + .transact( + () -> { + ofy() + .save() + .entities( + resource, + new HistoryEntry.Builder() + .setParent(resource) + .setType(getHistoryEntryType(resource)) + .setModificationTime(ofy().getTransactionTime()) + .build()); + ofy().save().entity(ForeignKeyIndex.create(resource, resource.getDeletionTime())); + }); ofy().clearSessionCache(); return ofy().load().entity(resource).safe(); } @@ -1099,11 +1086,7 @@ public class DatastoreHelper { * ForeignKeyedEppResources. */ public static ImmutableList persistSimpleResources(final Iterable resources) { - ofy().transact(new VoidWork(){ - @Override - public void vrun() { - ofy().saveWithoutBackup().entities(resources); - }}); + ofy().transact(() -> ofy().saveWithoutBackup().entities(resources)); // Force the session to be cleared so that when we read it back, we read from Datastore // and not from the transaction's session cache. ofy().clearSessionCache(); diff --git a/javatests/google/registry/tmch/LordnTaskTest.java b/javatests/google/registry/tmch/LordnTaskTest.java index ad0fd6032..d2229d95d 100644 --- a/javatests/google/registry/tmch/LordnTaskTest.java +++ b/javatests/google/registry/tmch/LordnTaskTest.java @@ -35,7 +35,6 @@ import com.google.appengine.api.taskqueue.TransientFailureException; import com.google.apphosting.api.DeadlineExceededException; import com.google.common.collect.ImmutableList; import com.googlecode.objectify.Key; -import com.googlecode.objectify.VoidWork; import google.registry.model.domain.DomainResource; import google.registry.model.domain.launch.LaunchNotice; import google.registry.model.ofy.Ofy; @@ -174,15 +173,7 @@ public class LordnTaskTest { public void test_enqueueDomainResourceTask_throwsNpeOnNullDomain() { assertThrows( NullPointerException.class, - () -> - ofy() - .transactNew( - new VoidWork() { - @Override - public void vrun() { - LordnTask.enqueueDomainResourceTask(null); - } - })); + () -> ofy().transactNew(() -> LordnTask.enqueueDomainResourceTask(null))); } @SuppressWarnings("unchecked")