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