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