Merge "Initial code/design for an Akka Raft implementation"
[controller.git] / opendaylight / md-sal / sal-binding-broker / src / main / java / org / opendaylight / controller / md / sal / binding / impl / AbstractWriteTransaction.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.binding.impl;
9
10 import java.util.Collections;
11 import java.util.Map.Entry;
12
13 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
14 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
15 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
16 import org.opendaylight.yangtools.yang.binding.DataObject;
17 import org.opendaylight.yangtools.yang.binding.Identifiable;
18 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
19 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
20 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23
24 import com.google.common.base.Optional;
25 import com.google.common.collect.Iterables;
26 import com.google.common.util.concurrent.CheckedFuture;
27
28 /**
29  *
30  * Abstract Base Transaction for transactions which are backed by
31  * {@link DOMDataWriteTransaction}
32  */
33 public abstract class AbstractWriteTransaction<T extends DOMDataWriteTransaction> extends
34         AbstractForwardedTransaction<T> {
35
36     private static final Logger LOG = LoggerFactory.getLogger(AbstractWriteTransaction.class);
37
38     protected AbstractWriteTransaction(final T delegate,
39             final BindingToNormalizedNodeCodec codec) {
40         super(delegate, codec);
41     }
42
43
44     public final <U extends DataObject> void put(final LogicalDatastoreType store,
45             final InstanceIdentifier<U> path, final U data, final boolean createParents) {
46        final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = getCodec()
47                 .toNormalizedNode(path, data);
48         if(createParents) {
49             ensureParentsByMerge(store, normalized.getKey(), path);
50         } else {
51             ensureListParentIfNeeded(store,path,normalized);
52         }
53         getDelegate().put(store, normalized.getKey(), normalized.getValue());
54     }
55
56
57     public final <U extends DataObject> void merge(final LogicalDatastoreType store,
58             final InstanceIdentifier<U> path, final U data,final boolean createParents) {
59
60         final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = getCodec()
61                 .toNormalizedNode(path, data);
62
63         if(createParents) {
64             ensureParentsByMerge(store, normalized.getKey(), path);
65         } else {
66             ensureListParentIfNeeded(store,path,normalized);
67         }
68
69         getDelegate().merge(store, normalized.getKey(), normalized.getValue());
70     }
71
72
73     /**
74      *
75      * Ensures list parent if item is list, otherwise noop.
76      *
77      * <p>
78      * One of properties of binding specification is that it is imposible
79      * to represent list as a whole and thus it is impossible to write
80      * empty variation of MapNode without creating parent node, with
81      * empty list.
82      *
83      * <p>
84      * This actually makes writes such as
85      * <pre>
86      * put("Nodes", new NodesBuilder().build());
87      * put("Nodes/Node[key]", new NodeBuilder().setKey("key").build());
88      * </pre>
89      * To result in three DOM operations:
90      * <pre>
91      * put("/nodes",domNodes);
92      * merge("/nodes/node",domNodeList);
93      * put("/nodes/node/node[key]",domNode);
94      * </pre>
95      *
96      *
97      * In order to allow that to be inserted if necessary, if we know
98      * item is list item, we will try to merge empty MapNode or OrderedNodeMap
99      * to ensure list exists.
100      *
101      * @param store Data Store type
102      * @param path Path to data (Binding Aware)
103      * @param normalized Normalized version of data to be written
104      */
105     private void ensureListParentIfNeeded(final LogicalDatastoreType store, final InstanceIdentifier<?> path,
106             final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized) {
107         if(Identifiable.class.isAssignableFrom(path.getTargetType())) {
108             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier parentMapPath = getParent(normalized.getKey()).get();
109             NormalizedNode<?, ?> emptyParent = getCodec().getDefaultNodeFor(parentMapPath);
110             getDelegate().merge(store, parentMapPath, emptyParent);
111         }
112
113     }
114
115     // FIXME (should be probaly part of InstanceIdentifier)
116     protected static Optional<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier> getParent(
117             final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier child) {
118
119         Iterable<PathArgument> mapEntryItemPath = child.getPathArguments();
120         int parentPathSize = Iterables.size(mapEntryItemPath) - 1;
121         if(parentPathSize > 1) {
122             return Optional.of(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.create(Iterables.limit(mapEntryItemPath,  parentPathSize)));
123         } else if(parentPathSize == 0) {
124             return Optional.of(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.create(Collections.<PathArgument>emptyList()));
125         } else {
126             return Optional.absent();
127         }
128     }
129
130     /**
131      * Subclasses of this class are required to implement creation of parent
132      * nodes based on behaviour of their underlying transaction.
133      *
134      * @param store
135      * @param key
136      * @param path
137      */
138     protected abstract void ensureParentsByMerge(LogicalDatastoreType store,
139             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier key, InstanceIdentifier<?> path);
140
141     protected final void doDelete(final LogicalDatastoreType store,
142             final InstanceIdentifier<?> path) {
143         final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized = getCodec().toNormalized(path);
144         getDelegate().delete(store, normalized);
145     }
146
147     protected final CheckedFuture<Void,TransactionCommitFailedException> doSubmit() {
148         return getDelegate().submit();
149     }
150
151     protected final boolean doCancel() {
152         return getDelegate().cancel();
153     }
154
155 }