mirror of
https://github.com/google/nomulus.git
synced 2025-05-12 22:38:16 +02:00
mv com/google/domain/registry google/registry
This change renames directories in preparation for the great package rename. The repository is now in a broken state because the code itself hasn't been updated. However this should ensure that git correctly preserves history for each file.
This commit is contained in:
parent
a41677aea1
commit
5012893c1d
2396 changed files with 0 additions and 0 deletions
237
java/google/registry/tools/MutatingCommand.java
Normal file
237
java/google/registry/tools/MutatingCommand.java
Normal file
|
@ -0,0 +1,237 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.domain.registry.tools;
|
||||
|
||||
import static com.google.common.base.CaseFormat.UPPER_CAMEL;
|
||||
import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
|
||||
import static com.google.common.base.Functions.toStringFunction;
|
||||
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 com.google.common.base.Strings.emptyToNull;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.util.DatastoreServiceUtils.getNameOrId;
|
||||
import static com.google.domain.registry.util.DiffUtils.prettyPrintDeepDiff;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.domain.registry.model.ImmutableObject;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.VoidWork;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** A {@link ConfirmingCommand} that changes objects in the datastore. */
|
||||
public abstract class MutatingCommand extends ConfirmingCommand implements RemoteApiCommand {
|
||||
|
||||
/**
|
||||
* A mutation of a specific entity, represented by an old and a new version of the entity.
|
||||
* Storing the old version is necessary to enable checking that the existing entity has not been
|
||||
* modified when applying a mutation that was created outside the same transaction.
|
||||
*/
|
||||
private static class EntityChange {
|
||||
|
||||
/** The possible types of mutation that can be performed on an entity. */
|
||||
public static enum ChangeType {
|
||||
CREATE, DELETE, UPDATE;
|
||||
|
||||
/** Return the ChangeType corresponding to the given combination of version existences. */
|
||||
public static ChangeType get(boolean hasOldVersion, boolean hasNewVersion) {
|
||||
checkArgument(
|
||||
hasOldVersion || hasNewVersion,
|
||||
"An entity change must have an old version or a new version (or both)");
|
||||
return !hasOldVersion ? CREATE : (!hasNewVersion ? DELETE : UPDATE);
|
||||
}
|
||||
}
|
||||
|
||||
/** The type of mutation being performed on the entity. */
|
||||
final ChangeType type;
|
||||
|
||||
/** The old version of the entity, or null if this is a create. */
|
||||
final ImmutableObject oldEntity;
|
||||
|
||||
/** The new version of the entity, or null if this is a delete. */
|
||||
final ImmutableObject newEntity;
|
||||
|
||||
/** The key that points to the entity being changed. */
|
||||
final Key<ImmutableObject> key;
|
||||
|
||||
public EntityChange(ImmutableObject oldEntity, ImmutableObject newEntity) {
|
||||
type = ChangeType.get(oldEntity != null, newEntity != null);
|
||||
checkArgument(
|
||||
type != ChangeType.UPDATE || Key.create(oldEntity).equals(Key.create(newEntity)),
|
||||
"Both entity versions in an update must have the same Key.");
|
||||
this.oldEntity = oldEntity;
|
||||
this.newEntity = newEntity;
|
||||
key = Key.create(MoreObjects.firstNonNull(oldEntity, newEntity));
|
||||
}
|
||||
|
||||
/** Returns a human-readable ID string for the entity being changed. */
|
||||
public String getEntityId() {
|
||||
return String.format(
|
||||
"%s@%s",
|
||||
key.getKind(),
|
||||
// NB: try name before id, since name defaults to null, whereas id defaults to 0.
|
||||
getNameOrId(key.getRaw()));
|
||||
}
|
||||
|
||||
/** Returns a string representation of this entity change. */
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"%s %s\n%s",
|
||||
UPPER_UNDERSCORE.to(UPPER_CAMEL, type.toString()),
|
||||
getEntityId(),
|
||||
type == ChangeType.UPDATE
|
||||
? Optional
|
||||
.fromNullable(emptyToNull(prettyPrintDeepDiff(
|
||||
oldEntity.toDiffableFieldMap(), newEntity.toDiffableFieldMap())))
|
||||
.or("[no changes]\n")
|
||||
: (MoreObjects.firstNonNull(oldEntity, newEntity) + "\n"));
|
||||
}
|
||||
}
|
||||
|
||||
/** Map from entity keys to EntityChange objects representing changes to those entities. */
|
||||
private final LinkedHashMap<Key<ImmutableObject>, EntityChange> changedEntitiesMap =
|
||||
new LinkedHashMap<>();
|
||||
|
||||
/** A set of resource keys for which new transactions should be created after. */
|
||||
private final Set<Key<ImmutableObject>> transactionBoundaries = new HashSet<>();
|
||||
|
||||
@Nullable
|
||||
private Key<ImmutableObject> lastAddedKey;
|
||||
|
||||
/**
|
||||
* Initializes the command.
|
||||
* <p>
|
||||
* Subclasses override this method to populate {@link #changedEntitiesMap} with updated
|
||||
* entities. The old entity is the key and the new entity is the value; the key is null for
|
||||
* newly created entities and the value is null for deleted entities.
|
||||
*/
|
||||
@Override
|
||||
protected abstract void init() throws Exception;
|
||||
|
||||
/**
|
||||
* Performs the command and returns a result description.
|
||||
*
|
||||
* <p>Subclasses can override this method if the command does something besides update entities,
|
||||
* such as running a full flow.
|
||||
*/
|
||||
@Override
|
||||
protected String execute() throws Exception {
|
||||
for (final List<EntityChange> 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",
|
||||
prettyPrintDeepDiff(
|
||||
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);
|
||||
}
|
||||
}
|
||||
}});
|
||||
}
|
||||
return String.format("Updated %d entities.\n", changedEntitiesMap.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of lists of EntityChange actions to commit. Each list should be executed in
|
||||
* order inside a single transaction.
|
||||
*/
|
||||
private ImmutableSet<ImmutableList<EntityChange>> getCollatedEntityChangeBatches() {
|
||||
ImmutableSet.Builder<ImmutableList<EntityChange>> batches = new ImmutableSet.Builder<>();
|
||||
ArrayList<EntityChange> nextBatch = new ArrayList<>();
|
||||
for (EntityChange change : changedEntitiesMap.values()) {
|
||||
nextBatch.add(change);
|
||||
if (transactionBoundaries.contains(change.key)) {
|
||||
batches.add(ImmutableList.copyOf(nextBatch));
|
||||
nextBatch.clear();
|
||||
}
|
||||
}
|
||||
if (!nextBatch.isEmpty()) {
|
||||
batches.add(ImmutableList.copyOf(nextBatch));
|
||||
}
|
||||
return batches.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can call this to stage a mutation to an entity that will be applied by execute().
|
||||
* Note that both objects passed must correspond to versions of the same entity with the same key.
|
||||
*
|
||||
* @param oldEntity the existing version of the entity, or null to create a new entity
|
||||
* @param newEntity the new version of the entity to save, or null to delete the entity
|
||||
*/
|
||||
protected void stageEntityChange(
|
||||
@Nullable ImmutableObject oldEntity, @Nullable ImmutableObject newEntity) {
|
||||
EntityChange change = new EntityChange(oldEntity, newEntity);
|
||||
checkArgument(
|
||||
!changedEntitiesMap.containsKey(change.key),
|
||||
"Cannot apply multiple changes for the same entity: %s",
|
||||
change.getEntityId());
|
||||
changedEntitiesMap.put(change.key, change);
|
||||
lastAddedKey = change.key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can call this to write out all previously requested entity changes since the last
|
||||
* transaction flush in a transaction.
|
||||
*/
|
||||
protected void flushTransaction() {
|
||||
transactionBoundaries.add(checkNotNull(lastAddedKey));
|
||||
}
|
||||
|
||||
/** Returns the changes that have been staged thus far. */
|
||||
@Override
|
||||
protected String prompt() {
|
||||
return changedEntitiesMap.isEmpty()
|
||||
? "No entity changes to apply."
|
||||
: Joiner.on("\n").join(FluentIterable
|
||||
.from(changedEntitiesMap.values())
|
||||
.transform(toStringFunction()));
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue