455dbefb246e567d7293b7b7a7de21f11cb14ecd
[ovsdb.git] / utils / mdsal-utils / src / main / java / org / opendaylight / ovsdb / utils / mdsal / utils / NotifyingDataChangeListener.java
1 /*
2  * Copyright (c) 2016 Red Hat, 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.ovsdb.utils.mdsal.utils;
9
10 import java.util.Collection;
11 import java.util.HashSet;
12 import java.util.List;
13 import java.util.Set;
14 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
15 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
16 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
17 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
18 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
19 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
20 import org.opendaylight.yangtools.concepts.ListenerRegistration;
21 import org.opendaylight.yangtools.yang.binding.DataObject;
22 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26 /**
27  * This class provides methods for checking or waiting for various md-sal operations to complete.
28  * Once an instance is created one must invoke the registerDataChangeListener method
29  * with a DataBroker.
30  */
31 public class NotifyingDataChangeListener implements AutoCloseable, DataTreeChangeListener<DataObject> {
32     private static final Logger LOG = LoggerFactory.getLogger(NotifyingDataChangeListener.class);
33     private LogicalDatastoreType type;
34     private final Set<InstanceIdentifier<?>> createdIids = new HashSet<>();
35     private final Set<InstanceIdentifier<?>> removedIids = new HashSet<>();
36     private final Set<InstanceIdentifier<?>> updatedIids = new HashSet<>();
37     private InstanceIdentifier<?> iid;
38     private final static int RETRY_WAIT = 100;
39     private final static int MDSAL_TIMEOUT_OPERATIONAL = 10000;
40     private final static int MDSAL_TIMEOUT_CONFIG = 1000;
41     private ListenerRegistration<?> listenerRegistration;
42     private List<NotifyingDataChangeListener> waitList = null;
43     private int mdsalTimeout = MDSAL_TIMEOUT_OPERATIONAL;
44     private Boolean listen;
45     public static final int BIT_CREATE = 1;
46     public static final int BIT_UPDATE = 2;
47     public static final int BIT_DELETE = 4;
48     public static final int BIT_ALL = 7;
49     private int mask;
50
51     public NotifyingDataChangeListener(LogicalDatastoreType type, int mask,
52                                        InstanceIdentifier<?> iid, List<NotifyingDataChangeListener> waitList) {
53         this(type, iid, waitList);
54         this.mask = mask;
55     }
56
57     /**
58      * Create a new NotifyingDataChangeListener
59      * @param type DataStore type
60      * @param iid of the md-sal object we're waiting for
61      * @param waitList for tracking outstanding changes
62      */
63     public NotifyingDataChangeListener(LogicalDatastoreType type,
64                                         InstanceIdentifier<?> iid, List<NotifyingDataChangeListener> waitList) {
65         this.type = type;
66         this.iid = iid;
67         this.waitList = waitList;
68         if (this.waitList != null) {
69             this.waitList.add(this);
70         }
71
72         mdsalTimeout = MDSAL_TIMEOUT_OPERATIONAL;
73         if (type == LogicalDatastoreType.CONFIGURATION) {
74             mdsalTimeout = MDSAL_TIMEOUT_CONFIG;
75         }
76         listen = true;
77         mask = BIT_ALL;
78     }
79
80     /**
81      * Completely reset the state of this NotifyingDataChangeListener.
82      * @param type DataStore type
83      * @param iid of the md-sal object we're waiting for
84      * @throws Exception
85      */
86     public void modify(LogicalDatastoreType type, InstanceIdentifier<?> iid) throws Exception {
87         this.close();
88         this.clear();
89         this.type = type;
90         this.iid = iid;
91     }
92
93     public void setlisten(Boolean listen) {
94         this.listen = listen;
95     }
96
97     public void setMask(int mask) {
98         this.mask = mask;
99     }
100
101     @Override
102     public void onDataTreeChanged(Collection<DataTreeModification<DataObject>> changes) {
103         if (!listen) {
104             return;
105         }
106
107         for (DataTreeModification<DataObject> change: changes) {
108             DataObjectModification<DataObject> rootNode = change.getRootNode();
109             final InstanceIdentifier<DataObject> identifier = change.getRootPath().getRootIdentifier();
110             switch (rootNode.getModificationType()) {
111                 case SUBTREE_MODIFIED:
112                 case WRITE:
113                     if (rootNode.getDataBefore() == null) {
114                         if ((mask & BIT_CREATE) == BIT_CREATE) {
115                             LOG.info("{} DataTreeChanged: created {}", type, identifier);
116                             createdIids.add(identifier);
117                         }
118                     } else if ((mask & BIT_UPDATE) == BIT_UPDATE) {
119                         LOG.info("{} DataTreeChanged: updated {}", type, identifier);
120                         updatedIids.add(identifier);
121                     }
122                     break;
123                 case DELETE:
124                     if ((mask & BIT_DELETE) == BIT_DELETE) {
125                         LOG.info("{} DataTreeChanged: removed {}", type, identifier);
126                         removedIids.add(identifier);
127                     }
128                     break;
129                 default:
130                     break;
131             }
132         }
133
134         synchronized (this) {
135             notifyAll();
136         }
137     }
138
139     public boolean isCreated(InstanceIdentifier<?> iid) {
140         return createdIids.remove(iid);
141     }
142
143     public boolean isUpdated(InstanceIdentifier<?> iid) {
144         return updatedIids.remove(iid);
145     }
146
147     public boolean isRemoved(InstanceIdentifier<?> iid) {
148         return removedIids.remove(iid);
149     }
150
151     public void clear() {
152         createdIids.clear();
153         updatedIids.clear();
154         removedIids.clear();
155     }
156
157     @SuppressWarnings({ "rawtypes", "unchecked" })
158     public void registerDataChangeListener(DataBroker dataBroker) {
159         listenerRegistration = dataBroker.registerDataTreeChangeListener(new DataTreeIdentifier<>(type,
160                 (InstanceIdentifier)iid), this);
161     }
162
163     public void waitForCreation() throws InterruptedException {
164         waitForCreation(mdsalTimeout);
165     }
166
167     public void waitForCreation(long timeout) throws InterruptedException {
168         synchronized (this) {
169             long _start = System.currentTimeMillis();
170             LOG.info("Waiting for {} DataChanged creation on {}", type, iid);
171             while (!isCreated(iid) && System.currentTimeMillis() - _start < timeout) {
172                 wait(RETRY_WAIT);
173             }
174             LOG.info("Woke up, waited {}ms for creation of {}", System.currentTimeMillis() - _start, iid);
175         }
176     }
177
178     public void waitForUpdate() throws InterruptedException {
179         waitForUpdate(mdsalTimeout);
180     }
181
182     public void waitForUpdate(long timeout) throws InterruptedException {
183         synchronized (this) {
184             long _start = System.currentTimeMillis();
185             LOG.info("Waiting for {} DataChanged update on {}", type, iid);
186             while (!isUpdated(iid) && System.currentTimeMillis() - _start < timeout) {
187                 wait(RETRY_WAIT);
188             }
189             LOG.info("Woke up, waited {}ms for update of {}", System.currentTimeMillis() - _start, iid);
190         }
191     }
192
193     public void waitForDeletion() throws InterruptedException {
194         waitForDeletion(mdsalTimeout);
195     }
196
197     public void waitForDeletion(long timeout) throws InterruptedException {
198         synchronized (this) {
199             long _start = System.currentTimeMillis();
200             LOG.info("Waiting for {} DataChanged deletion on {}", type, iid);
201             while (!isRemoved(iid) && System.currentTimeMillis() - _start < timeout) {
202                 wait(RETRY_WAIT);
203             }
204             LOG.info("Woke up, waited {}ms for deletion of {}", System.currentTimeMillis() - _start, iid);
205         }
206     }
207
208     @Override
209     public void close() throws Exception {
210         if (listenerRegistration != null) {
211             try {
212                 listenerRegistration.close();
213             } catch (final Exception ex) {
214                 LOG.warn("Failed to close registration {}, iid {}", listenerRegistration, iid, ex);
215             }
216         }
217         if (waitList != null) {
218             waitList.remove(this);
219         }
220         listenerRegistration = null;
221     }
222 }