BUG-5280: synchronize access to local histories
[controller.git] / opendaylight / md-sal / sal-distributed-datastore / src / main / java / org / opendaylight / controller / cluster / datastore / DataTreeCohortActorRegistry.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
9 package org.opendaylight.controller.cluster.datastore;
10
11 import akka.actor.ActorRef;
12 import akka.actor.PoisonPill;
13 import akka.actor.Status;
14 import akka.util.Timeout;
15 import com.google.common.base.Preconditions;
16 import java.util.ArrayList;
17 import java.util.Collection;
18 import java.util.HashMap;
19 import java.util.List;
20 import java.util.Map;
21 import javax.annotation.concurrent.NotThreadSafe;
22 import org.opendaylight.controller.cluster.access.concepts.TransactionIdentifier;
23 import org.opendaylight.controller.cluster.datastore.DataTreeCohortActor.CanCommit;
24 import org.opendaylight.controller.md.sal.dom.spi.AbstractRegistrationTree;
25 import org.opendaylight.controller.md.sal.dom.spi.RegistrationTreeNode;
26 import org.opendaylight.controller.md.sal.dom.spi.RegistrationTreeSnapshot;
27 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
28 import org.opendaylight.mdsal.dom.api.DOMDataTreeCandidate;
29 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
31 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
32 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
33 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
35 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 /**
40  * Registry of user commit cohorts, which is responsible for handling registration and calculation
41  * of affected cohorts based on {@link DataTreeCandidate}.
42  *
43  */
44 @NotThreadSafe
45 class DataTreeCohortActorRegistry extends AbstractRegistrationTree<ActorRef> {
46
47     private static final Logger LOG = LoggerFactory.getLogger(DataTreeCohortActorRegistry.class);
48
49     private final Map<ActorRef, RegistrationTreeNode<ActorRef>> cohortToNode = new HashMap<>();
50
51     @SuppressWarnings("checkstyle:IllegalCatch")
52     void registerCohort(final ActorRef sender, final RegisterCohort cohort) {
53         takeLock();
54         try {
55             final ActorRef cohortRef = cohort.getCohort();
56             final RegistrationTreeNode<ActorRef> node =
57                     findNodeFor(cohort.getPath().getRootIdentifier().getPathArguments());
58             addRegistration(node, cohort.getCohort());
59             cohortToNode.put(cohortRef, node);
60         } catch (final Exception e) {
61             sender.tell(new Status.Failure(e), ActorRef.noSender());
62             return;
63         } finally {
64             releaseLock();
65         }
66         sender.tell(new Status.Success(null), ActorRef.noSender());
67     }
68
69     void removeCommitCohort(final ActorRef sender, final RemoveCohort message) {
70         final ActorRef cohort = message.getCohort();
71         final RegistrationTreeNode<ActorRef> node = cohortToNode.get(cohort);
72         if (node != null) {
73             removeRegistration(node, cohort);
74             cohortToNode.remove(cohort);
75         }
76         sender.tell(new Status.Success(null), ActorRef.noSender());
77         cohort.tell(PoisonPill.getInstance(), cohort);
78     }
79
80     Collection<DataTreeCohortActor.CanCommit> createCanCommitMessages(final TransactionIdentifier txId,
81             final DataTreeCandidate candidate, final SchemaContext schema) {
82         try (RegistrationTreeSnapshot<ActorRef> cohorts = takeSnapshot()) {
83             return new CanCommitMessageBuilder(txId, candidate, schema).perform(cohorts.getRootNode());
84         }
85     }
86
87     void process(final ActorRef sender, final CohortRegistryCommand message) {
88         if (message instanceof RegisterCohort) {
89             registerCohort(sender, (RegisterCohort) message);
90         } else if (message instanceof RemoveCohort) {
91             removeCommitCohort(sender, (RemoveCohort) message);
92         }
93     }
94
95     abstract static class CohortRegistryCommand {
96
97         private final ActorRef cohort;
98
99         CohortRegistryCommand(final ActorRef cohort) {
100             this.cohort = Preconditions.checkNotNull(cohort);
101         }
102
103         ActorRef getCohort() {
104             return cohort;
105         }
106     }
107
108     static class RegisterCohort extends CohortRegistryCommand {
109
110         private final DOMDataTreeIdentifier path;
111
112         RegisterCohort(final DOMDataTreeIdentifier path, final ActorRef cohort) {
113             super(cohort);
114             this.path = path;
115
116         }
117
118         public DOMDataTreeIdentifier getPath() {
119             return path;
120         }
121
122     }
123
124     static class RemoveCohort extends CohortRegistryCommand {
125
126         RemoveCohort(final ActorRef cohort) {
127             super(cohort);
128         }
129
130     }
131
132     private static class CanCommitMessageBuilder {
133
134         private final TransactionIdentifier txId;
135         private final DataTreeCandidate candidate;
136         private final SchemaContext schema;
137         private final Collection<DataTreeCohortActor.CanCommit> messages =
138                 new ArrayList<>();
139
140         CanCommitMessageBuilder(final TransactionIdentifier txId, final DataTreeCandidate candidate,
141                 final SchemaContext schema) {
142             this.txId = Preconditions.checkNotNull(txId);
143             this.candidate = Preconditions.checkNotNull(candidate);
144             this.schema = schema;
145         }
146
147         private void lookupAndCreateCanCommits(final List<PathArgument> args, final int offset,
148                 final RegistrationTreeNode<ActorRef> node) {
149
150             if (args.size() != offset) {
151                 final PathArgument arg = args.get(offset);
152                 final RegistrationTreeNode<ActorRef> exactChild = node.getExactChild(arg);
153                 if (exactChild != null) {
154                     lookupAndCreateCanCommits(args, offset + 1, exactChild);
155                 }
156                 for (final RegistrationTreeNode<ActorRef> c : node.getInexactChildren(arg)) {
157                     lookupAndCreateCanCommits(args, offset + 1, c);
158                 }
159             } else {
160                 lookupAndCreateCanCommits(candidate.getRootPath(), node, candidate.getRootNode());
161             }
162         }
163
164         private void lookupAndCreateCanCommits(final YangInstanceIdentifier path,
165                 final RegistrationTreeNode<ActorRef> regNode, final DataTreeCandidateNode candNode) {
166             if (candNode.getModificationType() == ModificationType.UNMODIFIED) {
167                 LOG.debug("Skipping unmodified candidate {}", path);
168                 return;
169             }
170             final Collection<ActorRef> regs = regNode.getRegistrations();
171             if (!regs.isEmpty()) {
172                 createCanCommits(regs, path, candNode);
173             }
174
175             for (final DataTreeCandidateNode candChild : candNode.getChildNodes()) {
176                 if (candChild.getModificationType() != ModificationType.UNMODIFIED) {
177                     final RegistrationTreeNode<ActorRef> regChild =
178                             regNode.getExactChild(candChild.getIdentifier());
179                     if (regChild != null) {
180                         lookupAndCreateCanCommits(path.node(candChild.getIdentifier()), regChild, candChild);
181                     }
182
183                     for (final RegistrationTreeNode<ActorRef> rc : regNode
184                             .getInexactChildren(candChild.getIdentifier())) {
185                         lookupAndCreateCanCommits(path.node(candChild.getIdentifier()), rc, candChild);
186                     }
187                 }
188             }
189         }
190
191         private void createCanCommits(final Collection<ActorRef> regs, final YangInstanceIdentifier path,
192                 final DataTreeCandidateNode node) {
193             final DOMDataTreeCandidate domCandidate = DOMDataTreeCandidateTO.create(treeIdentifier(path), node);
194             for (final ActorRef reg : regs) {
195                 final CanCommit message = new DataTreeCohortActor.CanCommit(txId, domCandidate, schema, reg);
196                 messages.add(message);
197             }
198         }
199
200         private static DOMDataTreeIdentifier treeIdentifier(final YangInstanceIdentifier path) {
201             return new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, path);
202         }
203
204         private Collection<DataTreeCohortActor.CanCommit> perform(final RegistrationTreeNode<ActorRef> rootNode) {
205             final List<PathArgument> toLookup = candidate.getRootPath().getPathArguments();
206             lookupAndCreateCanCommits(toLookup, 0, rootNode);
207             return messages;
208         }
209     }
210
211     CompositeDataTreeCohort createCohort(final SchemaContext schemaContext, final TransactionIdentifier txId,
212             final Timeout commitStepTimeout) {
213         return new CompositeDataTreeCohort(this, txId, schemaContext, commitStepTimeout);
214     }
215 }