b87819c34337a0435b02a3f98209f73a29aef7fe
[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 com.google.common.annotations.Beta;
11 import com.google.common.base.Preconditions;
12 import java.util.Collection;
13 import java.util.Map;
14 import java.util.concurrent.ConcurrentHashMap;
15 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
16 import java.util.function.Function;
17 import javax.annotation.Nullable;
18 import org.opendaylight.controller.cluster.access.concepts.TransactionIdentifier;
19 import org.opendaylight.yangtools.concepts.Identifiable;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23
24 /**
25  * Abstract superclass of both ClientSnapshot and ClientTransaction. Provided for convenience.
26  *
27  * @author Robert Varga
28  */
29 @Beta
30 public abstract class AbstractClientHandle<T extends AbstractProxyTransaction> extends LocalAbortable
31         implements Identifiable<TransactionIdentifier> {
32     /*
33      * Our state consist of the the proxy map, hence we just subclass ConcurrentHashMap directly.
34      */
35     private static final class State<T> extends ConcurrentHashMap<Long, T> {
36         private static final long serialVersionUID = 1L;
37     }
38
39     private static final Logger LOG = LoggerFactory.getLogger(AbstractClientHandle.class);
40     @SuppressWarnings("rawtypes")
41     private static final AtomicReferenceFieldUpdater<AbstractClientHandle, State> STATE_UPDATER =
42             AtomicReferenceFieldUpdater.newUpdater(AbstractClientHandle.class, State.class, "state");
43
44     private final TransactionIdentifier transactionId;
45     private final AbstractClientHistory parent;
46
47     private volatile State<T> state = new State<>();
48
49     // Hidden to prevent outside instantiation
50     AbstractClientHandle(final AbstractClientHistory parent, final TransactionIdentifier transactionId) {
51         this.transactionId = Preconditions.checkNotNull(transactionId);
52         this.parent = Preconditions.checkNotNull(parent);
53     }
54
55     @Override
56     public final TransactionIdentifier getIdentifier() {
57         return transactionId;
58     }
59
60     /**
61      * Release all state associated with this transaction.
62      *
63      * @return True if this transaction became closed during this call
64      */
65     public final boolean abort() {
66         if (commonAbort()) {
67             parent.onTransactionAbort(this);
68             return true;
69         }
70
71         return false;
72     }
73
74     private boolean commonAbort() {
75         final Collection<T> toClose = ensureClosed();
76         if (toClose == null) {
77             return false;
78         }
79
80         toClose.forEach(AbstractProxyTransaction::abort);
81         return true;
82     }
83
84     @Override
85     final void localAbort(final Throwable cause) {
86         LOG.debug("Local abort of transaction {}", getIdentifier(), cause);
87         commonAbort();
88     }
89
90     /**
91      * Make sure this snapshot is closed. If it became closed as the effect of this call, return a collection of
92      * {@link AbstractProxyTransaction} handles which need to be closed, too.
93      *
94      * @return null if this snapshot has already been closed, otherwise a collection of proxies, which need to be
95      *         closed, too.
96      */
97     @Nullable final Collection<T> ensureClosed() {
98         @SuppressWarnings("unchecked")
99         final State<T> local = STATE_UPDATER.getAndSet(this, null);
100         return local == null ? null : local.values();
101     }
102
103     final T ensureProxy(final YangInstanceIdentifier path, final Function<Long, T> createProxy) {
104         final Map<Long, T> local = getState();
105         final Long shard = parent.resolveShardForPath(path);
106
107         return local.computeIfAbsent(shard, createProxy);
108     }
109
110     final AbstractClientHistory parent() {
111         return parent;
112     }
113
114     private State<T> getState() {
115         final State<T> local = state;
116         Preconditions.checkState(local != null, "Transaction %s is closed", transactionId);
117         return local;
118     }
119 }