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