Fixup checkstyle
[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 package org.opendaylight.controller.cluster.datastore;
9
10 import static java.util.Objects.requireNonNull;
11
12 import akka.actor.ActorRef;
13 import akka.actor.PoisonPill;
14 import akka.actor.Status;
15 import akka.util.Timeout;
16 import com.google.common.collect.ArrayListMultimap;
17 import com.google.common.collect.Multimap;
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.concurrent.Executor;
24 import org.opendaylight.controller.cluster.access.concepts.TransactionIdentifier;
25 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
26 import org.opendaylight.mdsal.dom.api.DOMDataTreeCandidate;
27 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
28 import org.opendaylight.mdsal.dom.spi.AbstractRegistrationTree;
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.tree.api.DataTreeCandidate;
32 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidateNode;
33 import org.opendaylight.yangtools.yang.data.tree.api.ModificationType;
34 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
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}. This class is NOT thread-safe.
41  */
42 class DataTreeCohortActorRegistry extends AbstractRegistrationTree<ActorRef> {
43
44     private static final Logger LOG = LoggerFactory.getLogger(DataTreeCohortActorRegistry.class);
45
46     private final Map<ActorRef, Node<ActorRef>> cohortToNode = new HashMap<>();
47
48     Collection<ActorRef> getCohortActors() {
49         return new ArrayList<>(cohortToNode.keySet());
50     }
51
52     @SuppressWarnings("checkstyle:IllegalCatch")
53     void registerCohort(final ActorRef sender, final RegisterCohort cohort) {
54         takeLock();
55         try {
56             final ActorRef cohortRef = cohort.getCohort();
57             final Node<ActorRef> node = findNodeFor(cohort.getPath().path().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 Node<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     List<DataTreeCohortActor.CanCommit> createCanCommitMessages(final TransactionIdentifier txId,
81             final DataTreeCandidate candidate, final EffectiveModelContext schema) {
82         try (var 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         private final ActorRef cohort;
97
98         CohortRegistryCommand(final ActorRef cohort) {
99             this.cohort = requireNonNull(cohort);
100         }
101
102         ActorRef getCohort() {
103             return cohort;
104         }
105     }
106
107     static class RegisterCohort extends CohortRegistryCommand {
108         private final DOMDataTreeIdentifier path;
109
110         RegisterCohort(final DOMDataTreeIdentifier path, final ActorRef cohort) {
111             super(cohort);
112             this.path = path;
113         }
114
115         public DOMDataTreeIdentifier getPath() {
116             return path;
117         }
118     }
119
120     static class RemoveCohort extends CohortRegistryCommand {
121         RemoveCohort(final ActorRef cohort) {
122             super(cohort);
123         }
124     }
125
126     private static class CanCommitMessageBuilder {
127         private final Multimap<ActorRef, DOMDataTreeCandidate> actorToCandidates = ArrayListMultimap.create();
128         private final TransactionIdentifier txId;
129         private final DataTreeCandidate candidate;
130         private final EffectiveModelContext schema;
131
132         CanCommitMessageBuilder(final TransactionIdentifier txId, final DataTreeCandidate candidate,
133                 final EffectiveModelContext schema) {
134             this.txId = requireNonNull(txId);
135             this.candidate = requireNonNull(candidate);
136             this.schema = schema;
137         }
138
139         private void lookupAndCreateCanCommits(final List<PathArgument> args, final int offset,
140                 final Node<ActorRef> node) {
141
142             if (args.size() != offset) {
143                 final PathArgument arg = args.get(offset);
144                 final var exactChild = node.getExactChild(arg);
145                 if (exactChild != null) {
146                     lookupAndCreateCanCommits(args, offset + 1, exactChild);
147                 }
148                 for (var inexact : node.getInexactChildren(arg)) {
149                     lookupAndCreateCanCommits(args, offset + 1, inexact);
150                 }
151             } else {
152                 lookupAndCreateCanCommits(candidate.getRootPath(), node, candidate.getRootNode());
153             }
154         }
155
156         private void lookupAndCreateCanCommits(final YangInstanceIdentifier path, final Node<ActorRef> regNode,
157                 final DataTreeCandidateNode candNode) {
158             if (candNode.modificationType() == ModificationType.UNMODIFIED) {
159                 LOG.debug("Skipping unmodified candidate {}", path);
160                 return;
161             }
162             final var regs = regNode.getRegistrations();
163             if (!regs.isEmpty()) {
164                 createCanCommits(regs, path, candNode);
165             }
166
167             for (var candChild : candNode.childNodes()) {
168                 if (candChild.modificationType() != ModificationType.UNMODIFIED) {
169                     final var regChild = regNode.getExactChild(candChild.name());
170                     if (regChild != null) {
171                         lookupAndCreateCanCommits(path.node(candChild.name()), regChild, candChild);
172                     }
173
174                     for (var rc : regNode.getInexactChildren(candChild.name())) {
175                         lookupAndCreateCanCommits(path.node(candChild.name()), rc, candChild);
176                     }
177                 }
178             }
179         }
180
181         private void createCanCommits(final Collection<ActorRef> regs, final YangInstanceIdentifier path,
182                 final DataTreeCandidateNode node) {
183             final DOMDataTreeCandidate domCandidate = DOMDataTreeCandidateTO.create(treeIdentifier(path), node);
184             for (final ActorRef reg : regs) {
185                 actorToCandidates.put(reg, domCandidate);
186             }
187         }
188
189         private static DOMDataTreeIdentifier treeIdentifier(final YangInstanceIdentifier path) {
190             return DOMDataTreeIdentifier.of(LogicalDatastoreType.CONFIGURATION, path);
191         }
192
193         List<DataTreeCohortActor.CanCommit> perform(final Node<ActorRef> rootNode) {
194             final var toLookup = candidate.getRootPath().getPathArguments();
195             lookupAndCreateCanCommits(toLookup, 0, rootNode);
196
197             final Map<ActorRef, Collection<DOMDataTreeCandidate>> mapView = actorToCandidates.asMap();
198             final List<DataTreeCohortActor.CanCommit> messages = new ArrayList<>(mapView.size());
199             for (Map.Entry<ActorRef, Collection<DOMDataTreeCandidate>> entry: mapView.entrySet()) {
200                 messages.add(new DataTreeCohortActor.CanCommit(txId, entry.getValue(), schema, entry.getKey()));
201             }
202
203             return messages;
204         }
205     }
206
207     CompositeDataTreeCohort createCohort(final EffectiveModelContext schemaContext, final TransactionIdentifier txId,
208             final Executor callbackExecutor, final Timeout commitStepTimeout) {
209         return new CompositeDataTreeCohort(this, txId, schemaContext, callbackExecutor, commitStepTimeout);
210     }
211 }