Add support for metadata to the Match/Action classes 55/11755/2
authorGiovanni Meo <gmeo@cisco.com>
Mon, 6 Oct 2014 10:09:25 +0000 (12:09 +0200)
committerGiovanni Meo <gmeo@cisco.com>
Wed, 15 Oct 2014 13:16:05 +0000 (15:16 +0200)
- Allow to attach some property to Match and Action so they can be
used for passing extra informations, that applications can use.

Change-Id: I4225271d7cd3429b11adb191ae178f541bbd635d
Signed-off-by: Giovanni Meo <gmeo@cisco.com>
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/action/Action.java
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/Match.java
opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/action/ActionTest.java
opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/match/MatchTest.java

index 1ff7ea1444052e84d36e4cf80b440ad9159cd01f..981b175e197242a0e4347b3407ad3cfc86cb3576 100644 (file)
@@ -8,6 +8,15 @@
 
 package org.opendaylight.controller.sal.action;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.opendaylight.controller.sal.core.Property;
+
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
@@ -32,6 +41,7 @@ public abstract class Action implements Serializable {
     @XmlElement
     protected ActionType type;
     private transient boolean isValid = true;
+    private ConcurrentMap<String, Property> props;
 
     /* Dummy constructor for JAXB */
     public Action() {
@@ -83,6 +93,81 @@ public abstract class Action implements Serializable {
         }
     }
 
+    /**
+     * Gets the list of metadata currently registered with this match
+     *
+     * @return List of metadata currently registered
+     */
+    public List <Property> getMetadatas() {
+        if (this.props != null) {
+            // Return all the values in the map
+            Collection res = this.props.values();
+            if (res == null) {
+                return Collections.emptyList();
+            }
+            return new ArrayList<Property>(res);
+        }
+        return Collections.emptyList();
+    }
+
+    /**
+     * Gets the metadata registered with a name if present
+     *
+     * @param name the name of the property to be extracted
+     *
+     * @return List of metadata currently registered
+     */
+    public Property getMetadata(String name) {
+        if (name == null) {
+            return null;
+        }
+        if (this.props != null) {
+            // Return the Property associated to the name
+            return this.props.get(name);
+        }
+        return null;
+    }
+
+    /**
+     * Sets the metadata associated to a name. If the name or prop is NULL,
+     * an exception NullPointerException will be raised.
+     *
+     * @param name the name of the property to be set
+     * @param prop, property to be set
+     */
+    public void setMetadata(String name, Property prop) {
+        if (this.props == null) {
+            props = new ConcurrentHashMap<String, Property>();
+        }
+
+        if (this.props != null) {
+            this.props.put(name, prop);
+        }
+    }
+
+    /**
+     * Remove the metadata associated to a name. If the name is NULL,
+     * nothing will be removed.
+     *
+     * @param name the name of the property to be set
+     * @param prop, property to be set
+     *
+     * @return List of metadata currently registered
+     */
+    public void removeMetadata(String name) {
+        if (this.props == null) {
+            return;
+        }
+
+        if (this.props != null) {
+            this.props.remove(name);
+        }
+        // It's intentional to keep the this.props still allocated
+        // till the parent data structure will be alive, so to avoid
+        // unnecessary allocation/deallocation, even if it's holding
+        // nothing
+    }
+
     /**
      * Returns the type of this action
      *
index 910695c1e9cbc0ebcfb121b621fb7187305aff37..a00c3dcb3422d6e20c619bd35d616b62fb6bbab3 100644 (file)
@@ -1,4 +1,3 @@
-
 /*
  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
  *
@@ -15,18 +14,22 @@ import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.TreeMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentHashMap;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
 
+import org.opendaylight.controller.sal.core.Property;
 import org.opendaylight.controller.sal.utils.EtherTypes;
 import org.opendaylight.controller.sal.utils.IPProtocols;
 import org.opendaylight.controller.sal.utils.NetUtils;
@@ -52,7 +55,9 @@ public class Match implements Cloneable, Serializable {
         reversableMatches = Collections.unmodifiableMap(map);
     }
     private Map<MatchType, MatchField> fields;
-    private int matches; // concise way to tell which fields the match is set for (may remove if not needed)
+    private int matches; // concise way to tell which fields the match
+                         // is set for (may remove if not needed)
+    private ConcurrentMap<String, Property> props;
 
     public Match() {
         fields = new HashMap<MatchType, MatchField>();
@@ -64,6 +69,81 @@ public class Match implements Cloneable, Serializable {
         matches = match.matches;
     }
 
+    /**
+     * Gets the list of metadata currently registered with this match
+     *
+     * @return List of metadata currently registered
+     */
+    public List <Property> getMetadatas() {
+        if (this.props != null) {
+            // Return all the values in the map
+            Collection res = this.props.values();
+            if (res == null) {
+                return Collections.emptyList();
+            }
+            return new ArrayList<Property>(res);
+        }
+        return Collections.emptyList();
+    }
+
+    /**
+     * Gets the metadata registered with a name if present
+     *
+     * @param name the name of the property to be extracted
+     *
+     * @return List of metadata currently registered
+     */
+    public Property getMetadata(String name) {
+        if (name == null) {
+            return null;
+        }
+        if (this.props != null) {
+            // Return the Property associated to the name
+            return this.props.get(name);
+        }
+        return null;
+    }
+
+    /**
+     * Sets the metadata associated to a name. If the name or prop is NULL,
+     * an exception NullPointerException will be raised.
+     *
+     * @param name the name of the property to be set
+     * @param prop, property to be set
+     */
+    public void setMetadata(String name, Property prop) {
+        if (this.props == null) {
+            props = new ConcurrentHashMap<String, Property>();
+        }
+
+        if (this.props != null) {
+            this.props.put(name, prop);
+        }
+    }
+
+    /**
+     * Remove the metadata associated to a name. If the name is NULL,
+     * nothing will be removed.
+     *
+     * @param name the name of the property to be set
+     * @param prop, property to be set
+     *
+     * @return List of metadata currently registered
+     */
+    public void removeMetadata(String name) {
+        if (this.props == null) {
+            return;
+        }
+
+        if (this.props != null) {
+            this.props.remove(name);
+        }
+        // It's intentional to keep the this.props still allocated
+        // till the parent data structure will be alive, so to avoid
+        // unnecessary allocation/deallocation, even if it's holding
+        // nothing
+    }
+
     /**
      * Generic setter for frame/packet/message's header fields against which to match
      * Note: For MAC addresses, please pass the cloned value to this function
index 14f0d83ff670d7343b1b67e59796cdec44223cf9..edcb3e2761fd1e8e02117eecb96c4685fa47d5fc 100644 (file)
@@ -17,6 +17,9 @@ import org.junit.Test;
 import org.junit.Assert;
 import org.opendaylight.controller.sal.core.Node;
 import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.Property;
+import org.opendaylight.controller.sal.core.Tables;
+import org.opendaylight.controller.sal.core.Tier;
 import org.opendaylight.controller.sal.utils.EtherTypes;
 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
 import org.slf4j.Logger;
@@ -269,4 +272,89 @@ public class ActionTest {
                 .createNodeConnector((short) 5, node))));
         Assert.assertFalse(actions.contains(new Controller()));
     }
+
+    @Test
+    public void testMetadata() {
+        Property tier1 = new Tier(1);
+        Property tier2 = new Tier(2);
+        Property table1 = new Tables((byte)0x7f);
+        Action a1 = new PopVlan();
+        List<Property> resprops = null;
+        resprops = a1.getMetadatas();
+        // This should be an empty list
+        Assert.assertTrue(resprops.isEmpty());
+        a1.setMetadata("tier1", tier1);
+        a1.setMetadata("tier2", tier2);
+        a1.setMetadata("table1", table1);
+        resprops = a1.getMetadatas();
+        // Check for the number of elements in it
+        Assert.assertTrue(resprops.size() == 3);
+        // Check if the elements are in it
+        Assert.assertTrue(resprops.contains(tier1));
+        Assert.assertTrue(resprops.contains(tier2));
+        Assert.assertTrue(resprops.contains(table1));
+        // Check for single elements retrieve
+        Assert.assertTrue(a1.getMetadata("tier1").equals(tier1));
+        Assert.assertTrue(a1.getMetadata("tier2").equals(tier2));
+        Assert.assertTrue(a1.getMetadata("table1").equals(table1));
+        // Now remove an element and make sure the remaining are
+        // correct
+        a1.removeMetadata("tier1");
+
+        resprops = a1.getMetadatas();
+        // Check for the number of elements in it
+        Assert.assertTrue(resprops.size() == 2);
+        // Check if the elements are in it
+        Assert.assertFalse(resprops.contains(tier1));
+        Assert.assertTrue(resprops.contains(tier2));
+        Assert.assertTrue(resprops.contains(table1));
+        // Check for single elements retrieve
+        Assert.assertTrue(a1.getMetadata("table1").equals(table1));
+        Assert.assertTrue(a1.getMetadata("tier2").equals(tier2));
+        Assert.assertNull(a1.getMetadata("tier1"));
+
+        // Check for an element never existed
+        Assert.assertNull(a1.getMetadata("table100"));
+
+        // Remove them all
+        a1.removeMetadata("tier2");
+        a1.removeMetadata("table1");
+
+        // Remove also a non-existent one
+        a1.removeMetadata("table100");
+
+        resprops = a1.getMetadatas();
+        // Check there are no elements left
+        Assert.assertTrue(resprops.size() == 0);
+
+        // Now check for exception on setting null values
+        try {
+            a1.setMetadata("foo", null);
+            // The line below should never be reached
+            Assert.assertTrue(false);
+        } catch (NullPointerException nue) {
+            // NPE should be raised for null value
+            Assert.assertTrue(true);
+        }
+
+        // Now check on using null key
+        try {
+            a1.setMetadata(null, table1);
+            // The line below should never be reached
+            Assert.assertTrue(false);
+        } catch (NullPointerException nue) {
+            // NPE should be raised for null value
+            Assert.assertTrue(true);
+        }
+
+        // Now check on using null key and null value
+        try {
+            a1.setMetadata(null, null);
+            // The line below should never be reached
+            Assert.assertTrue(false);
+        } catch (NullPointerException nue) {
+            // NPE should be raised for null value
+            Assert.assertTrue(true);
+        }
+    }
 }
index b88ae034d9aa713c96f4ce3ef633f977d5a223b3..b89b27ffe0a61a1c521bfe8657274081b73cd80e 100644 (file)
@@ -11,11 +11,15 @@ package org.opendaylight.controller.sal.match;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.Arrays;
+import java.util.List;
 
 import org.junit.Assert;
 import org.junit.Test;
 import org.opendaylight.controller.sal.core.Node;
 import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.Property;
+import org.opendaylight.controller.sal.core.Tables;
+import org.opendaylight.controller.sal.core.Tier;
 import org.opendaylight.controller.sal.utils.EtherTypes;
 import org.opendaylight.controller.sal.utils.IPProtocols;
 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
@@ -638,4 +642,89 @@ public class MatchTest {
         // No intersection with null match, empty set
         Assert.assertNull(m6.getIntersection(null));
     }
+
+    @Test
+    public void testMetadata() {
+        Property tier1 = new Tier(1);
+        Property tier2 = new Tier(2);
+        Property table1 = new Tables((byte)0x7f);
+        Match m1 = new Match();
+        List<Property> resprops = null;
+        resprops = m1.getMetadatas();
+        // This should be null
+        Assert.assertTrue(resprops.isEmpty());
+        m1.setMetadata("tier1", tier1);
+        m1.setMetadata("tier2", tier2);
+        m1.setMetadata("table1", table1);
+        resprops = m1.getMetadatas();
+        // Check for the number of elements in it
+        Assert.assertTrue(resprops.size() == 3);
+        // Check if the elements are in it
+        Assert.assertTrue(resprops.contains(tier1));
+        Assert.assertTrue(resprops.contains(tier2));
+        Assert.assertTrue(resprops.contains(table1));
+        // Check for single elements retrieve
+        Assert.assertTrue(m1.getMetadata("tier1").equals(tier1));
+        Assert.assertTrue(m1.getMetadata("tier2").equals(tier2));
+        Assert.assertTrue(m1.getMetadata("table1").equals(table1));
+        // Now remove an element and make sure the remaining are
+        // correct
+        m1.removeMetadata("tier1");
+
+        resprops = m1.getMetadatas();
+        // Check for the number of elements in it
+        Assert.assertTrue(resprops.size() == 2);
+        // Check if the elements are in it
+        Assert.assertFalse(resprops.contains(tier1));
+        Assert.assertTrue(resprops.contains(tier2));
+        Assert.assertTrue(resprops.contains(table1));
+        // Check for single elements retrieve
+        Assert.assertTrue(m1.getMetadata("table1").equals(table1));
+        Assert.assertTrue(m1.getMetadata("tier2").equals(tier2));
+        Assert.assertNull(m1.getMetadata("tier1"));
+
+        // Check for an element never existed
+        Assert.assertNull(m1.getMetadata("table100"));
+
+        // Remove them all
+        m1.removeMetadata("tier2");
+        m1.removeMetadata("table1");
+
+        // Remove also a non-existent one
+        m1.removeMetadata("table100");
+
+        resprops = m1.getMetadatas();
+        // Check there are no elements left
+        Assert.assertTrue(resprops.size() == 0);
+
+        // Now check for exception on setting null values
+        try {
+            m1.setMetadata("foo", null);
+            // The line below should never be reached
+            Assert.assertTrue(false);
+        } catch (NullPointerException nue) {
+            // NPE should be raised for null value
+            Assert.assertTrue(true);
+        }
+
+        // Now check on using null key
+        try {
+            m1.setMetadata(null, table1);
+            // The line below should never be reached
+            Assert.assertTrue(false);
+        } catch (NullPointerException nue) {
+            // NPE should be raised for null value
+            Assert.assertTrue(true);
+        }
+
+        // Now check on using null key and null value
+        try {
+            m1.setMetadata(null, null);
+            // The line below should never be reached
+            Assert.assertTrue(false);
+        } catch (NullPointerException nue) {
+            // NPE should be raised for null value
+            Assert.assertTrue(true);
+        }
+    }
 }