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