2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.cluster.databroker.actors.dds;
10 import static com.google.common.base.Preconditions.checkState;
11 import static java.util.Objects.requireNonNull;
13 import com.google.common.annotations.Beta;
14 import com.google.common.base.MoreObjects;
16 import java.util.concurrent.ConcurrentHashMap;
17 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.opendaylight.controller.cluster.access.concepts.TransactionIdentifier;
21 import org.opendaylight.yangtools.concepts.Identifiable;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
27 * Abstract superclass of both ClientSnapshot and ClientTransaction. Provided for convenience.
29 * @author Robert Varga
32 public abstract class AbstractClientHandle<T extends AbstractProxyTransaction> extends LocalAbortable
33 implements Identifiable<TransactionIdentifier> {
35 * Our state consist of the the proxy map, hence we just subclass ConcurrentHashMap directly.
37 private static final class State<T> extends ConcurrentHashMap<Long, T> {
38 private static final long serialVersionUID = 1L;
41 private static final Logger LOG = LoggerFactory.getLogger(AbstractClientHandle.class);
42 @SuppressWarnings("rawtypes")
43 private static final AtomicReferenceFieldUpdater<AbstractClientHandle, State> STATE_UPDATER =
44 AtomicReferenceFieldUpdater.newUpdater(AbstractClientHandle.class, State.class, "state");
46 private final @NonNull TransactionIdentifier transactionId;
47 private final @NonNull AbstractClientHistory parent;
49 private volatile State<T> state = new State<>();
51 // Hidden to prevent outside instantiation
52 AbstractClientHandle(final AbstractClientHistory parent, final TransactionIdentifier transactionId) {
53 this.transactionId = requireNonNull(transactionId);
54 this.parent = requireNonNull(parent);
58 // Non-final for mocking
59 public TransactionIdentifier getIdentifier() {
64 * Release all state associated with this transaction.
66 * @return True if this transaction became closed during this call
68 // Non-final for mocking
69 public boolean abort() {
71 parent.onTransactionAbort(this);
78 private boolean commonAbort() {
79 final Map<Long, T> toClose = ensureClosed();
80 if (toClose == null) {
84 toClose.values().forEach(AbstractProxyTransaction::abort);
85 parent.onTransactionShardsBound(transactionId, toClose.keySet());
90 final void localAbort(final Throwable cause) {
91 LOG.debug("Local abort of transaction {}", getIdentifier(), cause);
96 * Make sure this snapshot is closed. If it became closed as the effect of this call, return a collection of
97 * {@link AbstractProxyTransaction} handles which need to be closed, too.
99 * @return null if this snapshot has already been closed, otherwise a State with of proxies, which need to be
102 final @Nullable Map<Long, T> ensureClosed() {
103 // volatile read and a conditional CAS. This ends up being better in the typical case when we are invoked more
104 // than once (see ClientBackedTransaction) than performing a STATE_UPDATER.getAndSet().
105 final State<T> local = state;
106 return local != null && STATE_UPDATER.compareAndSet(this, local, null) ? local : null;
109 final T ensureProxy(final YangInstanceIdentifier path) {
110 final State<T> local = getState();
111 final Long shard = parent.resolveShardForPath(path);
113 return local.computeIfAbsent(shard, this::createProxy);
116 final AbstractClientHistory parent() {
120 abstract @NonNull T createProxy(@NonNull Long shard);
122 private State<T> getState() {
123 final State<T> local = state;
124 checkState(local != null, "Transaction %s is closed", transactionId);
129 public final String toString() {
130 return MoreObjects.toStringHelper(this).omitNullValues().add("identifier", transactionId).add("state", state)