Report ExactDataObjectStep from DataObjectModification
[mdsal.git] / dom / mdsal-dom-spi / src / main / java / org / opendaylight / mdsal / dom / spi / AbstractDOMForwardedTransaction.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.mdsal.dom.spi;
9
10 import static java.util.Objects.requireNonNull;
11
12 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
13 import java.lang.invoke.MethodHandles;
14 import java.lang.invoke.VarHandle;
15 import java.util.Map;
16 import java.util.Map.Entry;
17 import java.util.function.Function;
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
21 import org.opendaylight.mdsal.common.api.TransactionDatastoreMismatchException;
22 import org.opendaylight.mdsal.dom.api.DOMDataTreeTransaction;
23 import org.opendaylight.mdsal.dom.spi.store.DOMStoreTransaction;
24
25 /**
26  * Composite DOM Transaction backed by {@link DOMStoreTransaction}.
27  *
28  * <p>
29  * Abstract base for composite transaction, which provides access only to common
30  * functionality as retrieval of subtransaction, close method and retrieval of
31  * identifier.
32  *
33  * @param <T> Subtransaction type
34  */
35 abstract class AbstractDOMForwardedTransaction<T extends DOMStoreTransaction>
36         implements DOMDataTreeTransaction {
37     private static final VarHandle BACKING_TX;
38
39     static {
40         try {
41             BACKING_TX = MethodHandles.lookup().findVarHandle(AbstractDOMForwardedTransaction.class,
42                 "backingTx", Entry.class);
43         } catch (NoSuchFieldException | IllegalAccessException e) {
44             throw new ExceptionInInitializerError(e);
45         }
46     }
47
48     private final @NonNull Object identifier;
49     private final Function<LogicalDatastoreType, T> backingTxFactory;
50
51     @SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD",
52         justification = "https://github.com/spotbugs/spotbugs/issues/2749")
53     private volatile Entry<LogicalDatastoreType, T> backingTx;
54
55     /**
56      * Creates new composite Transactions.
57      *
58      * @param identifier
59      *            Identifier of transaction.
60      * @param backingTxFactory
61      *            Function which supplies transaction depending on store type
62      */
63     protected AbstractDOMForwardedTransaction(final Object identifier,
64             final Function<LogicalDatastoreType, T> backingTxFactory) {
65         this.identifier = requireNonNull(identifier, "Identifier should not be null");
66         this.backingTxFactory = requireNonNull(backingTxFactory, "Backing transaction factory should not be null");
67     }
68
69     /**
70      * Returns subtransaction associated with supplied datastore type.
71      *
72      * <p>
73      * The method allows usage of single datastore type per transaction instance;
74      * eligible datastore type is defined by first method access.
75      *
76      * @param datastoreType is used to identify subtransaction object
77      * @return the subtransaction object
78      * @throws NullPointerException if datastoreType is {@code null}
79      * @throws IllegalArgumentException if datastoreType is not supported
80      * @throws TransactionDatastoreMismatchException if datastoreType mismatches the one used at first access
81      */
82     protected final @NonNull T getSubtransaction(final LogicalDatastoreType datastoreType) {
83         final var ds = requireNonNull(datastoreType, "datastoreType must not be null.");
84
85         var entry = backingTx;
86         if (entry == null) {
87             final var tx = backingTxFactory.apply(datastoreType);
88             final var newEntry = Map.entry(ds, tx);
89             final var witness = (Entry<LogicalDatastoreType, T>) BACKING_TX.compareAndExchange(this, null, newEntry);
90             if (witness != null) {
91                 tx.close();
92                 entry = witness;
93             } else {
94                 entry = newEntry;
95             }
96         }
97
98         final var encountered = entry.getKey();
99         if (encountered != ds) {
100             throw new TransactionDatastoreMismatchException(encountered, ds);
101         }
102         return entry.getValue();
103     }
104
105     /**
106      * Returns immutable Iterable of all subtransactions.
107      */
108     protected @Nullable T getSubtransaction() {
109         final Entry<LogicalDatastoreType, T> entry;
110         return (entry = backingTx) == null ? null : entry.getValue();
111     }
112
113     @Override
114     public Object getIdentifier() {
115         return identifier;
116     }
117
118     @SuppressWarnings("checkstyle:IllegalCatch")
119     protected void closeSubtransactions() {
120         /*
121          * We share one exception for all failures, which are added
122          * as supressedExceptions to it.
123          */
124         final var subtransaction = getSubtransaction();
125         if (subtransaction != null) {
126             try {
127                 subtransaction.close();
128             } catch (Exception e) {
129                 // If we did not allocate failure we allocate it
130                 throw new IllegalStateException("Uncaught exception occurred during closing transaction", e);
131             }
132         }
133     }
134 }