Fix followerDistributedDataStore tear down
[controller.git] / opendaylight / md-sal / sal-distributed-datastore / src / main / java / org / opendaylight / controller / cluster / databroker / actors / dds / AbstractClientHandle.java
1 /*
2  * Copyright (c) 2016 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.actors.dds;
9
10 import static com.google.common.base.Preconditions.checkState;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.annotations.Beta;
14 import com.google.common.base.MoreObjects;
15 import java.util.Map;
16 import java.util.concurrent.ConcurrentHashMap;
17 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
18 import java.util.stream.Stream;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.opendaylight.controller.cluster.access.concepts.TransactionIdentifier;
22 import org.opendaylight.yangtools.concepts.Identifiable;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 /**
28  * Abstract superclass of both ClientSnapshot and ClientTransaction. Provided for convenience.
29  *
30  * @author Robert Varga
31  */
32 @Beta
33 public abstract class AbstractClientHandle<T extends AbstractProxyTransaction> extends LocalAbortable
34         implements Identifiable<TransactionIdentifier> {
35     /*
36      * Our state consist of the the proxy map, hence we just subclass ConcurrentHashMap directly.
37      */
38     private static final class State<T> extends ConcurrentHashMap<Long, T> {
39         private static final long serialVersionUID = 1L;
40     }
41
42     private static final Logger LOG = LoggerFactory.getLogger(AbstractClientHandle.class);
43     @SuppressWarnings("rawtypes")
44     private static final AtomicReferenceFieldUpdater<AbstractClientHandle, State> STATE_UPDATER =
45             AtomicReferenceFieldUpdater.newUpdater(AbstractClientHandle.class, State.class, "state");
46
47     private final @NonNull TransactionIdentifier transactionId;
48     private final @NonNull AbstractClientHistory parent;
49
50     private volatile State<T> state = new State<>();
51
52     // Hidden to prevent outside instantiation
53     AbstractClientHandle(final AbstractClientHistory parent, final TransactionIdentifier transactionId) {
54         this.transactionId = requireNonNull(transactionId);
55         this.parent = requireNonNull(parent);
56     }
57
58     @Override
59     // Non-final for mocking
60     public TransactionIdentifier getIdentifier() {
61         return transactionId;
62     }
63
64     /**
65      * Release all state associated with this transaction.
66      *
67      * @return True if this transaction became closed during this call
68      */
69     // Non-final for mocking
70     public boolean abort() {
71         if (commonAbort()) {
72             parent.onTransactionAbort(this);
73             return true;
74         }
75
76         return false;
77     }
78
79     private boolean commonAbort() {
80         final Map<Long, T> toClose = ensureClosed();
81         if (toClose == null) {
82             return false;
83         }
84
85         toClose.values().forEach(AbstractProxyTransaction::abort);
86         parent.onTransactionShardsBound(transactionId, toClose.keySet());
87         return true;
88     }
89
90     @Override
91     final void localAbort(final Throwable cause) {
92         LOG.debug("Local abort of transaction {}", getIdentifier(), cause);
93         commonAbort();
94     }
95
96     /**
97      * Make sure this snapshot is closed. If it became closed as the effect of this call, return a collection of
98      * {@link AbstractProxyTransaction} handles which need to be closed, too.
99      *
100      * @return null if this snapshot has already been closed, otherwise a State with of proxies, which need to be
101      *         closed, too.
102      */
103     final @Nullable Map<Long, T> ensureClosed() {
104         // volatile read and a conditional CAS. This ends up being better in the typical case when we are invoked more
105         // than once (see ClientBackedTransaction) than performing a STATE_UPDATER.getAndSet().
106         final State<T> local = state;
107         return local != null && STATE_UPDATER.compareAndSet(this, local, null) ? local : null;
108     }
109
110     final T ensureProxy(final YangInstanceIdentifier path) {
111         return ensureProxy(getState(), parent.resolveShardForPath(path));
112     }
113
114     private T ensureProxy(final State<T> localState, final Long shard) {
115         return localState.computeIfAbsent(shard, this::createProxy);
116     }
117
118     final Stream<T> ensureAllProxies() {
119         final var local = getState();
120         return parent.resolveAllShards().map(shard -> ensureProxy(local, shard));
121     }
122
123     final AbstractClientHistory parent() {
124         return parent;
125     }
126
127     abstract @NonNull T createProxy(@NonNull Long shard);
128
129     private State<T> getState() {
130         final State<T> local = state;
131         checkState(local != null, "Transaction %s is closed", transactionId);
132         return local;
133     }
134
135     @Override
136     public final String toString() {
137         return MoreObjects.toStringHelper(this).omitNullValues().add("identifier", transactionId).add("state", state)
138                 .toString();
139     }
140 }