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