5f102047684f595a81adc12ddd18866f4fbcd9d7
[controller.git] / opendaylight / md-sal / sal-inmemory-datastore / src / main / java / org / opendaylight / controller / md / sal / dom / store / impl / SnapshotBackedWriteTransaction.java
1 /*
2  * Copyright (c) 2014 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.md.sal.dom.store.impl;
9
10 import static com.google.common.base.Preconditions.checkState;
11 import com.google.common.base.Objects.ToStringHelper;
12 import com.google.common.base.Optional;
13 import com.google.common.base.Preconditions;
14 import com.google.common.base.Throwables;
15 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
16 import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
17 import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
18 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
19 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
20 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
21 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24
25 /**
26  * Implementation of Write transaction which is backed by
27  * {@link DataTreeSnapshot} and executed according to
28  * {@link org.opendaylight.controller.md.sal.dom.store.impl.SnapshotBackedWriteTransaction.TransactionReadyPrototype}.
29  *
30  */
31 class SnapshotBackedWriteTransaction extends AbstractDOMStoreTransaction implements DOMStoreWriteTransaction {
32     private static final Logger LOG = LoggerFactory.getLogger(SnapshotBackedWriteTransaction.class);
33     private static final AtomicReferenceFieldUpdater<SnapshotBackedWriteTransaction, TransactionReadyPrototype> READY_UPDATER =
34             AtomicReferenceFieldUpdater.newUpdater(SnapshotBackedWriteTransaction.class, TransactionReadyPrototype.class, "readyImpl");
35     private static final AtomicReferenceFieldUpdater<SnapshotBackedWriteTransaction, DataTreeModification> TREE_UPDATER =
36             AtomicReferenceFieldUpdater.newUpdater(SnapshotBackedWriteTransaction.class, DataTreeModification.class, "mutableTree");
37
38     private volatile TransactionReadyPrototype readyImpl;        // non-null when not ready
39     private volatile DataTreeModification mutableTree;           // non-null when not committed/closed
40
41     /**
42      * Creates new write-only transaction.
43      *
44      * @param identifier
45      *            transaction Identifier
46      * @param snapshot
47      *            Snapshot which will be modified.
48      * @param readyImpl
49      *            Implementation of ready method.
50      */
51     public SnapshotBackedWriteTransaction(final Object identifier, final boolean debug,
52             final DataTreeSnapshot snapshot, final TransactionReadyPrototype readyImpl) {
53         super(identifier, debug);
54         this.readyImpl = Preconditions.checkNotNull(readyImpl, "readyImpl must not be null.");
55         mutableTree = snapshot.newModification();
56         LOG.debug("Write Tx: {} allocated with snapshot {}", identifier, snapshot);
57     }
58
59     @Override
60     public void write(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
61         checkNotReady();
62
63         final DataTreeModification tree = mutableTree;
64         LOG.debug("Tx: {} Write: {}:{}", getIdentifier(), path, data);
65
66         try {
67             tree.write(path, data);
68             // FIXME: Add checked exception
69         } catch (Exception e) {
70             LOG.error("Tx: {}, failed to write {}:{} in {}", getIdentifier(), path, data, tree, e);
71             // Rethrow original ones if they are subclasses of RuntimeException
72             // or Error
73             Throwables.propagateIfPossible(e);
74             // FIXME: Introduce proper checked exception
75             throw new IllegalArgumentException("Illegal input data.", e);
76         }
77     }
78
79     @Override
80     public void merge(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
81         checkNotReady();
82
83         final DataTreeModification tree = mutableTree;
84         LOG.debug("Tx: {} Merge: {}:{}", getIdentifier(), path, data);
85
86         try {
87             tree.merge(path, data);
88             // FIXME: Add checked exception
89         } catch (Exception e) {
90             LOG.error("Tx: {}, failed to write {}:{} in {}", getIdentifier(), path, data, tree, e);
91             // Rethrow original ones if they are subclasses of RuntimeException
92             // or Error
93             Throwables.propagateIfPossible(e);
94             // FIXME: Introduce proper checked exception
95             throw new IllegalArgumentException("Illegal input data.", e);
96         }
97     }
98
99     @Override
100     public void delete(final YangInstanceIdentifier path) {
101         checkNotReady();
102
103         final DataTreeModification tree = mutableTree;
104         LOG.debug("Tx: {} Delete: {}", getIdentifier(), path);
105
106         try {
107             tree.delete(path);
108             // FIXME: Add checked exception
109         } catch (Exception e) {
110             LOG.error("Tx: {}, failed to delete {} in {}", getIdentifier(), path, tree, e);
111             // Rethrow original ones if they are subclasses of RuntimeException
112             // or Error
113             Throwables.propagateIfPossible(e);
114             // FIXME: Introduce proper checked exception
115             throw new IllegalArgumentException("Illegal path to delete.", e);
116         }
117     }
118
119     /**
120      * Exposed for {@link SnapshotBackedReadWriteTransaction}'s sake only. The contract does
121      * not allow data access after the transaction has been closed or readied.
122      *
123      * @param path Path to read
124      * @return null if the the transaction has been closed;
125      */
126     protected final Optional<NormalizedNode<?, ?>> readSnapshotNode(final YangInstanceIdentifier path) {
127         return readyImpl == null ? null : mutableTree.readNode(path);
128     }
129
130     private final void checkNotReady() {
131         checkState(readyImpl != null, "Transaction %s is no longer open. No further modifications allowed.", getIdentifier());
132     }
133
134     @Override
135     public DOMStoreThreePhaseCommitCohort ready() {
136         final TransactionReadyPrototype wasReady = READY_UPDATER.getAndSet(this, null);
137         checkState(wasReady != null, "Transaction %s is no longer open", getIdentifier());
138
139         LOG.debug("Store transaction: {} : Ready", getIdentifier());
140         mutableTree.ready();
141         return wasReady.ready(this);
142     }
143
144     @Override
145     public void close() {
146         final TransactionReadyPrototype wasReady = READY_UPDATER.getAndSet(this, null);
147         if (wasReady != null) {
148             LOG.debug("Store transaction: {} : Closed", getIdentifier());
149             TREE_UPDATER.lazySet(this, null);
150         } else {
151             LOG.debug("Store transaction: {} : Closed after submit", getIdentifier());
152         }
153     }
154
155     @Override
156     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
157         return toStringHelper.add("ready", readyImpl == null);
158     }
159
160     // FIXME: used by chaining on, which really wants an mutated view with a precondition
161     final boolean isReady() {
162         return readyImpl == null;
163     }
164
165     protected DataTreeModification getMutatedView() {
166         return mutableTree;
167     }
168
169     /**
170      * Prototype implementation of
171      * {@link #ready(org.opendaylight.controller.md.sal.dom.store.impl.SnapshotBackedWriteTransaction)}
172      *
173      * This class is intended to be implemented by Transaction factories
174      * responsible for allocation of {@link org.opendaylight.controller.md.sal.dom.store.impl.SnapshotBackedWriteTransaction} and
175      * providing underlying logic for applying implementation.
176      *
177      */
178     // FIXME: needs access to local stuff, so make it an abstract class
179     public static interface TransactionReadyPrototype {
180         /**
181          * Returns a commit coordinator associated with supplied transactions.
182          *
183          * This call must not fail.
184          *
185          * @param tx
186          *            Transaction on which ready was invoked.
187          * @return DOMStoreThreePhaseCommitCohort associated with transaction
188          */
189         DOMStoreThreePhaseCommitCohort ready(SnapshotBackedWriteTransaction tx);
190     }
191 }