Fix wait for schema in BindingToNormalizedNodeCodec
[controller.git] / opendaylight / md-sal / sal-binding-broker / src / main / java / org / opendaylight / controller / md / sal / binding / impl / BindingToNormalizedNodeCodec.java
index 66caaea7083af18f1ffaedc3113f4e35d167b4ff..ee60afbfd3d0ed12587752ed6b0d85c779f6c116 100644 (file)
  */
 package org.opendaylight.controller.md.sal.binding.impl;
 
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableBiMap;
 import java.lang.reflect.Method;
+import java.net.URI;
 import java.util.AbstractMap.SimpleEntry;
 import java.util.Collection;
+import java.util.Date;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
-
-import javax.annotation.Nullable;
-
+import java.util.concurrent.TimeUnit;
+import javax.annotation.Nonnull;
 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
-import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTree;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTreeFactory;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTreeNode;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.yangtools.binding.data.codec.impl.MissingSchemaException;
+import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy;
+import org.opendaylight.yangtools.sal.binding.generator.util.BindingRuntimeContext;
 import org.opendaylight.yangtools.yang.binding.BindingMapping;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.binding.RpcService;
 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
-import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
-import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
 import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
-import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
-import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
-import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-
-public class BindingToNormalizedNodeCodec implements SchemaContextListener {
+public final class BindingToNormalizedNodeCodec implements BindingCodecTreeFactory, BindingNormalizedNodeSerializer, SchemaContextListener, AutoCloseable {
 
+    private static final long WAIT_DURATION_SEC = 5;
     private static final Logger LOG = LoggerFactory.getLogger(BindingToNormalizedNodeCodec.class);
 
-    private final BindingIndependentMappingService bindingToLegacy;
-    private DataNormalizer legacyToNormalized;
+    private final BindingNormalizedNodeCodecRegistry codecRegistry;
 
-    public BindingToNormalizedNodeCodec(final BindingIndependentMappingService mappingService) {
-        super();
-        this.bindingToLegacy = mappingService;
-    }
+    private final GeneratedClassLoadingStrategy classLoadingStrategy;
+    private final FutureSchema futureSchema;
+    private final LoadingCache<InstanceIdentifier<?>, YangInstanceIdentifier> iiCache = CacheBuilder.newBuilder()
+            .softValues().build(new CacheLoader<InstanceIdentifier<?>, YangInstanceIdentifier>() {
 
-    public org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier toNormalized(
-            final InstanceIdentifier<? extends DataObject> binding) {
+                @Override
+                public YangInstanceIdentifier load(final InstanceIdentifier<?> key) throws Exception {
+                    return toYangInstanceIdentifierBlocking(key);
+                }
 
-        // Used instance-identifier codec do not support serialization of last
-        // path
-        // argument if it is Augmentation (behaviour expected by old datastore)
-        // in this case, we explicitly check if last argument is augmentation
-        // to process it separately
-        if (isAugmentationIdentifier(binding)) {
-            return toNormalizedAugmented(binding);
-        }
-        return toNormalizedImpl(binding);
-    }
+            });
+
+    private DataNormalizer legacyToNormalized;
 
-    public Entry<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
-            final InstanceIdentifier<? extends DataObject> bindingPath, final DataObject bindingObject) {
-        return toNormalizedNode(toBindingEntry(bindingPath, bindingObject));
+    public BindingToNormalizedNodeCodec(final GeneratedClassLoadingStrategy classLoadingStrategy,
+            final BindingNormalizedNodeCodecRegistry codecRegistry) {
+        this(classLoadingStrategy,codecRegistry,false);
 
     }
 
-    public Entry<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
-            final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> binding) {
-        Entry<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier, CompositeNode> legacyEntry = bindingToLegacy
-                .toDataDom(binding);
-        Entry<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier, NormalizedNode<?, ?>> normalizedEntry = legacyToNormalized
-                .toNormalized(legacyEntry);
-        LOG.trace("Serialization of {}, Legacy Representation: {}, Normalized Representation: {}", binding,
-                legacyEntry, normalizedEntry);
-        if (isAugmentation(binding.getKey().getTargetType())) {
-
-            for (DataContainerChild<? extends PathArgument, ?> child : ((DataContainerNode<?>) normalizedEntry
-                    .getValue()).getValue()) {
-                if (child instanceof AugmentationNode) {
-                    ImmutableList<PathArgument> childArgs = ImmutableList.<PathArgument> builder()
-                            .addAll(normalizedEntry.getKey().getPathArguments()).add(child.getIdentifier()).build();
-                    org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier childPath = org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier
-                            .create(childArgs);
-                    return toDOMEntry(childPath, child);
-                }
-            }
+    public BindingToNormalizedNodeCodec(final GeneratedClassLoadingStrategy classLoadingStrategy,
+            final BindingNormalizedNodeCodecRegistry codecRegistry,final boolean waitForSchema) {
+        this.classLoadingStrategy = Preconditions.checkNotNull(classLoadingStrategy,"classLoadingStrategy");
+        this.codecRegistry = Preconditions.checkNotNull(codecRegistry,"codecRegistry");
+        this.futureSchema = new FutureSchema(WAIT_DURATION_SEC, TimeUnit.SECONDS, waitForSchema);
+    }
 
+    YangInstanceIdentifier toYangInstanceIdentifierBlocking(final InstanceIdentifier<? extends DataObject> binding) {
+        try {
+            return codecRegistry.toYangInstanceIdentifier(binding);
+        } catch (final MissingSchemaException e) {
+            waitForSchema(decompose(binding),e);
+            return codecRegistry.toYangInstanceIdentifier(binding);
         }
-        return normalizedEntry;
-
     }
 
     /**
+     * Translates supplied Binding Instance Identifier into NormalizedNode
+     * instance identifier.
      *
-     * Returns a Binding-Aware instance identifier from normalized
-     * instance-identifier if it is possible to create representation.
-     *
-     * Returns Optional.absent for cases where target is mixin node except
-     * augmentation.
-     *
+     * @param binding
+     *            Binding Instance Identifier
+     * @return DOM Instance Identifier
+     * @throws IllegalArgumentException
+     *             If supplied Instance Identifier is not valid.
      */
-    public Optional<InstanceIdentifier<? extends DataObject>> toBinding(
-            final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized)
-                    throws DeserializationException {
-
-        PathArgument lastArgument = Iterables.getLast(normalized.getPathArguments());
-        // Used instance-identifier codec do not support serialization of last
-        // path
-        // argument if it is AugmentationIdentifier (behaviour expected by old
-        // datastore)
-        // in this case, we explicitly check if last argument is augmentation
-        // to process it separately
-        if (lastArgument instanceof AugmentationIdentifier) {
-            return toBindingAugmented(normalized);
-        }
-        return toBindingImpl(normalized);
+    public YangInstanceIdentifier toNormalized(final InstanceIdentifier<? extends DataObject> binding) {
+        return codecRegistry.toYangInstanceIdentifier(binding);
     }
 
-    private Optional<InstanceIdentifier<? extends DataObject>> toBindingAugmented(
-            final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized)
-                    throws DeserializationException {
-        Optional<InstanceIdentifier<? extends DataObject>> potential = toBindingImpl(normalized);
-        // Shorthand check, if codec already supports deserialization
-        // of AugmentationIdentifier we will return
-        if (potential.isPresent() && isAugmentationIdentifier(potential.get())) {
-            return potential;
-        }
-
-        int normalizedCount = getAugmentationCount(normalized);
-        AugmentationIdentifier lastArgument = (AugmentationIdentifier) Iterables.getLast(normalized.getPathArguments());
-
-        // Here we employ small trick - Binding-aware Codec injects an pointer
-        // to augmentation class
-        // if child is referenced - so we will reference child and then shorten
-        // path.
-        LOG.trace("Looking for candidates to match {}", normalized);
-        for (QName child : lastArgument.getPossibleChildNames()) {
-            org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier childPath = normalized.node(child);
-            try {
-                if (isNotRepresentable(childPath)) {
-                    LOG.trace("Path {} is not BI-representable, skipping it", childPath);
-                    continue;
-                }
-            } catch (DataNormalizationException e) {
-                LOG.warn("Failed to denormalize path {}, skipping it", childPath, e);
-                continue;
-            }
-
-            Optional<InstanceIdentifier<? extends DataObject>> baId = toBindingImpl(childPath);
-            if (!baId.isPresent()) {
-                LOG.debug("No binding-aware identifier found for path {}, skipping it", childPath);
-                continue;
-            }
-
-            InstanceIdentifier<? extends DataObject> potentialPath = shortenToLastAugment(baId.get());
-            int potentialAugmentCount = getAugmentationCount(potentialPath);
-            if (potentialAugmentCount == normalizedCount) {
-                LOG.trace("Found matching path {}", potentialPath);
-                return Optional.<InstanceIdentifier<? extends DataObject>> of(potentialPath);
-            }
-
-            LOG.trace("Skipping mis-matched potential path {}", potentialPath);
-        }
-
-        LOG.trace("Failed to find augmentation matching {}", normalized);
-        return Optional.absent();
+    @Override
+    public YangInstanceIdentifier toYangInstanceIdentifier(final InstanceIdentifier<?> binding) {
+        return codecRegistry.toYangInstanceIdentifier(binding);
     }
 
-    private Optional<InstanceIdentifier<? extends DataObject>> toBindingImpl(
-            final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized)
-                    throws DeserializationException {
-        org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier legacyPath;
 
-        try {
-            if (isNotRepresentable(normalized)) {
-                return Optional.absent();
-            }
-            legacyPath = legacyToNormalized.toLegacy(normalized);
-        } catch (DataNormalizationException e) {
-            throw new IllegalStateException("Could not denormalize path.", e);
-        }
-        LOG.trace("InstanceIdentifier Path Deserialization: Legacy representation {}, Normalized representation: {}",
-                legacyPath, normalized);
-        return Optional.<InstanceIdentifier<? extends DataObject>> of(bindingToLegacy.fromDataDom(legacyPath));
+    YangInstanceIdentifier toYangInstanceIdentifierCached(final InstanceIdentifier<?> binding) {
+        return iiCache .getUnchecked(binding);
     }
 
-    private boolean isNotRepresentable(final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized)
-            throws DataNormalizationException {
-        DataNormalizationOperation<?> op = findNormalizationOperation(normalized);
-        if (op.isMixin() && op.getIdentifier() instanceof NodeIdentifier) {
-            return true;
-        }
-        if (op.isLeaf()) {
-            return true;
-        }
-        return false;
+    @Override
+    public <T extends DataObject> Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
+            final InstanceIdentifier<T> path, final T data) {
+        return codecRegistry.toNormalizedNode(path, data);
     }
 
-    private DataNormalizationOperation<?> findNormalizationOperation(
-            final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized)
-                    throws DataNormalizationException {
-        DataNormalizationOperation<?> current = legacyToNormalized.getRootOperation();
-        for (PathArgument arg : normalized.getPathArguments()) {
-            current = current.getChild(arg);
-        }
-        return current;
-    }
-
-    private static final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> toBindingEntry(
-            final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject> key,
-            final DataObject value) {
-        return new SimpleEntry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject>(
-                key, value);
-    }
-
-    private static final Entry<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier, NormalizedNode<?, ?>> toDOMEntry(
-            final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier key, final NormalizedNode<?, ?> value) {
-        return new SimpleEntry<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier, NormalizedNode<?, ?>>(key,
-                value);
-    }
-
-    public DataObject toBinding(final InstanceIdentifier<?> path, final NormalizedNode<?, ?> normalizedNode)
-            throws DeserializationException {
-        CompositeNode legacy = null;
-        if (isAugmentationIdentifier(path) && normalizedNode instanceof AugmentationNode) {
-            QName augIdentifier = BindingReflections.findQName(path.getTargetType());
-            ContainerNode virtualNode = Builders.containerBuilder() //
-                    .withNodeIdentifier(new NodeIdentifier(augIdentifier)) //
-                    .withChild((DataContainerChild<?, ?>) normalizedNode) //
-                    .build();
-            legacy = (CompositeNode) DataNormalizer.toLegacy(virtualNode);
-        } else {
-            legacy = (CompositeNode) DataNormalizer.toLegacy(normalizedNode);
-        }
+    /**
+     * Converts Binding Map.Entry to DOM Map.Entry
+     *
+     * Same as {@link #toNormalizedNode(InstanceIdentifier, DataObject)}.
+     *
+     * @param binding Map Entry with InstanceIdentifier as key and DataObject as value.
+     * @return DOM Map Entry with {@link YangInstanceIdentifier} as key and {@link NormalizedNode}
+     *         as value.
+     */
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    public Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
+            final Entry<InstanceIdentifier<? extends DataObject>, DataObject> binding) {
+        return toNormalizedNode((InstanceIdentifier) binding.getKey(),binding.getValue());
+    }
 
-        return bindingToLegacy.dataObjectFromDataDom(path, legacy);
+    @Override
+    public Entry<InstanceIdentifier<?>, DataObject> fromNormalizedNode(final YangInstanceIdentifier path,
+            final NormalizedNode<?, ?> data) {
+        return codecRegistry.fromNormalizedNode(path, data);
     }
 
-    public DataNormalizer getDataNormalizer() {
-        return legacyToNormalized;
+    @Override
+    public Notification fromNormalizedNodeNotification(final SchemaPath path, final ContainerNode data) {
+        return codecRegistry.fromNormalizedNodeNotification(path, data);
     }
 
-    public Optional<Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject>> toBinding(
-            final Entry<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized)
-                    throws DeserializationException {
-        Optional<InstanceIdentifier<? extends DataObject>> potentialPath = toBinding(normalized.getKey());
-        if (potentialPath.isPresent()) {
-            InstanceIdentifier<? extends DataObject> bindingPath = potentialPath.get();
-            DataObject bindingData = toBinding(bindingPath, normalized.getValue());
-            if (bindingData == null) {
-                LOG.warn("Failed to deserialize {} to Binding format. Binding path is: {}", normalized, bindingPath);
-            }
-            return Optional.of(toBindingEntry(bindingPath, bindingData));
-        } else {
-            return Optional.absent();
-        }
+    @Override
+    public DataObject fromNormalizedNodeRpcData(final SchemaPath path, final ContainerNode data) {
+        return codecRegistry.fromNormalizedNodeRpcData(path, data);
     }
 
     @Override
-    public void onGlobalContextUpdated(final SchemaContext arg0) {
-        legacyToNormalized = new DataNormalizer(arg0);
+    public InstanceIdentifier<?> fromYangInstanceIdentifier(final YangInstanceIdentifier dom) {
+        return codecRegistry.fromYangInstanceIdentifier(dom);
     }
 
-    private org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier toNormalizedAugmented(
-            final InstanceIdentifier<?> augPath) {
-        org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier processed = toNormalizedImpl(augPath);
-        // If used instance identifier codec added supports for deserialization
-        // of last AugmentationIdentifier we will just reuse it
-        if (isAugmentationIdentifier(processed)) {
-            return processed;
-        }
-        Optional<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier> additionalSerialized;
-        additionalSerialized = toNormalizedAugmentedUsingChildContainers(augPath, processed);
+    @Override
+    public ContainerNode toNormalizedNodeNotification(final Notification data) {
+        return codecRegistry.toNormalizedNodeNotification(data);
+    }
 
-        if (additionalSerialized.isPresent()) {
-            return additionalSerialized.get();
-        }
-        additionalSerialized = toNormalizedAugmentedUsingChildLeafs(augPath, processed);
-        if (additionalSerialized.isPresent()) {
-            return additionalSerialized.get();
-        }
-        throw new IllegalStateException("Unabled to construct augmentation identfier for " + augPath);
+    @Override
+    public ContainerNode toNormalizedNodeRpcData(final DataContainer data) {
+        return codecRegistry.toNormalizedNodeRpcData(data);
     }
 
     /**
-     * Tries to find correct augmentation identifier using children leafs
      *
-     * This method uses normalized Instance Identifier of parent node to fetch
-     * schema and {@link BindingReflections#getModuleInfo(Class)} to learn about
-     * augmentation namespace, specificly, in which module it was defined.
-     *
-     * Then it uses it to filter all available augmentations for parent by
-     * module. After that it walks augmentations in particular module and
-     * pick-up first which at least one leaf name matches supplied augmentation.
-     * We could do this safely since YANG explicitly states that no any existing
-     * augmentations must differ in leaf fully qualified names.
+     * Returns a Binding-Aware instance identifier from normalized
+     * instance-identifier if it is possible to create representation.
      *
+     * Returns Optional.absent for cases where target is mixin node except
+     * augmentation.
      *
-     * @param augPath
-     *            Binding Aware Path which ends with augment
-     * @param parentPath
-     *            Processed path
-     * @return
      */
-    private Optional<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier> toNormalizedAugmentedUsingChildLeafs(
-            final InstanceIdentifier<?> augPath,
-            final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier parentPath) {
+    public Optional<InstanceIdentifier<? extends DataObject>> toBinding(final YangInstanceIdentifier normalized)
+                    throws DeserializationException {
         try {
-            DataNormalizationOperation<?> parentOp = legacyToNormalized.getOperation(parentPath);
-            if(!parentOp.getDataSchemaNode().isPresent()) {
-                return Optional.absent();
-            }
-            DataSchemaNode parentSchema = parentOp.getDataSchemaNode().get();
-            if (parentSchema instanceof AugmentationTarget) {
-                Set<AugmentationSchema> augmentations = ((AugmentationTarget) parentSchema).getAvailableAugmentations();
-                LOG.info("Augmentations for {}, {}", augPath, augmentations);
-                Optional<AugmentationSchema> schema = findAugmentation(augPath.getTargetType(), augmentations);
-                if (schema.isPresent()) {
-                    AugmentationIdentifier augmentationIdentifier = DataNormalizationOperation
-                            .augmentationIdentifierFrom(schema.get());
-                    return Optional.of(parentPath.node(augmentationIdentifier));
-                }
-            }
-        } catch (DataNormalizationException e) {
-            throw new IllegalArgumentException(e);
+            return Optional.<InstanceIdentifier<? extends DataObject>>fromNullable(codecRegistry.fromYangInstanceIdentifier(normalized));
+        } catch (final IllegalArgumentException e) {
+            return Optional.absent();
         }
-        return Optional.absent();
     }
 
-    /**
-     * Creates instance identifier for augmentation child, tries to serialize it
-     * Instance Identifier is then shortened to last augmentation.
-     *
-     * This is for situations, where underlying codec is implementing hydrogen
-     * style DOM APIs (which did not supported {@link AugmentationIdentifier}.)
-     *
-     * @param augPath
-     * @param parentPath
-     *            Path to parent node
-     * @return
-     */
-    @SuppressWarnings("rawtypes")
-    private Optional<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier> toNormalizedAugmentedUsingChildContainers(
-            final InstanceIdentifier<?> augPath,
-            final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier parentPath) {
-        for (Class augChild : BindingReflections.getChildrenClasses(augPath.getTargetType())) {
-            @SuppressWarnings("unchecked")
-            InstanceIdentifier<?> childPath = augPath.child(augChild);
-            org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized = toNormalizedImpl(childPath);
-            org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier potentialDiscovered = shortenToLastAugmentation(
-                    normalized, parentPath);
-            if (potentialDiscovered != null) {
-                return Optional.of(potentialDiscovered);
-            }
-        }
-        return Optional.absent();
+    public DataNormalizer getDataNormalizer() {
+        return legacyToNormalized;
     }
 
-    private Optional<AugmentationSchema> findAugmentation(final Class<?> targetType,
-            final Set<AugmentationSchema> augmentations) {
-        YangModuleInfo moduleInfo;
+    public Optional<Entry<InstanceIdentifier<? extends DataObject>, DataObject>> toBinding(
+            @Nonnull final Entry<YangInstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized)
+                    throws DeserializationException {
         try {
-            moduleInfo = BindingReflections.getModuleInfo(targetType);
-        } catch (Exception e) {
-            throw new IllegalStateException(e);
-        }
-        Iterable<AugmentationSchema> filtered = filteredByModuleInfo(augmentations,
-                BindingReflections.getModuleQName(moduleInfo).getModule());
-        filtered.toString();
-        Set<String> targetTypeGetters = getYangModeledGetters(targetType);
-        for (AugmentationSchema schema : filtered) {
-            for (DataSchemaNode child : schema.getChildNodes()) {
-                String getterName = "get" + BindingMapping.getClassName(child.getQName());
-                if (targetTypeGetters.contains(getterName)) {
-                    return Optional.of(schema);
-                }
-            }
+            /*
+             * This cast is required, due to generics behaviour in openjdk / oracle javac
+             *
+             * 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>
+             * as assignable. However openjdk / oracle javac treats this two types
+             * as incompatible and issues a compile error.
+             *
+             * It is safe to  loose generic information and cast it to other generic signature.
+             *
+             */
+            @SuppressWarnings("unchecked")
+            final Entry<InstanceIdentifier<? extends DataObject>, DataObject> binding = Entry.class.cast(codecRegistry.fromNormalizedNode(normalized.getKey(), normalized.getValue()));
+            return Optional.fromNullable(binding);
+        } catch (final IllegalArgumentException e) {
+            return Optional.absent();
         }
-        return Optional.absent();
     }
 
-    private static Iterable<AugmentationSchema> filteredByModuleInfo(final Iterable<AugmentationSchema> augmentations,
-            final QNameModule module) {
-        return Iterables.filter(augmentations, new Predicate<AugmentationSchema>() {
-            @Override
-            public boolean apply(final AugmentationSchema schema) {
-                final Collection<DataSchemaNode> childNodes = schema.getChildNodes();
-                return !childNodes.isEmpty() && module.equals(Iterables.get(childNodes, 0).getQName().getModule());
-            }
-        });
+    @Override
+    public void onGlobalContextUpdated(final SchemaContext schemaContext) {
+        legacyToNormalized = new DataNormalizer(schemaContext);
+        BindingRuntimeContext runtimeContext = BindingRuntimeContext.create(classLoadingStrategy, schemaContext);
+        codecRegistry.onBindingRuntimeContextUpdated(runtimeContext);
+        futureSchema.onRuntimeContextUpdated(runtimeContext);
     }
 
-    public static final Set<String> getYangModeledGetters(final Class<?> targetType) {
-        HashSet<String> ret = new HashSet<String>();
-        for (Method method : targetType.getMethods()) {
-            if (isYangModeledGetter(method)) {
-                ret.add(method.getName());
-            }
-        }
-        return ret;
+    public <T extends DataObject> Function<Optional<NormalizedNode<?, ?>>, Optional<T>>  deserializeFunction(final InstanceIdentifier<T> path) {
+        return codecRegistry.deserializeFunction(path);
     }
 
     /**
+     * Returns an default object according to YANG schema for supplied path.
      *
-     * Returns true if supplied method represent getter for YANG modeled value
-     *
-     * @param method
-     *            Method to be tested
-     * @return true if method represent getter for YANG Modeled value.
+     * @param path DOM Path
+     * @return Node with defaults set on.
      */
-    private static final boolean isYangModeledGetter(final Method method) {
-        return !method.getName().equals("getClass") && !method.getName().equals("getImplementedInterface")
-                && method.getName().startsWith("get") && method.getParameterTypes().length == 0;
-    }
-
-    private org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier shortenToLastAugmentation(
-            final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized,
-            final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier parentPath) {
-        int parentSize = Iterables.size(parentPath.getPathArguments());
-        int position = 0;
-        int foundPosition = -1;
-        for (PathArgument arg : normalized.getPathArguments()) {
-            position++;
-            if (arg instanceof AugmentationIdentifier) {
-                foundPosition = position;
-            }
-        }
-        if (foundPosition > 0 && foundPosition > parentSize) {
-            Iterable<PathArgument> shortened = Iterables.limit(normalized.getPathArguments(), foundPosition);
-            return org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.create(shortened);
-        }
-        return null;
-    }
-
-    private InstanceIdentifier<? extends DataObject> shortenToLastAugment(
-            final InstanceIdentifier<? extends DataObject> binding) {
-        int position = 0;
-        int foundPosition = -1;
-        for (org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : binding.getPathArguments()) {
-            position++;
-            if (isAugmentation(arg.getType())) {
-                foundPosition = position;
+    public NormalizedNode<?, ?> getDefaultNodeFor(final YangInstanceIdentifier path) {
+        final Iterator<PathArgument> iterator = path.getPathArguments().iterator();
+        DataNormalizationOperation<?> currentOp = legacyToNormalized.getRootOperation();
+        while (iterator.hasNext()) {
+            final PathArgument currentArg = iterator.next();
+            try {
+                currentOp = currentOp.getChild(currentArg);
+            } catch (final DataNormalizationException e) {
+                throw new IllegalArgumentException(String.format("Invalid child encountered in path %s", path), e);
             }
         }
-        return InstanceIdentifier.create(Iterables.limit(binding.getPathArguments(), foundPosition));
-    }
-
-    private org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier toNormalizedImpl(
-            final InstanceIdentifier<? extends DataObject> binding) {
-        final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier legacyPath = bindingToLegacy
-                .toDataDom(binding);
-        final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized = legacyToNormalized
-                .toNormalized(legacyPath);
-        return normalized;
+        return currentOp.createDefault(path.getLastPathArgument());
     }
 
-    private static boolean isAugmentation(final Class<? extends DataObject> type) {
-        return Augmentation.class.isAssignableFrom(type);
+    public BindingNormalizedNodeCodecRegistry getCodecRegistry() {
+        return codecRegistry;
     }
 
-    private static boolean isAugmentationIdentifier(final InstanceIdentifier<?> potential) {
-        return Augmentation.class.isAssignableFrom(potential.getTargetType());
+    @Override
+    public void close() {
+        // NOOP Intentionally
     }
 
-    private boolean isAugmentationIdentifier(final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier processed) {
-        return Iterables.getLast(processed.getPathArguments()) instanceof AugmentationIdentifier;
+    public BindingNormalizedNodeCodecRegistry getCodecFactory() {
+        return codecRegistry;
     }
 
-    private static int getAugmentationCount(final InstanceIdentifier<?> potential) {
-        int count = 0;
-        for (org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : potential.getPathArguments()) {
-            if (isAugmentation(arg.getType())) {
-                count++;
+    // FIXME: This should be probably part of Binding Runtime context
+    public ImmutableBiMap<Method, SchemaPath> getRpcMethodToSchemaPath(final Class<? extends RpcService> key) {
+        final Module module = getModuleBlocking(key);
+        final ImmutableBiMap.Builder<Method, SchemaPath> ret = ImmutableBiMap.<Method, SchemaPath>builder();
+        try {
+            for (final RpcDefinition rpcDef : module.getRpcs()) {
+                final Method method = findRpcMethod(key, rpcDef);
+                ret.put(method, rpcDef.getPath());
             }
-
+        } catch (final NoSuchMethodException e) {
+            throw new IllegalStateException("Rpc defined in model does not have representation in generated class.", e);
         }
-        return count;
+        return ret.build();
     }
 
-    private static int getAugmentationCount(final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier potential) {
-        int count = 0;
-        for (PathArgument arg : potential.getPathArguments()) {
-            if (arg instanceof AugmentationIdentifier) {
-                count++;
+    protected ImmutableBiMap<Method, RpcDefinition> getRpcMethodToSchema(final Class<? extends RpcService> key) {
+        final Module module = getModuleBlocking(key);
+        final ImmutableBiMap.Builder<Method, RpcDefinition> ret = ImmutableBiMap.builder();
+        try {
+            for (final RpcDefinition rpcDef : module.getRpcs()) {
+                final Method method = findRpcMethod(key, rpcDef);
+                ret.put(method, rpcDef);
             }
+        } catch (final NoSuchMethodException e) {
+            throw new IllegalStateException("Rpc defined in model does not have representation in generated class.", e);
         }
-        return count;
+        return ret.build();
     }
 
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    public <T extends DataObject> Function<Optional<NormalizedNode<?, ?>>, Optional<T>>  deserializeFunction(final InstanceIdentifier<T> path) {
-        return new DeserializeFunction(this, path);
+    private Module getModuleBlocking(final Class<?> modeledClass) {
+        final QNameModule moduleName = BindingReflections.getQNameModule(modeledClass);
+        final URI namespace = moduleName.getNamespace();
+        final Date revision = moduleName.getRevision();
+        Module module = runtimeContext().getSchemaContext().findModuleByNamespaceAndRevision(namespace, revision);
+        if(module == null && futureSchema.waitForSchema(namespace,revision)) {
+            module = runtimeContext().getSchemaContext().findModuleByNamespaceAndRevision(namespace, revision);
+        }
+        Preconditions.checkState(module != null, "Schema for %s is not available.", modeledClass);
+        return module;
     }
 
-    private static class DeserializeFunction<T extends DataObject> implements Function<Optional<NormalizedNode<?, ?>>, Optional<T>> {
+    private void waitForSchema(final Collection<Class<?>> binding, final MissingSchemaException e) {
+        LOG.warn("Blocking thread to wait for schema convergence updates for {} {}", futureSchema.getDuration(),
+                futureSchema.getUnit());
+        if(futureSchema.waitForSchema(binding)) {
+            return;
+        }
 
-        private final BindingToNormalizedNodeCodec codec;
-        private final InstanceIdentifier<?> path;
+        throw e;
+    }
 
-        public DeserializeFunction(final BindingToNormalizedNodeCodec codec, final InstanceIdentifier<?> path) {
-            super();
-            this.codec = Preconditions.checkNotNull(codec, "Codec must not be null");
-            this.path = Preconditions.checkNotNull(path, "Path must not be null");
+    private Method findRpcMethod(final Class<? extends RpcService> key, final RpcDefinition rpcDef) throws NoSuchMethodException {
+        final String methodName = BindingMapping.getMethodName(rpcDef.getQName());
+        if(rpcDef.getInput() != null) {
+            final Class<?> inputClz = runtimeContext().getClassForSchema(rpcDef.getInput());
+            return key.getMethod(methodName, inputClz);
         }
+        return key.getMethod(methodName);
+    }
 
-        @SuppressWarnings("rawtypes")
-        @Nullable
-        @Override
-        public Optional apply(@Nullable final Optional<NormalizedNode<?, ?>> normalizedNode) {
-            if (normalizedNode.isPresent()) {
-                final DataObject dataObject;
-                try {
-                    dataObject = codec.toBinding(path, normalizedNode.get());
-                } catch (DeserializationException e) {
-                    LOG.warn("Failed to create dataobject from node {}", normalizedNode.get(), e);
-                    throw new IllegalStateException("Failed to create dataobject", e);
-                }
+    private BindingRuntimeContext runtimeContext() {
+        return futureSchema.runtimeContext();
+    }
+
+    @Override
+    public BindingCodecTree create(final BindingRuntimeContext context) {
+        return codecRegistry.create(context);
+    }
 
-                if (dataObject != null) {
-                    return Optional.of(dataObject);
+    @Override
+    public BindingCodecTree create(final SchemaContext context, final Class<?>... bindingClasses) {
+        return codecRegistry.create(context, bindingClasses);
+    }
+
+    @Nonnull
+    protected Map.Entry<InstanceIdentifier<?>, BindingCodecTreeNode<?>> getSubtreeCodec(
+            final YangInstanceIdentifier domIdentifier) {
+
+        final BindingCodecTree currentCodecTree = codecRegistry.getCodecContext();
+        final InstanceIdentifier<?> bindingPath = codecRegistry.fromYangInstanceIdentifier(domIdentifier);
+        Preconditions.checkArgument(bindingPath != null);
+        /**
+         * If we are able to deserialize YANG instance identifier, getSubtreeCodec must
+         * return non-null value.
+         */
+        final BindingCodecTreeNode<?> codecContext = currentCodecTree.getSubtreeCodec(bindingPath);
+        return new SimpleEntry<InstanceIdentifier<?>, BindingCodecTreeNode<?>>(bindingPath, codecContext);
+    }
+
+    @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) {
+            if (interested.contains(notification.getPath())) {
+                try {
+                    result.add((Class<? extends Notification>) runtimeContext().getClassForSchema(notification));
+                } catch (final IllegalStateException e) {
+                    // Ignore
+                    LOG.warn("Class for {} is currently not known.",notification.getPath(),e);
                 }
             }
-            return Optional.absent();
         }
+        return result;
     }
 
-    /**
-     * Returns an default object according to YANG schema for supplied path.
-     *
-     * @param path DOM Path
-     * @return Node with defaults set on.
-     */
-    public NormalizedNode<?, ?> getDefaultNodeFor(final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier path) {
-        Iterator<PathArgument> iterator = path.getPathArguments().iterator();
-        DataNormalizationOperation<?> currentOp = legacyToNormalized.getRootOperation();
-        while (iterator.hasNext()) {
-            PathArgument currentArg = iterator.next();
-            try {
-                currentOp = currentOp.getChild(currentArg);
-            } catch (DataNormalizationException e) {
-                throw new IllegalArgumentException(String.format("Invalid child encountered in path %s", path), e);
-            }
+    private static Collection<Class<?>> decompose(final InstanceIdentifier<?> path) {
+        final Set<Class<?>> clazzes = new HashSet<>();
+        for(final InstanceIdentifier.PathArgument arg : path.getPathArguments()) {
+            clazzes.add(arg.getType());
         }
-        return currentOp.createDefault(path.getLastPathArgument());
+        return clazzes;
     }
+
 }