2 * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.clustering.it.provider.impl;
10 import static com.google.common.base.Preconditions.checkState;
11 import static org.opendaylight.controller.clustering.it.provider.impl.AbstractTransactionHandler.ITEM;
13 import com.google.common.util.concurrent.SettableFuture;
14 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
15 import java.util.HashMap;
16 import java.util.List;
17 import java.util.concurrent.Executors;
18 import java.util.concurrent.Future;
19 import java.util.concurrent.ScheduledExecutorService;
20 import java.util.concurrent.ScheduledFuture;
21 import java.util.concurrent.TimeUnit;
22 import java.util.concurrent.atomic.AtomicLong;
23 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
26 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
27 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
28 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
29 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidate;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
33 public final class IdIntsListener implements DOMDataTreeChangeListener {
34 private static final Logger LOG = LoggerFactory.getLogger(IdIntsListener.class);
35 private static final long SECOND_AS_NANO = 1000000000;
37 private final AtomicLong lastNotifTimestamp = new AtomicLong(0);
38 private ScheduledExecutorService executorService = null;
39 private ScheduledFuture<?> scheduledFuture = null;
41 private volatile NormalizedNode localCopy;
44 public void onInitialData() {
49 public void onDataTreeChanged(final List<DataTreeCandidate> changes) {
51 // There should only be one candidate reported
52 checkState(changes.size() == 1);
54 lastNotifTimestamp.set(System.nanoTime());
56 // do not log the change into debug, only use trace since it will lead to OOM on default heap settings
57 LOG.debug("Received data tree changed");
59 changes.forEach(change -> {
60 final var root = change.getRootNode();
61 final var after = root.dataAfter();
63 final var before = root.dataBefore();
64 LOG.trace("Received change, data before: {}, data after: {}", before != null ? before : "", after);
67 LOG.warn("getDataAfter() is missing from notification. change: {}", change);
72 public boolean hasTriggered() {
73 return localCopy != null;
76 public boolean checkEqual(final NormalizedNode expected) {
77 return localCopy.equals(expected);
80 @SuppressFBWarnings("BC_UNCONFIRMED_CAST")
81 public String diffWithLocalCopy(final NormalizedNode expected) {
82 return diffNodes((MapNode)expected, (MapNode)localCopy);
85 public Future<Void> tryFinishProcessing() {
86 executorService = Executors.newSingleThreadScheduledExecutor();
87 final var settableFuture = SettableFuture.<Void>create();
89 scheduledFuture = executorService.scheduleAtFixedRate(new CheckFinishedTask(settableFuture),
90 0, 1, TimeUnit.SECONDS);
91 return settableFuture;
94 public static String diffNodes(final MapNode expected, final MapNode actual) {
95 StringBuilder builder = new StringBuilder("MapNodes diff:");
97 final var itemNodeId = new NodeIdentifier(ITEM);
99 final var expIdIntMap = new HashMap<NodeIdentifierWithPredicates, MapEntryNode>();
100 expected.body().forEach(node -> expIdIntMap.put(node.name(), node));
102 actual.body().forEach(actIdInt -> {
103 final var expIdInt = expIdIntMap.remove(actIdInt.name());
104 if (expIdInt == null) {
105 builder.append('\n').append(" Unexpected id-int entry for ").append(actIdInt.name());
109 final var expItemMap = new HashMap<NodeIdentifierWithPredicates, MapEntryNode>();
110 ((MapNode)expIdInt.getChildByArg(itemNodeId)).body()
111 .forEach(node -> expItemMap.put(node.name(), node));
113 ((MapNode)actIdInt.getChildByArg(itemNodeId)).body().forEach(actItem -> {
114 final var expItem = expItemMap.remove(actItem.name());
115 if (expItem == null) {
116 builder.append('\n').append(" Unexpected item entry ").append(actItem.name())
117 .append(" for id-int entry ").append(actIdInt.name());
121 expItemMap.values().forEach(node -> builder.append('\n')
122 .append(" Actual is missing item entry ").append(node.name())
123 .append(" for id-int entry ").append(actIdInt.name()));
126 expIdIntMap.values().forEach(node -> builder.append('\n')
127 .append(" Actual is missing id-int entry for ").append(node.name()));
129 return builder.toString();
132 private final class CheckFinishedTask implements Runnable {
133 private final SettableFuture<Void> future;
135 CheckFinishedTask(final SettableFuture<Void> future) {
136 this.future = future;
141 if (System.nanoTime() - lastNotifTimestamp.get() > SECOND_AS_NANO * 4) {
142 scheduledFuture.cancel(false);
145 executorService.shutdown();