Merge "Breaking up 6360: Simple karaf distro"
authorTony Tkacik <ttkacik@cisco.com>
Mon, 5 May 2014 20:43:08 +0000 (20:43 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Mon, 5 May 2014 20:43:08 +0000 (20:43 +0000)
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingToNormalizedNodeCodec.java
opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/WriteParentListenAugmentTest.java [new file with mode: 0644]

index 35ebe76499f51e6bb5b629c4cf42a944fe727ea8..6329637dd01cfdad2470345e4c8433a07fe62ebf 100644 (file)
@@ -7,28 +7,43 @@
  */
 package org.opendaylight.controller.md.sal.binding.impl;
 
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
 import java.util.AbstractMap.SimpleEntry;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Map.Entry;
+import java.util.concurrent.Callable;
 
 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
+import org.opendaylight.yangtools.concepts.util.ClassLoaderUtils;
 import org.opendaylight.yangtools.yang.binding.Augmentation;
+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.InstanceIdentifier.Item;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.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.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 
 public class BindingToNormalizedNodeCodec implements SchemaContextListener {
 
@@ -44,10 +59,15 @@ public class BindingToNormalizedNodeCodec implements SchemaContextListener {
 
     public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalized(
             final InstanceIdentifier<? extends DataObject> binding) {
-        final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier legacyPath = bindingToLegacy.toDataDom(binding);
-        final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized = legacyToNormalized.toNormalized(legacyPath);
-        LOG.trace("InstanceIdentifier Path {} Serialization: Legacy representation {}, Normalized representation: {}",binding,legacyPath,normalized);
-        return normalized;
+
+        // 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);
     }
 
     public Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
@@ -58,39 +78,85 @@ public class BindingToNormalizedNodeCodec implements SchemaContextListener {
 
     public Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
             final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> binding) {
-        Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> legacyEntry = bindingToLegacy.toDataDom(binding);
-        Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalizedEntry = legacyToNormalized.toNormalized(legacyEntry);
-        LOG.trace("Serialization of {}, Legacy Representation: {}, Normalized Representation: {}",binding,legacyEntry,normalizedEntry);
-        if(Augmentation.class.isAssignableFrom(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().getPath())
-                           .add(child.getIdentifier())
-                           .build();
-                   org.opendaylight.yangtools.yang.data.api.InstanceIdentifier childPath = new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(childArgs);
-                   return new SimpleEntry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>>(childPath,child);
-               }
+        Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> legacyEntry = bindingToLegacy
+                .toDataDom(binding);
+        Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalizedEntry = legacyToNormalized
+                .toNormalized(legacyEntry);
+        LOG.trace("Serialization of {}, Legacy Representation: {}, Normalized Representation: {}", binding,
+                legacyEntry, normalizedEntry);
+        if (Augmentation.class.isAssignableFrom(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().getPath()).add(child.getIdentifier()).build();
+                    org.opendaylight.yangtools.yang.data.api.InstanceIdentifier childPath = new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(
+                            childArgs);
+                    return new SimpleEntry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>>(
+                            childPath, child);
+                }
             }
 
         }
         return normalizedEntry;
 
-
     }
 
     public InstanceIdentifier<? extends DataObject> toBinding(
             final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
             throws DeserializationException {
 
+        PathArgument lastArgument = Iterables.getLast(normalized.getPath());
+        // 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);
+    }
+
+    private InstanceIdentifier<? extends DataObject> toBindingAugmented(
+            final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized) throws DeserializationException {
+        InstanceIdentifier<? extends DataObject> potential = toBindingImpl(normalized);
+        // Shorthand check, if codec already supports deserialization
+        // of AugmentationIdentifier we will return
+        if(isAugmentationIdentifier(potential)) {
+            return potential;
+        }
+
+        AugmentationIdentifier lastArgument = (AugmentationIdentifier) Iterables.getLast(normalized.getPath());
+
+        // 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.
+        for (QName child : lastArgument.getPossibleChildNames()) {
+            org.opendaylight.yangtools.yang.data.api.InstanceIdentifier childPath = new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(
+                    ImmutableList.<PathArgument> builder()
+                    .addAll(normalized.getPath()).add(new NodeIdentifier(child)).build());
+            try {
+
+                InstanceIdentifier<? extends DataObject> potentialPath = shortenToLastAugment(toBindingImpl(childPath));
+                return potentialPath;
+            } catch (Exception e) {
+                LOG.trace("Unable to deserialize aug. child path for {}",childPath,e);
+            }
+        }
+        return toBindingImpl(normalized);
+    }
+
+    private InstanceIdentifier<? extends DataObject> toBindingImpl(
+            final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
+            throws DeserializationException {
         org.opendaylight.yangtools.yang.data.api.InstanceIdentifier legacyPath;
         try {
             legacyPath = legacyToNormalized.toLegacy(normalized);
         } catch (DataNormalizationException e) {
-            throw new IllegalStateException("Could not denormalize path.",e);
+            throw new IllegalStateException("Could not denormalize path.", e);
         }
-        LOG.trace("InstanceIdentifier Path Deserialization: Legacy representation {}, Normalized representation: {}",legacyPath,normalized);
+        LOG.trace("InstanceIdentifier Path Deserialization: Legacy representation {}, Normalized representation: {}",
+                legacyPath, normalized);
         return bindingToLegacy.fromDataDom(legacyPath);
     }
 
@@ -103,7 +169,19 @@ public class BindingToNormalizedNodeCodec implements SchemaContextListener {
 
     public DataObject toBinding(final InstanceIdentifier<?> path, final NormalizedNode<?, ?> normalizedNode)
             throws DeserializationException {
-        return bindingToLegacy.dataObjectFromDataDom(path, (CompositeNode) DataNormalizer.toLegacy(normalizedNode));
+        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);
+        }
+
+        return bindingToLegacy.dataObjectFromDataDom(path, legacy);
     }
 
     public DataNormalizer getDataNormalizer() {
@@ -123,4 +201,141 @@ public class BindingToNormalizedNodeCodec implements SchemaContextListener {
         legacyToNormalized = new DataNormalizer(arg0);
     }
 
+    private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalizedAugmented(
+            final InstanceIdentifier<?> augPath) {
+        org.opendaylight.yangtools.yang.data.api.InstanceIdentifier 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;
+        }
+        // Here we employ small trick - DataNormalizer injecst augmentation identifier if child is
+        // also part of the path (since using a child we can safely identify augmentation)
+        // so, we scan augmentation for children add it to path
+        // and use original algorithm, then shorten it to last augmentation
+        for (@SuppressWarnings("rawtypes") Class augChild : getAugmentationChildren(augPath.getTargetType())) {
+            @SuppressWarnings("unchecked")
+            InstanceIdentifier<?> childPath = augPath.child(augChild);
+            org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized = toNormalizedImpl(childPath);
+            org.opendaylight.yangtools.yang.data.api.InstanceIdentifier potentialDiscovered = shortenToLastAugmentation(normalized);
+            if (potentialDiscovered != null) {
+                return potentialDiscovered;
+            }
+        }
+        return processed;
+    }
+
+
+
+    private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier shortenToLastAugmentation(
+            final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized) {
+        int position = 0;
+        int foundPosition = -1;
+        for (PathArgument arg : normalized.getPath()) {
+            position++;
+            if (arg instanceof AugmentationIdentifier) {
+                foundPosition = position;
+            }
+        }
+        if (foundPosition > 0) {
+            return new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(normalized.getPath().subList(0,
+                    foundPosition));
+        }
+        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;
+            }
+        }
+        return InstanceIdentifier.create(Iterables.limit(binding.getPathArguments(), foundPosition));
+    }
+
+
+
+    private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalizedImpl(
+            final InstanceIdentifier<? extends DataObject> binding) {
+        final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier legacyPath = bindingToLegacy
+                .toDataDom(binding);
+        final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized = legacyToNormalized
+                .toNormalized(legacyPath);
+        return normalized;
+    }
+
+    @SuppressWarnings("unchecked")
+    private Iterable<Class<? extends DataObject>> getAugmentationChildren(final Class<?> targetType) {
+        List<Class<? extends DataObject>> ret = new LinkedList<>();
+        for (Method method : targetType.getMethods()) {
+            Class<?> entity = getYangModeledType(method);
+            if (entity != null) {
+                ret.add((Class<? extends DataObject>) entity);
+            }
+        }
+        return ret;
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    private Class<? extends DataObject> getYangModeledType(final Method method) {
+        if (method.getName().equals("getClass") || !method.getName().startsWith("get")
+                || method.getParameterTypes().length > 0) {
+            return null;
+        }
+
+        Class<?> returnType = method.getReturnType();
+        if (DataContainer.class.isAssignableFrom(returnType)) {
+            return (Class) returnType;
+        } else if (List.class.isAssignableFrom(returnType)) {
+            try {
+                return ClassLoaderUtils.withClassLoader(method.getDeclaringClass().getClassLoader(),
+                        new Callable<Class>() {
+
+                            @SuppressWarnings("rawtypes")
+                            @Override
+                            public Class call() throws Exception {
+                                Type listResult = ClassLoaderUtils.getFirstGenericParameter(method
+                                        .getGenericReturnType());
+                                if (listResult instanceof Class
+                                        && DataObject.class.isAssignableFrom((Class) listResult)) {
+                                    return (Class<?>) listResult;
+                                }
+                                return null;
+                            }
+
+                        });
+            } catch (Exception e) {
+                LOG.debug("Could not get YANG modeled entity for {}", method, e);
+                return null;
+            }
+
+        }
+        return null;
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    private static InstanceIdentifier<?> toWildcarded(final InstanceIdentifier<?> orig) {
+        List<org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument> wildArgs = new LinkedList<>();
+        for (org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : orig.getPathArguments()) {
+            wildArgs.add(new Item(arg.getType()));
+        }
+        return InstanceIdentifier.create(wildArgs);
+    }
+
+
+    private static boolean isAugmentation(final Class<? extends DataObject> type) {
+        return Augmentation.class.isAssignableFrom(type);
+    }
+
+    private static boolean isAugmentationIdentifier(final InstanceIdentifier<?> path) {
+        return Augmentation.class.isAssignableFrom(path.getTargetType());
+    }
+
+    private boolean isAugmentationIdentifier(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier processed) {
+        return Iterables.getLast(processed.getPath()) instanceof AugmentationIdentifier;
+    }
 }
diff --git a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/WriteParentListenAugmentTest.java b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/WriteParentListenAugmentTest.java
new file mode 100644 (file)
index 0000000..cb31885
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2014 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.controller.sal.binding.test.bugfix;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.util.concurrent.SettableFuture;
+
+public class WriteParentListenAugmentTest extends AbstractDataServiceTest {
+
+    private static final String NODE_ID = "node:1";
+
+    private static final NodeKey NODE_KEY = new NodeKey(new NodeId(NODE_ID));
+    private static final InstanceIdentifier<Node> NODE_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) //
+            .child(Node.class, NODE_KEY).toInstance();
+
+    private static final InstanceIdentifier<FlowCapableNode> AUGMENT_WILDCARDED_PATH = InstanceIdentifier
+            .builder(Nodes.class).child(Node.class).augmentation(FlowCapableNode.class).toInstance();
+
+    private static final InstanceIdentifier<FlowCapableNode> AUGMENT_NODE_PATH = InstanceIdentifier
+            .builder(Nodes.class).child(Node.class, NODE_KEY).augmentation(FlowCapableNode.class).toInstance();
+
+    @Test
+    public void writeNodeListenAugment() throws Exception {
+
+        final SettableFuture<DataChangeEvent<InstanceIdentifier<?>, DataObject>> event = SettableFuture.create();
+
+        ListenerRegistration<DataChangeListener> dclRegistration = baDataService.registerDataChangeListener(
+                AUGMENT_WILDCARDED_PATH, new DataChangeListener() {
+
+                    @Override
+                    public void onDataChanged(final DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+                        event.set(change);
+                    }
+                });
+
+        DataModificationTransaction modification = baDataService.beginTransaction();
+
+        Node node = new NodeBuilder() //
+                .setKey(NODE_KEY) //
+                .addAugmentation(FlowCapableNode.class, flowCapableNode("one")).build();
+        modification.putOperationalData(NODE_INSTANCE_ID_BA, node);
+        modification.commit().get();
+
+        DataChangeEvent<InstanceIdentifier<?>, DataObject> receivedEvent = event.get(1000, TimeUnit.MILLISECONDS);
+        assertTrue(receivedEvent.getCreatedOperationalData().containsKey(AUGMENT_NODE_PATH));
+
+        dclRegistration.close();
+
+        DataModificationTransaction mod2 = baDataService.beginTransaction();
+        mod2.putOperationalData(AUGMENT_NODE_PATH, flowCapableNode("two"));
+        mod2.commit().get();
+
+        FlowCapableNode readedAug = (FlowCapableNode) baDataService.readOperationalData(AUGMENT_NODE_PATH);
+        assertEquals("two", readedAug.getDescription());
+
+    }
+
+    private FlowCapableNode flowCapableNode(final String description) {
+        return new FlowCapableNodeBuilder() //
+                .setDescription(description) //
+                .build();
+    }
+}
\ No newline at end of file