Fix wait for schema in BindingToNormalizedNodeCodec
[controller.git] / opendaylight / md-sal / sal-binding-broker / src / main / java / org / opendaylight / controller / md / sal / binding / impl / FutureSchema.java
1 /*
2  * Copyright (c) 2015 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.md.sal.binding.impl;
10
11 import com.google.common.base.Predicate;
12 import com.google.common.base.Throwables;
13 import com.google.common.util.concurrent.SettableFuture;
14 import java.net.URI;
15 import java.util.Collection;
16 import java.util.Collections;
17 import java.util.Date;
18 import java.util.LinkedHashSet;
19 import java.util.Set;
20 import java.util.concurrent.ExecutionException;
21 import java.util.concurrent.TimeUnit;
22 import java.util.concurrent.TimeoutException;
23 import javax.annotation.concurrent.GuardedBy;
24 import org.opendaylight.yangtools.sal.binding.generator.util.BindingRuntimeContext;
25 import org.opendaylight.yangtools.yang.binding.Augmentation;
26
27 class FutureSchema implements AutoCloseable {
28
29     @GuardedBy(value="postponedOperations")
30     private final Set<FutureSchemaPredicate> postponedOperations = new LinkedHashSet<>();
31     private final long duration;
32     private final TimeUnit unit;
33     private final boolean waitEnabled;
34     private volatile BindingRuntimeContext runtimeContext;
35
36     protected FutureSchema(final long time, final TimeUnit unit, final boolean waitEnabled) {
37         this.duration = time;
38         this.unit = unit;
39         this.waitEnabled = waitEnabled;
40     }
41
42     BindingRuntimeContext runtimeContext() {
43         BindingRuntimeContext localRuntimeContext = runtimeContext;
44         if(localRuntimeContext != null) {
45             return localRuntimeContext;
46         }
47
48         if(waitForSchema(Collections.emptyList())) {
49             return runtimeContext;
50         }
51
52         throw new IllegalStateException("No SchemaContext is available");
53     }
54
55     void onRuntimeContextUpdated(final BindingRuntimeContext context) {
56         synchronized(postponedOperations) {
57             runtimeContext = context;
58             for (final FutureSchemaPredicate op : postponedOperations) {
59                 op.unlockIfPossible(context);
60             }
61         }
62     }
63
64     long getDuration() {
65         return duration;
66     }
67
68     TimeUnit getUnit() {
69         return unit;
70     }
71
72     @Override
73     public void close() {
74         synchronized(postponedOperations) {
75             for (final FutureSchemaPredicate op : postponedOperations) {
76                 op.cancel();
77             }
78         }
79     }
80
81     private static boolean isSchemaAvailable(final Class<?> clz, final BindingRuntimeContext context) {
82         final Object schema;
83         if (Augmentation.class.isAssignableFrom(clz)) {
84             schema = context.getAugmentationDefinition(clz);
85         } else {
86             schema = context.getSchemaDefinition(clz);
87         }
88         return schema != null;
89     }
90
91     boolean waitForSchema(final URI namespace, final Date revision) {
92         return addPostponedOpAndWait(new FutureSchemaPredicate() {
93             @Override
94             public boolean apply(final BindingRuntimeContext input) {
95                 return input.getSchemaContext().findModuleByNamespaceAndRevision(namespace, revision) != null;
96             }
97         });
98     }
99
100     boolean waitForSchema(final Collection<Class<?>> bindingClasses) {
101         return addPostponedOpAndWait(new FutureSchemaPredicate() {
102             @Override
103             public boolean apply(final BindingRuntimeContext context) {
104                 for (final Class<?> clz : bindingClasses) {
105                     if (!isSchemaAvailable(clz, context)) {
106                         return false;
107                     }
108                 }
109                 return true;
110             }
111         });
112     }
113
114     private boolean addPostponedOpAndWait(FutureSchemaPredicate postponedOp) {
115         if(!waitEnabled) {
116             return false;
117         }
118
119         BindingRuntimeContext localRuntimeContext = runtimeContext;
120         synchronized(postponedOperations) {
121             postponedOperations.add(postponedOp);
122
123             // If the runtimeContext changed, this op may now be satisfied so check it.
124             if(localRuntimeContext != runtimeContext) {
125                 postponedOp.unlockIfPossible(runtimeContext);
126             }
127         }
128
129         return postponedOp.waitForSchema();
130     }
131
132     private abstract class FutureSchemaPredicate implements Predicate<BindingRuntimeContext> {
133
134         final boolean waitForSchema() {
135             try {
136                 schemaPromise.get(duration, unit);
137                 return true;
138             } catch (final InterruptedException | ExecutionException e) {
139                 throw Throwables.propagate(e);
140             } catch (final TimeoutException e) {
141                 return false;
142             } finally {
143                 synchronized(postponedOperations) {
144                     postponedOperations.remove(this);
145                 }
146             }
147         }
148
149         final void unlockIfPossible(final BindingRuntimeContext context) {
150             if (!schemaPromise.isDone() && apply(context)) {
151                 schemaPromise.set(null);
152             }
153         }
154
155         final void cancel() {
156             schemaPromise.cancel(true);
157         }
158
159         private final SettableFuture<?> schemaPromise = SettableFuture.create();
160     }
161
162 }