Fix wait for schema in BindingToNormalizedNodeCodec 42/62942/3
authorRobert Varga <robert.varga@pantheon.tech>
Sun, 10 Sep 2017 07:18:07 +0000 (09:18 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Tue, 12 Sep 2017 08:09:55 +0000 (10:09 +0200)
The wait for schema mechanism does not work. A few issues were fixed:

 - FutureSchema did not add the FutureSchemaPredicate instances to the
   postponedOperations list so they always timed out.

 - The waitForSchema method in BindingToNormalizedNodeCodec checked for
   !futureSchema.waitForSchema so it only successfully returned on
   failure.

 - There was a timing issue in FutureSchema where a context update could
   occur just prior to adding a postponed operation to the list, in
   which case the operation may timeout if the new update contained the
   postponed criteria and another update didn't occur in time. To
   alleviate this, the sychronization was tighted up and runtime context
   is now stored in FutureSchema instead of BindingToNormalizedNodeCodec.

 - The runtimeContext field needs to be volatile.

I also added a wait if the runtimeContext was null b/c
onGlobalContextUpdated hadn't been initially notified yet.

Added unit tests to cover the wait for schema mechanism.

Change-Id: I7155dae021453d085f89d13dfd4b069178dd2fc8
Signed-off-by: Tom Pantelis <tpanteli@brocade.com>
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingToNormalizedNodeCodec.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/FutureSchema.java
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/BindingNormalizedCodecTest.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/FutureSchemaTest.java
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/test/BindingNormalizedCodecTest.java [deleted file]

index d6fb13202e79e7f9e9af8e9be496b0edb793ea0a..5e5905ec47238d8db2403929d4555911775728bd 100644 (file)
@@ -83,8 +83,6 @@ public final class BindingToNormalizedNodeCodec implements BindingCodecTreeFacto
     private final ClassLoadingStrategy classLoadingStrategy;
     private final FutureSchema futureSchema;
 
-    private volatile BindingRuntimeContext runtimeContext;
-
     public BindingToNormalizedNodeCodec(final ClassLoadingStrategy classLoadingStrategy,
             final BindingNormalizedNodeCodecRegistry codecRegistry) {
         this(classLoadingStrategy, codecRegistry, false);
@@ -94,7 +92,7 @@ public final class BindingToNormalizedNodeCodec implements BindingCodecTreeFacto
             final BindingNormalizedNodeCodecRegistry codecRegistry, final boolean waitForSchema) {
         this.classLoadingStrategy = Preconditions.checkNotNull(classLoadingStrategy, "classLoadingStrategy");
         this.codecRegistry = Preconditions.checkNotNull(codecRegistry, "codecRegistry");
-        this.futureSchema = waitForSchema ? new FutureSchema(WAIT_DURATION_SEC, TimeUnit.SECONDS) : null;
+        this.futureSchema = FutureSchema.create(WAIT_DURATION_SEC, TimeUnit.SECONDS, waitForSchema);
     }
 
     YangInstanceIdentifier toYangInstanceIdentifierBlocking(final InstanceIdentifier<? extends DataObject> binding) {
@@ -217,13 +215,12 @@ public final class BindingToNormalizedNodeCodec implements BindingCodecTreeFacto
              * InstanceIdentifier has definition InstanceIdentifier<T extends DataObject>,
              * this means '?' is always  <? extends DataObject>. Eclipse compiler
              * is able to determine this relationship and treats
-             * Entry<InstanceIdentifier<?>,DataObject> and Entry<InstanceIdentifier<? extends DataObject,DataObject>
+             * Entry<InstanceIdentifier<?>, DataObject> and Entry<InstanceIdentifier<? extends DataObject, DataObject>
              * as assignable. However openjdk / oracle javac treats this two types
              * as incompatible and issues a compile error.
              *
              * <p>
-             * It is safe to  loose generic information and cast it to other generic signature.
-             *
+             * It is safe to lose generic information and cast it to other generic signature.
              */
             @SuppressWarnings("unchecked")
             final Entry<InstanceIdentifier<? extends DataObject>, DataObject> binding = Entry.class.cast(
@@ -236,11 +233,9 @@ public final class BindingToNormalizedNodeCodec implements BindingCodecTreeFacto
 
     @Override
     public void onGlobalContextUpdated(final SchemaContext context) {
-        runtimeContext = BindingRuntimeContext.create(classLoadingStrategy, context);
+        final BindingRuntimeContext runtimeContext = BindingRuntimeContext.create(classLoadingStrategy, context);
         codecRegistry.onBindingRuntimeContextUpdated(runtimeContext);
-        if (futureSchema != null) {
-            futureSchema.onRuntimeContextUpdated(runtimeContext);
-        }
+        futureSchema.onRuntimeContextUpdated(runtimeContext);
     }
 
     public <T extends DataObject> Function<Optional<NormalizedNode<?, ?>>, Optional<T>>
@@ -294,11 +289,11 @@ public final class BindingToNormalizedNodeCodec implements BindingCodecTreeFacto
         final QNameModule moduleName = BindingReflections.getQNameModule(modeledClass);
         final URI namespace = moduleName.getNamespace();
         final Date revision = moduleName.getRevision();
-        BindingRuntimeContext localRuntimeContext = runtimeContext;
+        BindingRuntimeContext localRuntimeContext = runtimeContext();
         Module module = localRuntimeContext == null ? null :
             localRuntimeContext.getSchemaContext().findModuleByNamespaceAndRevision(namespace, revision);
-        if (module == null && futureSchema != null && futureSchema.waitForSchema(namespace,revision)) {
-            localRuntimeContext = runtimeContext;
+        if (module == null && futureSchema.waitForSchema(namespace,revision)) {
+            localRuntimeContext = runtimeContext();
             Preconditions.checkState(localRuntimeContext != null, "BindingRuntimeContext is not available.");
             module = localRuntimeContext.getSchemaContext().findModuleByNamespaceAndRevision(namespace, revision);
         }
@@ -307,21 +302,18 @@ public final class BindingToNormalizedNodeCodec implements BindingCodecTreeFacto
     }
 
     private void waitForSchema(final Collection<Class<?>> binding, final MissingSchemaException exception) {
-        if (futureSchema != null) {
-            LOG.warn("Blocking thread to wait for schema convergence updates for {} {}",
-                    futureSchema.getDuration(), futureSchema.getUnit());
-            if (!futureSchema.waitForSchema(binding)) {
-                return;
-            }
+        LOG.warn("Blocking thread to wait for schema convergence updates for {} {}", futureSchema.getDuration(),
+            futureSchema.getUnit());
+        if (!futureSchema.waitForSchema(binding)) {
+            throw exception;
         }
-        throw exception;
     }
 
     private Method findRpcMethod(final Class<? extends RpcService> key, final RpcDefinition rpcDef)
             throws NoSuchMethodException {
         final String methodName = BindingMapping.getMethodName(rpcDef.getQName());
         if (rpcDef.getInput() != null && isExplicitStatement(rpcDef.getInput())) {
-            final Class<?> inputClz = runtimeContext.getClassForSchema(rpcDef.getInput());
+            final Class<?> inputClz = runtimeContext().getClassForSchema(rpcDef.getInput());
             return key.getMethod(methodName, inputClz);
         }
         return key.getMethod(methodName);
@@ -360,8 +352,8 @@ public final class BindingToNormalizedNodeCodec implements BindingCodecTreeFacto
     @SuppressWarnings("unchecked")
     public Set<Class<? extends Notification>> getNotificationClasses(final Set<SchemaPath> interested) {
         final Set<Class<? extends Notification>> result = new HashSet<>();
-        final Set<NotificationDefinition> knownNotifications = runtimeContext.getSchemaContext().getNotifications();
-        for (final NotificationDefinition notification : knownNotifications) {
+        final BindingRuntimeContext runtimeContext = runtimeContext();
+        for (final NotificationDefinition notification : runtimeContext.getSchemaContext().getNotifications()) {
             if (interested.contains(notification.getPath())) {
                 try {
                     result.add((Class<? extends Notification>) runtimeContext.getClassForSchema(notification));
@@ -374,12 +366,16 @@ public final class BindingToNormalizedNodeCodec implements BindingCodecTreeFacto
         return result;
     }
 
+    private BindingRuntimeContext runtimeContext() {
+        return futureSchema.runtimeContext();
+    }
+
     private static Collection<Class<?>> decompose(final InstanceIdentifier<?> path) {
         return ImmutableSet.copyOf(Iterators.transform(path.getPathArguments().iterator(), PathArgument::getType));
     }
 
     protected NormalizedNode<?, ?> instanceIdentifierToNode(final YangInstanceIdentifier parentPath) {
-        return ImmutableNodes.fromInstanceId(runtimeContext.getSchemaContext(), parentPath);
+        return ImmutableNodes.fromInstanceId(runtimeContext().getSchemaContext(), parentPath);
     }
 
     public NormalizedNode<?, ?> getDefaultNodeFor(final YangInstanceIdentifier parentMapPath) {
index 537ab1f72a0585c640ce912116eeb94a0e9e9c28..0dfa8419e1dadb1d683678fb37428b3d78a5f39e 100644 (file)
 
 package org.opendaylight.mdsal.binding.dom.adapter;
 
-import com.google.common.base.Predicate;
+import static java.util.Objects.requireNonNull;
+
 import com.google.common.util.concurrent.SettableFuture;
 import java.net.URI;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Date;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.LinkedHashSet;
+import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
+import java.util.function.Predicate;
+import javax.annotation.concurrent.GuardedBy;
 import org.opendaylight.mdsal.binding.generator.util.BindingRuntimeContext;
 import org.opendaylight.yangtools.yang.binding.Augmentation;
 
-class FutureSchema implements AutoCloseable {
+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(FutureSchema.this.duration, FutureSchema.this.unit);
+                return true;
+            } catch (final InterruptedException | ExecutionException e) {
+                throw new RuntimeException(e);
+            } catch (final TimeoutException e) {
+                return false;
+            } finally {
+                synchronized (FutureSchema.this.postponedOperations) {
+                    FutureSchema.this.postponedOperations.remove(this);
+                }
+            }
+        }
+
+        final void unlockIfPossible(final BindingRuntimeContext context) {
+            if (!schemaPromise.isDone() && test(context)) {
+                schemaPromise.set(null);
+            }
+        }
+
+        final void cancel() {
+            schemaPromise.cancel(true);
+        }
+    }
 
-    private final List<FutureSchemaPredicate> postponedOperations = new CopyOnWriteArrayList<>();
-    private final SettableFuture<Void> schemaPromise = SettableFuture.create();
+    @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) {
+    private volatile BindingRuntimeContext runtimeContext;
+
+    FutureSchema(final long time, final TimeUnit unit) {
         this.duration = time;
-        this.unit = unit;
+        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);
+            }
         }
     }
 
@@ -49,72 +120,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() {
-
+        return addPostponedOpAndWait(new FutureSchemaPredicate() {
             @Override
-            public boolean apply(final BindingRuntimeContext input) {
+            public boolean test(final BindingRuntimeContext input) {
                 return input.getSchemaContext().findModuleByNamespaceAndRevision(namespace, revision) != null;
             }
-        };
-        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) != null;
                     }
-                }
-                return true;
+
+                    return context.getSchemaDefinition(clz) != null;
+                });
             }
-        };
-        return postponedOp.waitForSchema();
+        });
     }
 
