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