--- /dev/null
+package org.opendaylight.controller.md.sal.common.api.data;
+
+/**
+*
+* Failure of asynchronous transaction commit caused by failure
+* of optimistic locking.
+*
+* This exception is raised and returned when transaction commit
+* failed, because other transaction finished successfully
+* and modified same data as failed transaction.
+*
+* Clients may recover from this error condition by
+* retrieving current state and submitting new updated
+* transaction.
+*
+*/
+public class OptimisticLockFailedException extends TransactionCommitFailedException {
+
+ private static final long serialVersionUID = 1L;
+
+ protected OptimisticLockFailedException(final String message, final Throwable cause, final boolean enableSuppression,
+ final boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+
+ public OptimisticLockFailedException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+
+ public OptimisticLockFailedException(final String message) {
+ super(message);
+ }
+
+}
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.md.sal.dom.store.impl.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ConflictingModificationAppliedException;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTree;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTreeCandidate;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTreeModification;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTreeSnapshot;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataValidationFailedException;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.InMemoryDataTreeFactory;
import org.opendaylight.controller.sal.core.spi.data.DOMStore;
public ListenableFuture<Boolean> canCommit() {
return executor.submit(new Callable<Boolean>() {
@Override
- public Boolean call() {
+ public Boolean call() throws TransactionCommitFailedException {
try {
dataTree.validate(modification);
LOG.debug("Store Transaction: {} can be committed", transaction.getIdentifier());
return true;
- } catch (DataPreconditionFailedException e) {
+ } catch (ConflictingModificationAppliedException e) {
+ LOG.warn("Store Tx: {} Conflicting modification for {}.", transaction.getIdentifier(),
+ e.getPath());
+ throw new OptimisticLockFailedException("Optimistic lock failed.",e);
+ } catch (DataValidationFailedException e) {
LOG.warn("Store Tx: {} Data Precondition failed for {}.", transaction.getIdentifier(),
e.getPath(), e);
- return false;
+ throw new TransactionCommitFailedException("Data did not pass validation.",e);
}
}
});
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl.tree;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+/**
+ * Exception thrown when a proposed change fails validation before being
+ * applied into the Data Tree because the Data Tree has been modified
+ * in way that a conflicting
+ * node is present.
+ */
+public class ConflictingModificationAppliedException extends DataValidationFailedException {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ public ConflictingModificationAppliedException(final InstanceIdentifier path, final String message, final Throwable cause) {
+ super(path, message, cause);
+ }
+
+ public ConflictingModificationAppliedException(final InstanceIdentifier path, final String message) {
+ super(path, message);
+ }
+
+}
/**
* Validate whether a particular modification can be applied to the data tree.
*/
- void validate(DataTreeModification modification) throws DataPreconditionFailedException;
+ void validate(DataTreeModification modification) throws DataValidationFailedException;
/**
* Prepare a modification for commit.
* the datastore has been concurrently modified such that a conflicting
* node is present, or the modification is structurally incorrect.
*/
-public class DataPreconditionFailedException extends Exception {
+public class DataValidationFailedException extends Exception {
private static final long serialVersionUID = 1L;
private final InstanceIdentifier path;
* @param path Object path which caused this exception
* @param message Specific message describing the failure
*/
- public DataPreconditionFailedException(final InstanceIdentifier path, final String message) {
+ public DataValidationFailedException(final InstanceIdentifier path, final String message) {
this(path, message, null);
}
/**
* @param message Specific message describing the failure
* @param cause Exception which triggered this failure, may be null
*/
- public DataPreconditionFailedException(final InstanceIdentifier path, final String message, final Throwable cause) {
+ public DataValidationFailedException(final InstanceIdentifier path, final String message, final Throwable cause) {
super(message, cause);
this.path = Preconditions.checkNotNull(path);
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl.tree;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+/**
+ * Exception thrown when a proposed change fails validation before being
+ * applied into the datastore because of incorrect structure of user supplied
+ * data.
+ *
+ */
+public class IncorrectDataStructureException extends DataValidationFailedException {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ public IncorrectDataStructureException(final InstanceIdentifier path, final String message, final Throwable cause) {
+ super(path, message, cause);
+ }
+
+ public IncorrectDataStructureException(final InstanceIdentifier path, final String message) {
+ super(path, message);
+ }
+
+}
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataValidationFailedException;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTree;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTreeCandidate;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTreeModification;
}
@Override
- public void validate(final DataTreeModification modification) throws DataPreconditionFailedException {
+ public void validate(final DataTreeModification modification) throws DataValidationFailedException {
Preconditions.checkArgument(modification instanceof InMemoryDataTreeModification, "Invalid modification class %s", modification.getClass());
final InMemoryDataTreeModification m = (InMemoryDataTreeModification)modification;
*/
package org.opendaylight.controller.md.sal.dom.store.impl.tree.data;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataValidationFailedException;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreTreeNode;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.TreeNode;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.Version;
* @param current Metadata Node to which modification should be applied
* @return true if modification is applicable
* false if modification is no applicable
- * @throws DataPreconditionFailedException
+ * @throws DataValidationFailedException
*/
- void checkApplicable(InstanceIdentifier path, NodeModification modification, Optional<TreeNode> current) throws DataPreconditionFailedException;
+ void checkApplicable(InstanceIdentifier path, NodeModification modification, Optional<TreeNode> current) throws DataValidationFailedException;
}
import java.util.Map;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataValidationFailedException;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.ModificationType;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.DataNodeContainerModificationStrategy.ListEntryModificationStrategy;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.ValueNodeModificationStrategy.LeafSetEntryModificationStrategy;
@Override
protected void checkWriteApplicable(final InstanceIdentifier path, final NodeModification modification,
- final Optional<TreeNode> current) throws DataPreconditionFailedException {
+ final Optional<TreeNode> current) throws DataValidationFailedException {
// FIXME: Implement proper write check for replacement of node container
// prerequisite is to have transaction chain available for clients
// otherwise this will break chained writes to same node.
@Override
protected void checkSubtreeModificationApplicable(final InstanceIdentifier path, final NodeModification modification,
- final Optional<TreeNode> current) throws DataPreconditionFailedException {
- checkDataPrecondition(path, current.isPresent(), "Node was deleted by other transaction.");
+ final Optional<TreeNode> current) throws DataValidationFailedException {
+ checkConflicting(path, current.isPresent(), "Node was deleted by other transaction.");
checkChildPreconditions(path, modification, current);
}
- private void checkChildPreconditions(final InstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataPreconditionFailedException {
+ private void checkChildPreconditions(final InstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataValidationFailedException {
final TreeNode currentMeta = current.get();
for (NodeModification childMod : modification.getChildren()) {
final PathArgument childId = childMod.getIdentifier();
@Override
protected void checkMergeApplicable(final InstanceIdentifier path, final NodeModification modification,
- final Optional<TreeNode> current) throws DataPreconditionFailedException {
+ final Optional<TreeNode> current) throws DataValidationFailedException {
if(current.isPresent()) {
checkChildPreconditions(path, modification,current);
}
import java.util.List;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ConflictingModificationAppliedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataValidationFailedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.IncorrectDataStructureException;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.ModificationType;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.DataNodeContainerModificationStrategy.ContainerModificationStrategy;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.DataNodeContainerModificationStrategy.UnkeyedListItemModificationStrategy;
return null;
}
- public static boolean checkDataPrecondition(final InstanceIdentifier path, final boolean condition, final String message) throws DataPreconditionFailedException {
+ public static boolean checkConflicting(final InstanceIdentifier path, final boolean condition, final String message) throws ConflictingModificationAppliedException {
if(!condition) {
- throw new DataPreconditionFailedException(path, message);
+ throw new ConflictingModificationAppliedException(path, message);
}
return condition;
}
}
}
- private static final void checkNotConflicting(final InstanceIdentifier path, final TreeNode original, final TreeNode current) throws DataPreconditionFailedException {
- checkDataPrecondition(path, original.getVersion().equals(current.getVersion()),
+ private static final void checkNotConflicting(final InstanceIdentifier path, final TreeNode original, final TreeNode current) throws ConflictingModificationAppliedException {
+ checkConflicting(path, original.getVersion().equals(current.getVersion()),
"Node was replaced by other transaction.");
- checkDataPrecondition(path, original.getSubtreeVersion().equals(current.getSubtreeVersion()),
+ checkConflicting(path, original.getSubtreeVersion().equals(current.getSubtreeVersion()),
"Node children was modified by other transaction");
}
}
@Override
- public final void checkApplicable(final InstanceIdentifier path,final NodeModification modification, final Optional<TreeNode> current) throws DataPreconditionFailedException {
+ public final void checkApplicable(final InstanceIdentifier path,final NodeModification modification, final Optional<TreeNode> current) throws DataValidationFailedException {
switch (modification.getType()) {
case DELETE:
checkDeleteApplicable(modification, current);
}
- protected void checkMergeApplicable(final InstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataPreconditionFailedException {
+ protected void checkMergeApplicable(final InstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataValidationFailedException {
Optional<TreeNode> original = modification.getOriginal();
if (original.isPresent() && current.isPresent()) {
/*
}
}
- protected void checkWriteApplicable(final InstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataPreconditionFailedException {
+ protected void checkWriteApplicable(final InstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataValidationFailedException {
Optional<TreeNode> original = modification.getOriginal();
if (original.isPresent() && current.isPresent()) {
checkNotConflicting(path, original.get(), current.get());
} else if(original.isPresent()) {
- throw new DataPreconditionFailedException(path,"Node was deleted by other transaction.");
+ throw new ConflictingModificationAppliedException(path,"Node was deleted by other transaction.");
}
}
protected abstract TreeNode applySubtreeChange(ModifiedNode modification,
TreeNode currentMeta, Version version);
+ /**
+ *
+ * Checks is supplied {@link NodeModification} is applicable for Subtree Modification.
+ *
+ * @param path Path to current node
+ * @param modification Node modification which should be applied.
+ * @param current Current state of data tree
+ * @throws ConflictingModificationAppliedException If subtree was changed in conflicting way
+ * @throws IncorrectDataStructureException If subtree modification is not applicable (e.g. leaf node).
+ */
protected abstract void checkSubtreeModificationApplicable(InstanceIdentifier path, final NodeModification modification,
- final Optional<TreeNode> current) throws DataPreconditionFailedException;
+ final Optional<TreeNode> current) throws DataValidationFailedException;
protected abstract void verifyWrittenStructure(NormalizedNode<?, ?> writtenValue);
@Override
protected void checkSubtreeModificationApplicable(final InstanceIdentifier path, final NodeModification modification,
- final Optional<TreeNode> current) throws DataPreconditionFailedException {
- throw new DataPreconditionFailedException(path, "Subtree modification is not allowed.");
+ final Optional<TreeNode> current) throws IncorrectDataStructureException {
+ throw new IncorrectDataStructureException(path, "Subtree modification is not allowed.");
}
}
}
import static com.google.common.base.Preconditions.checkArgument;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.IncorrectDataStructureException;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.TreeNode;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.TreeNodeFactory;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.Version;
@Override
protected void checkSubtreeModificationApplicable(final InstanceIdentifier path, final NodeModification modification,
- final Optional<TreeNode> current) throws DataPreconditionFailedException {
- throw new DataPreconditionFailedException(path, "Subtree modification is not allowed.");
+ final Optional<TreeNode> current) throws IncorrectDataStructureException {
+ throw new IncorrectDataStructureException(path, "Subtree modification is not allowed.");
}
public static class LeafSetEntryModificationStrategy extends ValueNodeModificationStrategy<LeafListSchemaNode> {