-    private abstract class FutureSchemaPredicate implements Predicate<BindingRuntimeContext> {
+    boolean addPostponedOpAndWait(final FutureSchemaPredicate postponedOp) {
+        synchronized (postponedOperations) {
+            postponedOperations.add(postponedOp);
 
-        final boolean waitForSchema() {
-            try {
-                schemaPromise.get(duration, unit);
-                return true;
-            } catch (final InterruptedException | ExecutionException e) {
-                throw new RuntimeException(e);
-            } catch (final TimeoutException e) {
-                return false;
-            } finally {
-                postponedOperations.remove(this);
+            // 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 unlockIfPossible(final BindingRuntimeContext context) {
-            if (!schemaPromise.isDone() && apply(context)) {
-                schemaPromise.set(null);
-            }
-        }
-
-        final void cancel() {
-            schemaPromise.cancel(true);
-        }
+        return postponedOp.waitForSchema();
     }
-
 }
diff --git a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/BindingNormalizedCodecTest.java b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/BindingNormalizedCodecTest.java
new file mode 100644 (file)
index 0000000..686f4bc
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.SetMultimap;
+import com.google.common.util.concurrent.Uninterruptibles;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import javassist.ClassPool;
+import org.junit.Test;
+import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractSchemaAwareTest;
+import org.opendaylight.mdsal.binding.dom.codec.gen.impl.DataObjectSerializerGenerator;
+import org.opendaylight.mdsal.binding.dom.codec.gen.impl.StreamWriterGenerator;
+import org.opendaylight.mdsal.binding.dom.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.mdsal.binding.generator.impl.GeneratedClassLoadingStrategy;
+import org.opendaylight.mdsal.binding.generator.util.JavassistUtils;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.bi.ba.rpcservice.rev140701.OpendaylightTestRpcServiceService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeLeafOnlyAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.AbstractSchemaContext;
+
+public class BindingNormalizedCodecTest extends AbstractSchemaAwareTest {
+
+    private static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo");
+    private static final InstanceIdentifier<TopLevelList> BA_TOP_LEVEL_LIST = InstanceIdentifier
+            .builder(Top.class).child(TopLevelList.class, TOP_FOO_KEY).build();
+    private static final InstanceIdentifier<TreeLeafOnlyAugment> BA_TREE_LEAF_ONLY = BA_TOP_LEVEL_LIST
+        .augmentation(TreeLeafOnlyAugment.class);
+    private static final InstanceIdentifier<TreeComplexUsesAugment> BA_TREE_COMPLEX_USES = BA_TOP_LEVEL_LIST
+        .augmentation(TreeComplexUsesAugment.class);
+    private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value");
+    private static final QName NAME_QNAME = QName.create(Top.QNAME, "name");
+    private static final YangInstanceIdentifier BI_TOP_LEVEL_LIST = YangInstanceIdentifier.builder().node(Top.QNAME)
+        .node(TopLevelList.QNAME).nodeWithKey(TopLevelList.QNAME, NAME_QNAME, TOP_FOO_KEY.getName()).build();
+
+    private BindingToNormalizedNodeCodec codec;
+    private SchemaContext context;
+
+    @Override
+    protected void setupWithSchema(final SchemaContext context) {
+        this.context = context;
+        final DataObjectSerializerGenerator streamWriter = StreamWriterGenerator.create(JavassistUtils.forClassPool(
+                    ClassPool.getDefault()));
+        final BindingNormalizedNodeCodecRegistry registry = new BindingNormalizedNodeCodecRegistry(streamWriter);
+        this.codec = new BindingToNormalizedNodeCodec(GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy(),
+                registry, true);
+    }
+
+    @Test
+    public void testComplexAugmentationSerialization() {
+        this.codec.onGlobalContextUpdated(this.context);
+        final PathArgument lastArg = this.codec.toYangInstanceIdentifier(BA_TREE_COMPLEX_USES).getLastPathArgument();
+        assertTrue(lastArg instanceof AugmentationIdentifier);
+    }
+
+
+    @Test
+    public void testLeafOnlyAugmentationSerialization() {
+        this.codec.onGlobalContextUpdated(this.context);
+        final PathArgument leafOnlyLastArg = this.codec.toYangInstanceIdentifier(BA_TREE_LEAF_ONLY)
+            .getLastPathArgument();
+        assertTrue(leafOnlyLastArg instanceof AugmentationIdentifier);
+        assertTrue(((AugmentationIdentifier) leafOnlyLastArg).getPossibleChildNames().contains(SIMPLE_VALUE_QNAME));
+    }
+
+    @Test
+    @SuppressWarnings("checkstyle:illegalCatch")
+    public void testToYangInstanceIdentifierBlocking() {
+        this.codec.onGlobalContextUpdated(new EmptySchemaContext());
+
+        final CountDownLatch done = new CountDownLatch(1);
+        final AtomicReference<YangInstanceIdentifier> yangId = new AtomicReference<>();
+        final AtomicReference<RuntimeException> error = new AtomicReference<>();
+        new Thread(() -> {
+            try {
+                yangId.set(BindingNormalizedCodecTest.this.codec.toYangInstanceIdentifierBlocking(BA_TOP_LEVEL_LIST));
+            } catch (final RuntimeException e) {
+                error.set(e);
+            } finally {
+                done.countDown();
+            }
+        }).start();
+
+        Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS);
+        this.codec.onGlobalContextUpdated(this.context);
+
+        assertTrue("toYangInstanceIdentifierBlocking completed",
+                Uninterruptibles.awaitUninterruptibly(done, 3, TimeUnit.SECONDS));
+        if (error.get() != null) {
+            throw error.get();
+        }
+
+        assertEquals("toYangInstanceIdentifierBlocking", BI_TOP_LEVEL_LIST, yangId.get());
+    }
+
+    @Test
+    public void testGetRpcMethodToSchemaPathWithNoInitialSchemaContext() {
+        testGetRpcMethodToSchemaPath();
+    }
+
+    @Test
+    public void testGetRpcMethodToSchemaPathBlocking() {
+        this.codec.onGlobalContextUpdated(new EmptySchemaContext());
+        testGetRpcMethodToSchemaPath();
+    }
+
+    @SuppressWarnings("checkstyle:illegalCatch")
+    private void testGetRpcMethodToSchemaPath() {
+        final CountDownLatch done = new CountDownLatch(1);
+        final AtomicReference<ImmutableBiMap<Method, SchemaPath>> retMap = new AtomicReference<>();
+        final AtomicReference<RuntimeException> error = new AtomicReference<>();
+        new Thread(() -> {
+            try {
+                retMap.set(BindingNormalizedCodecTest.this.codec.getRpcMethodToSchemaPath(
+                            OpendaylightTestRpcServiceService.class));
+            } catch (final RuntimeException e) {
+                error.set(e);
+            } finally {
+                done.countDown();
+            }
+        }).start();
+
+        Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS);
+        this.codec.onGlobalContextUpdated(this.context);
+
+        assertTrue("getRpcMethodToSchemaPath completed",
+                Uninterruptibles.awaitUninterruptibly(done, 3, TimeUnit.SECONDS));
+        if (error.get() != null) {
+            throw error.get();
+        }
+
+        for (final Method method : retMap.get().keySet()) {
+            if (method.getName().equals("rockTheHouse")) {
+                return;
+            }
+        }
+
+        fail("rockTheHouse RPC method not found");
+    }
+
+    static class EmptySchemaContext extends AbstractSchemaContext {
+        @Override
+        public Set<Module> getModules() {
+            return Collections.emptySet();
+        }
+
+        @Override
+        protected Map<ModuleIdentifier, String> getIdentifiersToSources() {
+            return Collections.emptyMap();
+        }
+
+        @Override
+        protected SetMultimap<URI, Module> getNamespaceToModules() {
+            return Multimaps.forMap(Collections.emptyMap());
+        }
+
+        @Override
+        protected SetMultimap<String, Module> getNameToModules() {
+            return Multimaps.forMap(Collections.emptyMap());
+        }
+    }
+}
index d688599692739e025e5e8ae0014f63d322ff2572..fa4574f2904fee5fe0f327c75fa1ebd10f2fe35d 100644 (file)
@@ -21,7 +21,7 @@ public class FutureSchemaTest {
 
     @Test
     public void basicTest() throws Exception {
-        final FutureSchema futureSchema = new FutureSchema(0, TimeUnit.MICROSECONDS);
+        final FutureSchema futureSchema = FutureSchema.create(0, TimeUnit.MICROSECONDS, true);
         assertNotNull(futureSchema);
         assertFalse(futureSchema.waitForSchema(new URI("test"), new Date(0)));
         assertFalse(futureSchema.waitForSchema(ImmutableSet.of()));
diff --git a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/test/BindingNormalizedCodecTest.java b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/test/BindingNormalizedCodecTest.java
deleted file mode 100644 (file)
index 3fd92be..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * 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.test;
-
-import static org.junit.Assert.assertTrue;
-
-import javassist.ClassPool;
-import org.junit.Test;
-import org.opendaylight.mdsal.binding.dom.adapter.BindingToNormalizedNodeCodec;
-import org.opendaylight.mdsal.binding.dom.codec.gen.impl.DataObjectSerializerGenerator;
-import org.opendaylight.mdsal.binding.dom.codec.gen.impl.StreamWriterGenerator;
-import org.opendaylight.mdsal.binding.dom.codec.impl.BindingNormalizedNodeCodecRegistry;
-import org.opendaylight.mdsal.binding.generator.impl.GeneratedClassLoadingStrategy;
-import org.opendaylight.mdsal.binding.generator.util.JavassistUtils;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexUsesAugment;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeLeafOnlyAugment;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.Top;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelList;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListKey;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-
-public class BindingNormalizedCodecTest extends AbstractSchemaAwareTest {
-
-    private static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo");
-    private static final InstanceIdentifier<TopLevelList> BA_TOP_LEVEL_LIST = InstanceIdentifier
-            .builder(Top.class).child(TopLevelList.class, TOP_FOO_KEY).toInstance();
-    private static final InstanceIdentifier<TreeLeafOnlyAugment> BA_TREE_LEAF_ONLY
-            = BA_TOP_LEVEL_LIST.augmentation(TreeLeafOnlyAugment.class);
-    private static final InstanceIdentifier<TreeComplexUsesAugment> BA_TREE_COMPLEX_USES
-            = BA_TOP_LEVEL_LIST.augmentation(TreeComplexUsesAugment.class);
-    private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value");
-
-
-    private BindingToNormalizedNodeCodec codec;
-
-    @Override
-    protected void setupWithSchema(final SchemaContext context) {
-        final DataObjectSerializerGenerator streamWriter
-                = StreamWriterGenerator.create(JavassistUtils.forClassPool(ClassPool.getDefault()));
-        final BindingNormalizedNodeCodecRegistry registry = new BindingNormalizedNodeCodecRegistry(streamWriter);
-        codec = new BindingToNormalizedNodeCodec(GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy(), registry);
-        codec.onGlobalContextUpdated(context);
-    }
-
-    @Test
-    public void testComplexAugmentationSerialization() {
-
-        final PathArgument lastArg = codec.toYangInstanceIdentifier(BA_TREE_COMPLEX_USES).getLastPathArgument();
-        assertTrue(lastArg instanceof AugmentationIdentifier);
-    }
-
-
-    @Test
-    public void testLeafOnlyAugmentationSerialization() {
-
-        final PathArgument leafOnlyLastArg = codec.toYangInstanceIdentifier(BA_TREE_LEAF_ONLY).getLastPathArgument();
-        assertTrue(leafOnlyLastArg instanceof AugmentationIdentifier);
-        assertTrue(((AugmentationIdentifier) leafOnlyLastArg).getPossibleChildNames().contains(SIMPLE_VALUE_QNAME));
-    }
-
-}