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