e0648186956d4901cc31ad49eb0182e7e822a7bc
[controller.git] / opendaylight / md-sal / sal-distributed-datastore / src / main / java / org / opendaylight / controller / cluster / databroker / AbstractDOMBrokerTransaction.java
1 /*
2  * Copyright (c) 2015 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.controller.cluster.databroker;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.annotations.VisibleForTesting;
14 import com.google.common.base.MoreObjects;
15 import com.google.common.base.MoreObjects.ToStringHelper;
16 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
17 import java.lang.invoke.MethodHandles;
18 import java.lang.invoke.VarHandle;
19 import java.util.Map;
20 import java.util.Map.Entry;
21 import org.eclipse.jdt.annotation.NonNull;
22 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
23 import org.opendaylight.mdsal.common.api.TransactionDatastoreMismatchException;
24 import org.opendaylight.mdsal.dom.api.DOMDataTreeTransaction;
25 import org.opendaylight.mdsal.dom.spi.store.DOMStoreTransaction;
26 import org.opendaylight.mdsal.dom.spi.store.DOMStoreTransactionFactory;
27
28 public abstract class AbstractDOMBrokerTransaction<T extends DOMStoreTransaction> implements DOMDataTreeTransaction {
29
30     private static final VarHandle BACKING_TX;
31
32     static {
33         try {
34             BACKING_TX = MethodHandles.lookup()
35                 .findVarHandle(AbstractDOMBrokerTransaction.class, "backingTx", Entry.class);
36         } catch (NoSuchFieldException | IllegalAccessException e) {
37             throw new ExceptionInInitializerError(e);
38         }
39     }
40
41     private final @NonNull Object identifier;
42     private final Map<LogicalDatastoreType, ? extends DOMStoreTransactionFactory> storeTxFactories;
43
44     @SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD",
45         justification = "https://github.com/spotbugs/spotbugs/issues/2749")
46     private volatile Entry<LogicalDatastoreType, T> backingTx;
47
48     /**
49      * Creates new transaction.
50      *
51      * @param identifier Identifier of transaction.
52      */
53     protected AbstractDOMBrokerTransaction(final Object identifier,
54             final Map<LogicalDatastoreType, ? extends DOMStoreTransactionFactory> storeTxFactories) {
55         this.identifier = requireNonNull(identifier, "Identifier should not be null");
56         this.storeTxFactories = requireNonNull(storeTxFactories, "Store Transaction Factories should not be null");
57         checkArgument(!storeTxFactories.isEmpty(), "Store Transaction Factories should not be empty");
58     }
59
60     /**
61      * Returns sub-transaction associated with supplied key.
62      *
63      * @param datastoreType the data store type
64      * @return the sub-transaction
65      * @throws NullPointerException                  if datastoreType is null
66      * @throws IllegalArgumentException              if no sub-transaction is associated with datastoreType.
67      * @throws TransactionDatastoreMismatchException if datastoreType mismatches the one used at first access
68      */
69     protected final T getSubtransaction(final LogicalDatastoreType datastoreType) {
70         requireNonNull(datastoreType, "datastoreType must not be null.");
71
72         var entry = backingTx;
73         if (entry == null) {
74             if (!storeTxFactories.containsKey(datastoreType)) {
75                 throw new IllegalArgumentException(datastoreType + " is not supported");
76             }
77             final var tx = createTransaction(datastoreType);
78             final var newEntry = Map.entry(datastoreType, tx);
79             final var witness = (Entry<LogicalDatastoreType, T>) BACKING_TX.compareAndExchange(this, null, newEntry);
80             if (witness != null) {
81                 tx.close();
82                 entry = witness;
83             } else {
84                 entry = newEntry;
85             }
86         }
87
88         final var expected = entry.getKey();
89         if (expected != datastoreType) {
90             throw new TransactionDatastoreMismatchException(expected, datastoreType);
91         }
92         return entry.getValue();
93     }
94
95     /**
96      * Returns sub-transaction if initialized.
97      */
98     @VisibleForTesting
99     protected T getSubtransaction() {
100         final Entry<LogicalDatastoreType, T> entry;
101         return (entry = backingTx) == null ? null : entry.getValue();
102     }
103
104     protected abstract T createTransaction(LogicalDatastoreType datastoreType);
105
106     @Override
107     public Object getIdentifier() {
108         return identifier;
109     }
110
111     @SuppressWarnings("checkstyle:IllegalCatch")
112     protected void closeSubtransaction() {
113         final var local = backingTx;
114         if (local != null) {
115             try {
116                 local.getValue().close();
117             } catch (Exception e) {
118                 throw new IllegalStateException("Uncaught exception occurred during closing transaction", e);
119             }
120         }
121     }
122
123     protected DOMStoreTransactionFactory getTxFactory(final LogicalDatastoreType type) {
124         return storeTxFactories.get(type);
125     }
126
127     @Override
128     public final String toString() {
129         return addToStringAttributes(MoreObjects.toStringHelper(this).omitNullValues()).toString();
130     }
131
132     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
133         return toStringHelper.add("identifier", identifier);
134     }
135 }