Use java.lang.ref.Cleaner for ClientBackedTransaction
[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.Collection;
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;
25
26 /**
27  * Abstract superclass of both ClientSnapshot and ClientTransaction. Provided for convenience.
28  *
29  * @author Robert Varga
30  */
31 @Beta
32 public abstract class AbstractClientHandle<T extends AbstractProxyTransaction> extends LocalAbortable
33         implements Identifiable<TransactionIdentifier> {
34     /*
35      * Our state consist of the the proxy map, hence we just subclass ConcurrentHashMap directly.
36      */
37     private static final class State<T> extends ConcurrentHashMap<Long, T> {
38         private static final long serialVersionUID = 1L;
39     }
40
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");
45
46     private final TransactionIdentifier transactionId;
47     private final AbstractClientHistory parent;
48
49     private volatile State<T> state = new State<>();
50
51     // Hidden to prevent outside instantiation
52     AbstractClientHandle(final AbstractClientHistory parent, final TransactionIdentifier transactionId) {
53         this.transactionId = requireNonNull(transactionId);
54         this.parent = requireNonNull(parent);
55     }
56
57     @Override
58     public TransactionIdentifier getIdentifier() {
59         return transactionId;
60     }
61
62     /**
63      * Release all state associated with this transaction.
64      *
65      * @return True if this transaction became closed during this call
66      */
67     public boolean abort() {
68         if (commonAbort()) {
69             parent.onTransactionAbort(this);
70             return true;
71         }
72
73         return false;
74     }
75
76     private boolean commonAbort() {
77         final Collection<T> toClose = ensureClosed();
78         if (toClose == null) {
79             return false;
80         }
81
82         toClose.forEach(AbstractProxyTransaction::abort);
83         return true;
84     }
85
86     @Override
87     final void localAbort(final Throwable cause) {
88         LOG.debug("Local abort of transaction {}", getIdentifier(), cause);
89         commonAbort();
90     }
91
92     /**
93      * Make sure this snapshot is closed. If it became closed as the effect of this call, return a collection of
94      * {@link AbstractProxyTransaction} handles which need to be closed, too.
95      *
96      * @return null if this snapshot has already been closed, otherwise a collection of proxies, which need to be
97      *         closed, too.
98      */
99     final @Nullable Collection<T> ensureClosed() {
100         // volatile read and a conditional CAS. This ends up being better in the typical case when we are invoked more
101         // than once (see ClientBackedTransaction) than performing a STATE_UPDATER.getAndSet().
102         final State<T> local = state;
103         return local != null && STATE_UPDATER.compareAndSet(this, local, null) ? local.values() : null;
104     }
105
106     final T ensureProxy(final YangInstanceIdentifier path) {
107         final State<T> local = getState();
108         final Long shard = parent.resolveShardForPath(path);
109
110         return local.computeIfAbsent(shard, this::createProxy);
111     }
112
113     final AbstractClientHistory parent() {
114         return parent;
115     }
116
117     abstract @NonNull T createProxy(@NonNull Long shard);
118
119     private State<T> getState() {
120         final State<T> local = state;
121         checkState(local != null, "Transaction %s is closed", transactionId);
122         return local;
123     }
124
125     @Override
126     public final String toString() {
127         return MoreObjects.toStringHelper(this).omitNullValues().add("identifier", transactionId).add("state", state)
128                 .toString();
129     }
130 }

©2013 OpenDaylight, A Linux Foundation Collaborative Project. All Rights Reserved.
OpenDaylight is a registered trademark of The OpenDaylight Project, Inc.
Linux Foundation and OpenDaylight are registered trademarks of the Linux Foundation.
Linux is a registered trademark of Linus Torvalds.