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