Bump yangtools to 13.0.0
[mdsal.git] / binding / mdsal-binding-dom-adapter / src / main / java / org / opendaylight / mdsal / binding / dom / adapter / FutureSchema.java
index 6c7c12b5feab884abf3465b6cf62b5dced75c709..fbe1d71f70b23168a5ae009665ea9367adc26782 100644 (file)
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
-
 package org.opendaylight.mdsal.binding.dom.adapter;
 
-import com.google.common.base.Predicate;
-import com.google.common.base.Throwables;
+import static java.util.Objects.requireNonNull;
+
 import com.google.common.util.concurrent.SettableFuture;
-import java.net.URI;
 import java.util.Collection;
-import java.util.Date;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
-import org.opendaylight.mdsal.binding.generator.util.BindingRuntimeContext;
+import java.util.function.Predicate;
+import org.checkerframework.checker.lock.qual.GuardedBy;
+import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext;
 import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+abstract class FutureSchema implements AutoCloseable {
+    private static final class Waiting extends FutureSchema {
+        Waiting(final long time, final TimeUnit unit) {
+            super(time, unit);
+        }
+    }
+
+    private static final class NonWaiting extends FutureSchema {
+        NonWaiting(final long time, final TimeUnit unit) {
+            super(time, unit);
+        }
+
+        @Override
+        boolean addPostponedOpAndWait(final FutureSchemaPredicate postponedOp) {
+            return false;
+        }
+    }
+
+    private abstract class FutureSchemaPredicate implements Predicate<BindingRuntimeContext> {
+        private final SettableFuture<Void> schemaPromise = SettableFuture.create();
+
+        final boolean waitForSchema() {
+            try {
+                schemaPromise.get(duration, unit);
+                return true;
+            } catch (final InterruptedException | ExecutionException e) {
+                throw new IllegalStateException(e);
+            } catch (final TimeoutException e) {
+                LOG.trace("Wait for {} timed out", schemaPromise, e);
+                return false;
+            } finally {
+                synchronized (postponedOperations) {
+                    postponedOperations.remove(this);
+                }
+            }
+        }
+
+        final void unlockIfPossible(final BindingRuntimeContext context) {
+            if (!schemaPromise.isDone() && test(context)) {
+                schemaPromise.set(null);
+            }
+        }
 
-class FutureSchema implements AutoCloseable {
+        final void cancel() {
+            schemaPromise.cancel(true);
+        }
+    }
 
-    private final List<FutureSchemaPredicate> postponedOperations = new CopyOnWriteArrayList<>();
-    private final SettableFuture<Void> schemaPromise = SettableFuture.create();
+    private static final Logger LOG = LoggerFactory.getLogger(FutureSchema.class);
+
+    @GuardedBy("postponedOperations")
+    private final Set<FutureSchemaPredicate> postponedOperations = new LinkedHashSet<>();
     private final long duration;
     private final TimeUnit unit;
 
-    protected FutureSchema(final long time, final TimeUnit unit) {
-        this.duration = time;
-        this.unit = unit;
+    private volatile BindingRuntimeContext runtimeContext;
+
+    FutureSchema(final long time, final TimeUnit unit) {
+        duration = time;
+        this.unit = requireNonNull(unit);
+    }
+
+    static FutureSchema create(final long time, final TimeUnit unit, final boolean waitEnabled) {
+        return waitEnabled ? new Waiting(time, unit) : new NonWaiting(time, unit);
+    }
+
+    BindingRuntimeContext runtimeContext() {
+        final BindingRuntimeContext localRuntimeContext = runtimeContext;
+        if (localRuntimeContext != null) {
+            return localRuntimeContext;
+        }
+
+        if (waitForSchema(Collections.emptyList())) {
+            return runtimeContext;
+        }
+
+        throw new IllegalStateException("No SchemaContext is available");
     }
 
     void onRuntimeContextUpdated(final BindingRuntimeContext context) {
-        for (final FutureSchemaPredicate op : postponedOperations) {
-            op.unlockIfPossible(context);
+        synchronized (postponedOperations) {
+            runtimeContext = context;
+            for (final FutureSchemaPredicate op : postponedOperations) {
+                op.unlockIfPossible(context);
+            }
         }
     }
 
@@ -50,72 +123,46 @@ class FutureSchema implements AutoCloseable {
 
     @Override
     public void close() {
-        for (final FutureSchemaPredicate op : postponedOperations) {
-            op.cancel();
-        }
-    }
-
-    private static boolean isSchemaAvailable(final Class<?> clz, final BindingRuntimeContext context) {
-        final Object schema;
-        if (Augmentation.class.isAssignableFrom(clz)) {
-            schema = context.getAugmentationDefinition(clz);
-        } else {
-            schema = context.getSchemaDefinition(clz);
+        synchronized (postponedOperations) {
+            postponedOperations.forEach(FutureSchemaPredicate::cancel);
         }
-        return schema != null;
     }
 
-    boolean waitForSchema(final URI namespace, final Date revision) {
-        final FutureSchemaPredicate postponedOp = new FutureSchemaPredicate() {
-
+    boolean waitForSchema(final QNameModule module) {
+        return addPostponedOpAndWait(new FutureSchemaPredicate() {
             @Override
-            public boolean apply(final BindingRuntimeContext input) {
-                return input.getSchemaContext().findModuleByNamespaceAndRevision(namespace, revision) != null;
+            public boolean test(final BindingRuntimeContext input) {
+                return input.modelContext().findModule(module).isPresent();
             }
-        };
-        return postponedOp.waitForSchema();
+        });
     }
 
     boolean waitForSchema(final Collection<Class<?>> bindingClasses) {
-        final FutureSchemaPredicate postponedOp = new FutureSchemaPredicate() {
-
+        return addPostponedOpAndWait(new FutureSchemaPredicate() {
             @Override
-            public boolean apply(final BindingRuntimeContext context) {
-                for (final Class<?> clz : bindingClasses) {
-                    if (!isSchemaAvailable(clz, context)) {
-                        return false;
+            public boolean test(final BindingRuntimeContext context) {
+                return bindingClasses.stream().allMatch(clz -> {
+                    if (Augmentation.class.isAssignableFrom(clz)) {
+                        return context.getAugmentationDefinition(clz.asSubclass(Augmentation.class)) != null;
                     }
-                }
-                return true;
+
+                    return context.getSchemaDefinition(clz) != null;
+                });
             }
-        };
-        return postponedOp.waitForSchema();
+        });
     }
 
-    private abstract class FutureSchemaPredicate implements Predicate<BindingRuntimeContext> {
-
-        final boolean waitForSchema() {
-            try {
-                schemaPromise.get(duration, unit);
-                return true;
-            } catch (final InterruptedException | ExecutionException e) {
-                throw Throwables.propagate(e);
-            } catch (final TimeoutException e) {
-                return false;
-            } finally {
-                postponedOperations.remove(this);
-            }
-        }
+    boolean addPostponedOpAndWait(final FutureSchemaPredicate postponedOp) {
+        synchronized (postponedOperations) {
+            postponedOperations.add(postponedOp);
 
-        final void unlockIfPossible(final BindingRuntimeContext context) {
-            if (!schemaPromise.isDone() && apply(context)) {
-                schemaPromise.set(null);
+            // If the runtimeContext changed, this op may now be satisfied so check it.
+            final BindingRuntimeContext context = runtimeContext;
+            if (context != null) {
+                postponedOp.unlockIfPossible(context);
             }
         }
 
-        final void cancel() {
-            schemaPromise.cancel(true);
-        }
+        return postponedOp.waitForSchema();
     }
-
 }