Add new DOMDataTreeCommitCohort#canCoomit method 60/52960/3
authorTom Pantelis <[email protected]>
Tue, 7 Mar 2017 14:57:54 +0000 (09:57 -0500)
committerRobert Varga <[email protected]>
Thu, 9 Mar 2017 13:30:10 +0000 (13:30 +0000)
If a cohort registers for yang list entries, there may be multiple
list entry modifications in a transaction thus the canCommit method
needs to take a Collection of DOMDataTreeCandidates for consistency.

For avoid breaking current implementations, I added the new method
with a default implementation that invokes the single DOMDataTreeCandidate
canCommit method with a warning that the new canCommit method should
be implemented. The single canCommit method is now deprecated with a
default implementation that logs an error and returns success.

Change-Id: If83d380a0ca8ba55be9e99b3d1da4d309e9a6487
Signed-off-by: Tom Pantelis <[email protected]>
common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/PostCanCommitStep.java
dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataTreeCommitCohort.java

index eac087d2cf6b18ae305279735313e73c7bb33da1..cba9dbf1aea2a8a51168c755767a6ec92e080284 100644 (file)
@@ -44,7 +44,7 @@ public interface PostCanCommitStep extends ThreePhaseCommitStep {
      * Successful future, returning {@link #NOOP} implementation of {@link PostCanCommitStep}s.
      *
      **/
-    CheckedFuture<? extends PostCanCommitStep, DataValidationFailedException> NOOP_SUCCESS_FUTURE =
+    CheckedFuture<PostCanCommitStep, DataValidationFailedException> NOOP_SUCCESS_FUTURE =
             Futures.immediateCheckedFuture(NOOP);
 
     /**
index 251061d9f63c2f66c777b845cda7c883439721bc..868586cdb94628ad1f6a28f777c0f770ea5dc41e 100644 (file)
@@ -8,11 +8,21 @@
 package org.opendaylight.mdsal.dom.api;
 
 import com.google.common.annotations.Beta;
+import com.google.common.collect.Iterables;
+import com.google.common.util.concurrent.AsyncFunction;
 import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 import javax.annotation.Nonnull;
 import org.opendaylight.mdsal.common.api.DataValidationFailedException;
 import org.opendaylight.mdsal.common.api.PostCanCommitStep;
+import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
+import org.opendaylight.yangtools.util.concurrent.MappingCheckedFuture;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.LoggerFactory;
 
 /**
  * Commit cohort participating in commit of data modification, which can validate data tree
@@ -23,10 +33,10 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
  * {@link DOMDataTreeCommitCohort}s are hooked up into commit of data tree changes and MAY
  * negatively affect performance of data broker / store.
  * Implementations of this interface are discouraged, unless you really need ability to veto data
- * tree changes, or to provide external state change in sync with visibility of commited data.
+ * tree changes, or to provide external state change in sync with visibility of committed data.
  *
  * <h2>Implementation requirements</h2>
- * <h3>Correctness assumptions</h3> Implementation SHOULD use only {@link DOMDataTreeCandidate} and
+ * <h3>Correctness assumptions</h3> Implementation SHOULD use only the {@link DOMDataTreeCandidate} instances and
  * provided {@link SchemaContext} for validation purposes.
  * Use of any other external mutable state is discouraged, implementation MUST NOT use any
  * transaction related APIs on same data broker / data store instance during invocation of
@@ -36,17 +46,17 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
  * and such calls may fail.
  * <h3>Correct model usage</h3> If implementation is performing YANG-model driven validation
  * implementation SHOULD use provided schema context.
- * Any other instance of {@link SchemaContext} obtained by other means, may not be valid for
- * associated DOMDataTreeCandidate and it may lead to incorrect validation or processing of provided
+ * Any other instance of {@link SchemaContext} obtained by other means, may not be valid for the
+ * associated DOMDataTreeCandidates and it may lead to incorrect validation or processing of provided
  * data.
- * <h3>DataTreeCandidate assumptions</h3> Implementation SHOULD NOT make any assumptions on
+ * <h3>DataTreeCandidate assumptions</h3> Implementation SHOULD NOT make any assumptions on a
  * {@link DOMDataTreeCandidate} being successfully committed until associated
  * {@link PostCanCommitStep#preCommit()} and
  * {@link org.opendaylight.mdsal.common.api.PostPreCommitStep#commit()} callback was invoked.
  * <h2>Usage patterns</h2>
  * <h3>Data Tree Validator</h3>
- * Validator is implementation, which only validates {@link DOMDataTreeCandidate} and does not
- * retain any state derived from edited data - does not care if {@link DOMDataTreeCandidate} was
+ * Validator is implementation, which only validates {@link DOMDataTreeCandidate} instances and does not
+ * retain any state derived from edited data - does not care if {@link DOMDataTreeCandidate} was
  * rejected afterwards or transaction was cancelled.
  * Implementation may opt-out from receiving {@code preCommit()}, {@code commit()}, {@code abort()}
  * callbacks by returning {@link PostCanCommitStep#NOOP}.
@@ -59,7 +69,25 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 public interface DOMDataTreeCommitCohort {
 
     /**
-     * Validates supplied data tree candidate and associates cohort-specific steps with data broker
+     * DO NOT implement or invoke this method. It is deprecated in favor of
+     * {@link #canCommit(Object, Collection, SchemaContext)} and only exists for backwards compatibility. The
+     * default implementation returns {@link PostCanCommitStep#NOOP_SUCCESS_FUTURE} and is invoked by the
+     * default implementation of {@link #canCommit(Object, Collection, SchemaContext)}.
+     *
+     * @deprecated Implement and invoke {@link #canCommit(Object, Collection, SchemaContext)} instead.
+     */
+    @Deprecated
+    @Nonnull
+    default CheckedFuture<PostCanCommitStep, DataValidationFailedException> canCommit(@Nonnull Object txId,
+            @Nonnull DOMDataTreeCandidate candidate, @Nonnull SchemaContext ctx) {
+        LoggerFactory.getLogger(getClass()).error(
+                "The default implementation of DOMDataTreeCommitCohort#canCommit(Object, DOMDataTreeCandidate, "
+                + "SchemaContext) was invoked on {}", getClass());
+        return PostCanCommitStep.NOOP_SUCCESS_FUTURE;
+    }
+
+    /**
+     * Validates supplied data tree candidates and associates cohort-specific steps with data broker
      * transaction.
      * If {@link DataValidationFailedException} is thrown by implementation, commit of supplied data
      * will be prevented, with the DataBroker transaction providing the thrown exception as the
@@ -79,16 +107,50 @@ public interface DOMDataTreeCommitCohort {
      *
      * @param txId Transaction identifier. SHOULD be used only for reporting and correlation.
      *        Implementation MUST NOT use {@code txId} for validation.
-     * @param candidate Data Tree candidate to be validated and committed.
+     * @param candidates Data Tree candidates to be validated and committed.
      * @param ctx Schema Context to which Data Tree candidate should conform.
-     * @return Checked future which will successfully complete with user-supplied implementation of
-     *         {@link PostCanCommitStep} if data are valid, or failed check future with
-     *         {@link DataValidationFailedException} if and only if provided
-     *         {@link DOMDataTreeCandidate} did not pass validation. Users are encouraged to use
+     * @return Checked future which will successfully complete with the user-supplied implementation of
+     *         {@link PostCanCommitStep} if all candidates are valid, or a failed checked future with a
+     *         {@link DataValidationFailedException} if and only if provided
+     *         {@link DOMDataTreeCandidate} instance did not pass validation. Users are encouraged to use
      *         more specific subclasses of this exception to provide additional information about
      *         validation failure reason.
      */
     @Nonnull
-    CheckedFuture<PostCanCommitStep, DataValidationFailedException> canCommit(@Nonnull Object txId,
-            @Nonnull DOMDataTreeCandidate candidate, @Nonnull SchemaContext ctx);
+    default CheckedFuture<PostCanCommitStep, DataValidationFailedException> canCommit(@Nonnull Object txId,
+            @Nonnull Collection<DOMDataTreeCandidate> candidates, @Nonnull SchemaContext ctx) {
+        LoggerFactory.getLogger(getClass()).warn("DOMDataTreeCommitCohort implementation {} should override "
+                + "canCommit(Object, Collection, SchemaContext)", getClass());
+
+        // For backwards compatibility, the default implementation is to invoke the deprecated
+        // canCommit(Object, DOMDataTreeCandidate, SchemaContext) method for each DOMDataTreeCandidate and return the
+        // last PostCanCommitStep.
+        List<CheckedFuture<PostCanCommitStep, DataValidationFailedException>> futures = new ArrayList<>();
+        for (DOMDataTreeCandidate candidate : candidates) {
+            futures.add(canCommit(txId, candidate, ctx));
+        }
+
+        final ListenableFuture<PostCanCommitStep> resultFuture = Futures.transform(Futures.allAsList(futures),
+            (AsyncFunction<List<PostCanCommitStep>, PostCanCommitStep>)input ->
+                Futures.immediateFuture(input.get(input.size() - 1)));
+        return MappingCheckedFuture.create(resultFuture, new DataValidationFailedExceptionMapper("canCommit",
+                Iterables.getLast(candidates).getRootPath()));
+    }
+
+    /**
+     * An ExceptionMapper that translates an Exception to a DataValidationFailedException.
+     */
+    class DataValidationFailedExceptionMapper extends ExceptionMapper<DataValidationFailedException> {
+        private final DOMDataTreeIdentifier failedTreeId;
+
+        public DataValidationFailedExceptionMapper(String opName, DOMDataTreeIdentifier failedTreeId) {
+            super(opName, DataValidationFailedException.class);
+            this.failedTreeId = failedTreeId;
+        }
+
+        @Override
+        protected DataValidationFailedException newWithCause(String message, Throwable cause) {
+            return new DataValidationFailedException(DOMDataTreeIdentifier.class, failedTreeId, message, cause);
+        }
+    }
 }