Major refactoring to use FlowObjectives

- Removed all Group-related code from CarrierEthernetOpenFlowPacketManager.java
- EPL tested to work with CPqD and Accton 6712
- Note: For double-tagging with CPqD check out Change 9696

Change-Id: I5406a336f7cf65aa46912a1dc9cc09891f606a8e
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetEnni.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetEnni.java
index 41c57fa..3888f2d 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetEnni.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetEnni.java
@@ -84,7 +84,7 @@
     public void addEvcEnni(CarrierEthernetEnni enni) {
 
         // Add S-VLAN ID
-        if (enni.sVlanId() != null) {
+        if (enni.sVlanId() != VlanId.NONE) {
             this.sVlanIdSet.add(enni.sVlanId());
         }
         // Used capacity cannot be more than ENNI capacity
@@ -114,7 +114,7 @@
     public boolean validateEvcEnni(CarrierEthernetEnni enni) {
 
         // Check if the S-VLAN ID of the ENNI is already included in global ENNI
-        if (enni.sVlanId() != null) {
+        if (enni.sVlanId() != VlanId.NONE) {
             if (sVlanIdSet.contains(enni.sVlanId())) {
                 log.error("S-VLAN ID {} already exists in ENNI {}", enni.sVlanId().toString(), this.id());
                 return false;
@@ -146,13 +146,23 @@
      */
     public VlanId sVlanId() {
         if (sVlanIdSet.isEmpty()) {
-            return null;
+            return VlanId.NONE;
         } else {
             return sVlanIdSet.iterator().next();
         }
     }
 
     /**
+     * Always returns null, since CE-VLAN IDs are not associated with ENNIs.
+     *
+     * @return null
+     */
+    @Override
+    public VlanId ceVlanId() {
+        return null;
+    }
+
+    /**
      * Returns ENNI tpid - applicable only to service-specific ENNIs.
      *
      * @return tpid
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetInni.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetInni.java
index a195bc1..b5d5ff3 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetInni.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetInni.java
@@ -87,7 +87,7 @@
     public void addEvcInni(CarrierEthernetInni inni) {
 
         // Add S-VLAN ID
-        if (inni.sVlanId() != null) {
+        if (inni.sVlanId() != VlanId.NONE) {
             this.sVlanIdSet.add(inni.sVlanId());
         }
         // Used capacity cannot be more than INNI capacity
@@ -150,13 +150,23 @@
      */
     public VlanId sVlanId() {
         if (sVlanIdSet.isEmpty()) {
-            return null;
+            return VlanId.NONE;
         } else {
             return sVlanIdSet.iterator().next();
         }
     }
 
     /**
+     * Always returns null, since CE-VLAN IDs are not associated with INNIs.
+     *
+     * @return null
+     */
+    @Override
+    public VlanId ceVlanId() {
+        return null;
+    }
+
+    /**
      * Returns the set of S-VLAN ids associated with the INNI.
      *
      * @return S-VLAN id set
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetManager.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetManager.java
index 5c5ce09..43e29fa 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetManager.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetManager.java
@@ -238,10 +238,10 @@
         Iterator<CarrierEthernetUni> it = evc.uniSet().iterator();
         while (it.hasNext()) {
             CarrierEthernetUni uni = it.next();
-            if (uni.ceVlanId() == null && isVirtual) {
+            if (uni.ceVlanId() == VlanId.NONE && isVirtual) {
                 log.error("Could not validate the virtual status of the EVC.");
                 return null;
-            } else if (uni.ceVlanId() != null){
+            } else if (uni.ceVlanId() != VlanId.NONE){
                 isVirtual = true;
             }
         }
@@ -483,9 +483,10 @@
             cePktProvisioner.removeBandwidthProfiles(evcMap.get(evcId));
 
             // Remove UNI resources (BWP, CE-VLAN ID) from global UNI
-            CarrierEthernetUni newUni = uniMap.get(uni.id());
+            /*CarrierEthernetUni newUni = uniMap.get(uni.id());
             newUni.removeEvcUni(uni);
-            uniMap.put(uni.id(), newUni);
+            uniMap.put(uni.id(), newUni);*/
+            uniMap.get(uni.id()).removeEvcUni(uni);
         });
     }
 
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetNetworkInterface.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetNetworkInterface.java
index 45ffb9d..1dbda7d 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetNetworkInterface.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetNetworkInterface.java
@@ -16,6 +16,7 @@
 package org.onosproject.ecord.carrierethernet.app;
 
 import com.google.common.base.Objects;
+import org.onlab.packet.VlanId;
 import org.onlab.util.Bandwidth;
 import org.onosproject.cli.AbstractShellCommand;
 import org.onosproject.net.ConnectPoint;
@@ -105,6 +106,20 @@
     }
 
     /**
+     * Returns the NI S-TAG.
+     *
+     * @return NI S-TAG
+     */
+    public abstract VlanId sVlanId();
+
+    /**
+     * Returns the NI CE-VLAN ID.
+     *
+     * @return NI CE-VLAN ID
+     */
+    public abstract VlanId ceVlanId();
+
+    /**
      * Returns the scope of the NI (GLOBAL or SERVICE).
      *
      * @return NI scope
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetOpenFlowPacketNodeManager.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetOpenFlowPacketNodeManager.java
index 2936a44..8446d80 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetOpenFlowPacketNodeManager.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetOpenFlowPacketNodeManager.java
@@ -1,6 +1,5 @@
 package org.onosproject.ecord.carrierethernet.app;
 
-import com.google.common.collect.Lists;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
@@ -11,14 +10,9 @@
 import org.onlab.packet.VlanId;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
-import org.onosproject.core.DefaultGroupId;
-import org.onosproject.core.GroupId;
-import org.onosproject.driver.extensions.OfdpaMatchVlanVid;
-import org.onosproject.driver.extensions.OfdpaSetVlanVid;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
-import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.flow.DefaultFlowRule;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
@@ -29,22 +23,10 @@
 import org.onosproject.net.flow.criteria.Criteria;
 import org.onosproject.net.flow.criteria.Criterion;
 import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
 import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.flow.instructions.Instructions;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.net.flowobjective.DefaultForwardingObjective;
-import org.onosproject.net.flowobjective.FilteringObjective;
-import org.onosproject.net.flowobjective.DefaultFilteringObjective;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
-import org.onosproject.net.group.Group;
-import org.onosproject.net.group.GroupBucket;
-import org.onosproject.net.group.GroupBuckets;
-import org.onosproject.net.group.GroupKey;
-import org.onosproject.net.group.DefaultGroupKey;
-import org.onosproject.net.group.DefaultGroupBucket;
-import org.onosproject.net.group.DefaultGroupDescription;
-import org.onosproject.net.group.GroupDescription;
-import org.onosproject.net.group.GroupService;
+import org.onosproject.net.flowobjective.*;
 import org.onosproject.net.meter.Meter;
 import org.onosproject.net.meter.MeterId;
 import org.onosproject.net.meter.MeterRequest;
@@ -55,23 +37,15 @@
 import org.onosproject.openflow.controller.Dpid;
 import org.onosproject.openflow.controller.OpenFlowController;
 import org.onosproject.openflow.controller.OpenFlowSwitch;
-import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.slf4j.Logger;
 
-import java.util.Map;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Collection;
-import java.util.Objects;
+import java.util.*;
 
-import static com.google.common.base.Preconditions.checkNotNull;
+import org.apache.commons.lang3.tuple.Pair;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
- *  Class used to control Carrier Ethernet nodes according to the OpenFlow protocol.
+ *  Class used to control Carrier Ethernet nodes according to the OpenFlow (1.3 and above) protocol.
  */
 @Component(immediate = true)
 @Service (value = CarrierEthernetOpenFlowPacketNodeManager.class)
@@ -81,15 +55,9 @@
     protected CoreService coreService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected DeviceService deviceService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected FlowRuleService flowRuleService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected GroupService groupService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected MeterService meterService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -104,22 +72,11 @@
 
     private static final int PRIORITY = 50000;
 
-    // TODO: Get the OFDPA2.0 table numbers from the OFDPA Pipeline Class?
-    protected static final int PORT_TABLE = 0;
-    protected static final int VLAN_TABLE = 10;
-    protected static final int TMAC_TABLE = 20;
-    protected static final int UNICAST_ROUTING_TABLE = 30;
-    protected static final int MULTICAST_ROUTING_TABLE = 40;
-    protected static final int MPLS_TABLE_0 = 23;
-    protected static final int MPLS_TABLE_1 = 24;
-    protected static final int BRIDGING_TABLE = 50;
-    protected static final int ACL_TABLE = 60;
-    protected static final int MAC_LEARNING_TABLE = 254;
+    // TODO: Below maps to be replaced by the meter ids and flow objectives associated with each CE Intent
 
-    // TODO: Below maps to be replaced by the meter ids, egress cps and flow rules kept with each service (?)
+    // FIXME: Replace with Pair<DeviceId, MeterId>
     private final Map<String, Set<DeviceMeterId>> deviceMeterIdMap = new HashMap<>();
-    private final Map<String, Set<ConnectPoint>> egressCpMap = new HashMap<>();
-    private final Map<String, Set<FlowRule>> flowRuleMap = new HashMap();
+    private final Map<String, LinkedList<Pair<DeviceId, Objective>>> flowObjectiveMap = new HashMap();
 
     @Activate
     protected void activate() {
@@ -130,533 +87,180 @@
     protected void deactivate() {}
 
     @Override
-    public void setNodeForwarding(CarrierEthernetVirtualConnection service, CarrierEthernetNetworkInterface srcNi,
+    public void setNodeForwarding(CarrierEthernetVirtualConnection evc, CarrierEthernetNetworkInterface srcNi,
                                   CarrierEthernetNetworkInterface dstNi, ConnectPoint ingress, ConnectPoint egress,
                                   boolean first, boolean last) {
 
-        // TODO: Produce error if ingress and egress do not belong to same device
-
-        Set<ConnectPoint> egressCpSet = egressCpMap.get(service.id());
-        if (egressCpSet == null) {
-            egressCpSet = new HashSet<>();
-        }
-        Set<FlowRule> flowRuleSet = flowRuleMap.get(service.id());
-        if (flowRuleSet == null) {
-            flowRuleSet = new HashSet<>();
+        // FIXME: Is this necessary? Can first and last be true at the same time?
+        if(srcNi.cp().deviceId().equals(dstNi.cp().deviceId())) {
+            log.error("Source and destination NI must belong to different devices.");
+            return;
         }
 
-        // FIXME: Uncomment this after Hackathon
-        flowRuleSet.addAll(createNrpFlowRule(service.id(), srcNi, dstNi, service.vlanId(),
-                ingress, egress, first, last));
-        /*flowRuleSet.addAll(createFlowRules(service.id(), ((CarrierEthernetUni) srcNi).ceVlanId(), service.vlanId(),
-                ingress, egress, first, last));*/
+        if (flowObjectiveMap.get(evc.id()) == null) {
+            flowObjectiveMap.put(evc.id(), new LinkedList<>());
+        }
 
-
-        egressCpSet.add(egress);
-
-        egressCpMap.put(service.id(), egressCpSet);
-        flowRuleMap.put(service.id(), flowRuleSet);
-
+        // TODO: Get created FlowObjectives from this method
+        createFlowObjectives(evc, srcNi, dstNi, ingress, egress, first, last);
     }
 
-    // Directly creates FlowRules using GROUP action (meant for OF1.3 non-OFDPA devices)
-    private Set<FlowRule> createNrpFlowRule(String serviceId, CarrierEthernetNetworkInterface srcNi,
-                                            CarrierEthernetNetworkInterface dstNi, VlanId vlanId,
-                                             ConnectPoint ingress, ConnectPoint egress,
-                                             boolean first, boolean last) {
+    // Directly creates FlowRules using GROUP actions
+    private void createFlowObjectives(CarrierEthernetVirtualConnection evc, CarrierEthernetNetworkInterface srcNi,
+                                      CarrierEthernetNetworkInterface dstNi, ConnectPoint ingress, ConnectPoint egress,
+                                      boolean first, boolean last) {
 
-        Set<FlowRule> flowRuleSet = new HashSet<>();
+        /////////////////////////////////////////
+        // Prepare and submit filtering objective
+        /////////////////////////////////////////
 
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
-                .matchInPort(ingress.port());
+        FilteringObjective.Builder filteringObjectiveBuilder = DefaultFilteringObjective.builder()
+                .permit().fromApp(appId)
+                .withPriority(PRIORITY)
+                .withKey(Criteria.matchInPort(ingress.port()));
 
-        TrafficTreatment.Builder tBuilder;
+        TrafficTreatment.Builder filterTreatmentBuilder = DefaultTrafficTreatment.builder();
+
+        // In general, nodes would match on the VLAN tag assigned to the EVC/FC
+        Criterion filterVlanIdCriterion = Criteria.matchVlanId(evc.vlanId());
+
         if (first) {
-
-            // Decide what to do depending on the type of NI (UNI/INNI/ENNI)
             if ((srcNi instanceof CarrierEthernetInni) || (srcNi instanceof CarrierEthernetEnni) ) {
-                VlanId sVlanId;
-                if (srcNi instanceof CarrierEthernetInni) {
-                    sVlanId = ((CarrierEthernetInni) srcNi).sVlanId();
-                } else {
-                    sVlanId = ((CarrierEthernetEnni) srcNi).sVlanId();
-                }
-                if (sVlanId != null) {
-                    sBuilder.matchVlanId(sVlanId);
-                }
-                tBuilder = DefaultTrafficTreatment.builder();
-                // Pop S-TAG if it exists and add the new one
-                // TODO: Check TPID
-                tBuilder.popVlan();
-                tBuilder.pushVlan().setVlanId(vlanId);
+                // TODO: Check TPID? Also: Is is possible to receive untagged pkts at an INNI/ENNI?
+                // First node of an FC should match on S-TAG if it's an INNI/ENNI
+                filterVlanIdCriterion = Criteria.matchVlanId(srcNi.sVlanId());
+                // Translate S-TAG to the one used in the current FC
+                filterTreatmentBuilder.setVlanId(evc.vlanId());
             } else {
-                VlanId ceVlanId = ((CarrierEthernetUni) srcNi).ceVlanId();
-                // If this is a virtual service, match also on CE-VLAN ID at first hop
-                if (ceVlanId != null) {
-                    sBuilder.matchVlanId(ceVlanId);
-                }
-                tBuilder = DefaultTrafficTreatment.builder();
-                tBuilder.pushVlan().setVlanId(vlanId);
-            }
-        } else {
-            sBuilder.matchVlanId(vlanId);
-            tBuilder = DefaultTrafficTreatment.builder();
-        }
-
-        if (last) {
-            // If NI is not a UNI, keep the existing tags - they will be popped at the entrance of the next FC
-            if (dstNi instanceof CarrierEthernetUni) {
-                tBuilder.popVlan();
+                // First node of an FC should match on CE-VLAN ID (if present) if it's a UNI
+                filterVlanIdCriterion = Criteria.matchVlanId(srcNi.ceVlanId());
+                // Push S-TAG of current FC on top of existing CE-VLAN ID
+                filterTreatmentBuilder.pushVlan().setVlanId(evc.vlanId());
             }
         }
-        tBuilder.setOutput(egress.port());
 
-        // Check if flow with same selector already exists. If yes, modify existing flow rule if needed
-        flowRuleService.getFlowRulesById(appId).forEach(flowRule -> {
-            if (flowRule.deviceId().equals(egress.deviceId()) && flowRule.selector().equals(sBuilder.build())) {
-                flowRule.treatment().allInstructions().forEach(instruction -> {
-                    // If this is an OUTPUT instruction and output is different than existing, add the group
-                    if (instruction.type() == Instruction.Type.OUTPUT &&
-                            !(instruction.equals(Instructions.createOutput(egress.port())))) {
-                        tBuilder.add(instruction);
-                    }
-                });
-            }
-        });
+        filteringObjectiveBuilder.addCondition(filterVlanIdCriterion);
 
-        // FIXME: For efficiency do not send FlowMod again if the new treatment is exactly the same as the existing one
-        FlowRule flowRule = createFlowRule(egress.deviceId(), PRIORITY, sBuilder.build(), tBuilder.build(), 0);
-        flowRuleService.applyFlowRules(flowRule);
-        flowRuleSet.add(flowRule);
-
-        return flowRuleSet;
-    }
-
-    // FIXME: Temporary solution for establishing flow rules according to switch type
-    private Set<FlowRule> createFlowRules(String serviceId, VlanId ceVlanId, VlanId vlanId,
-                                          ConnectPoint ingress, ConnectPoint egress, boolean first, boolean last) {
-
-        Dpid dpid = Dpid.dpid(egress.deviceId().uri());
-        OpenFlowSwitch sw = controller.getSwitch(dpid);
-
-        Set<FlowRule> flowRuleSet = new HashSet<>();
-        if (sw.softwareDescription().equals("OF-DPA 2.0")) {
-            flowRuleSet = createOfdpaFlowRules(serviceId, ceVlanId, vlanId, ingress, egress, first, last);
-            //createFilteringForwarding(evcId, ceVlanId, vlanId, ingress, egress, first, last);
-        } else if (sw.factory().getVersion() == OFVersion.OF_13) {
-            flowRuleSet = createOF13FlowRule(serviceId, ceVlanId, vlanId, ingress, egress, first, last);
-        } else {
-            flowRuleSet = createOF10FlowRule(serviceId, ceVlanId, vlanId, ingress, egress, first, last);
-        }
-
-        return flowRuleSet;
-    }
-
-    /**
-     * Directly creates FlowRules according to the OFDPA pipeline.
-     * To be used instead of FlowObjectives until the OFDPA2Pipeline is modified appropriately
-     *
-     * @param serviceId User-provided identifier of the CE service
-     * @param vlanId VLAN id of the service
-     * @param ingress ingress connect point at the particular device
-     * @param first indicates whether the current device is the first one in the path
-     * @param last indicates whether the current device is the last one in the path
-     */
-    private Set<FlowRule> createOfdpaFlowRules(String serviceId, VlanId ceVlanId, VlanId vlanId,
-                                               ConnectPoint ingress, ConnectPoint egress, boolean first, boolean last) {
-
-        Set<FlowRule> flowRuleSet = new HashSet<>();
-
-        DeviceId deviceId = egress.deviceId();
-
-        // VLAN Table
-        // "Vlan Assignment"
-
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
-                .matchInPort(ingress.port());
-
-        TrafficSelector.Builder preSelector = null;
-        TrafficTreatment.Builder preTreatment = null;
-
-        // Transition to TMAC table
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
-                .transition(TMAC_TABLE);
-
+        // Do not add meta if there are no instructions (i.e. if not first)
         if (first) {
-            // If this is a virtual service, match also on CE-VLAN ID at first hop
-            if (ceVlanId != null) {
-                OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(ceVlanId);
-                sBuilder.extension(ofdpaMatchVlanVid, deviceId);
-                OfdpaSetVlanVid ofdpaSetVlanVid = new OfdpaSetVlanVid(vlanId);
-                tBuilder.extension(ofdpaSetVlanVid, deviceId);
-            } else {
-                OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(VlanId.vlanId((short) 0));
-                sBuilder.extension(ofdpaMatchVlanVid, deviceId);
-                OfdpaSetVlanVid ofdpaSetVlanVid = new OfdpaSetVlanVid(vlanId);
-                tBuilder.extension(ofdpaSetVlanVid, deviceId);
-                // XXX ofdpa will require an additional vlan match on the assigned vlan
-                // and it may not require the push. This is not in compliance with OF
-                // standard. Waiting on what the exact flows are going to look like.
-                preSelector = DefaultTrafficSelector.builder();
-                preSelector.matchInPort(ingress.port());
-                OfdpaMatchVlanVid preOfdpaMatchVlanVid = new OfdpaMatchVlanVid(vlanId);
-                preSelector.extension(preOfdpaMatchVlanVid, deviceId);
-                preTreatment = DefaultTrafficTreatment.builder().transition(20);
-            }
-        } else {
-            OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(vlanId);
-            sBuilder.extension(ofdpaMatchVlanVid, deviceId);
+            filteringObjectiveBuilder.withMeta(filterTreatmentBuilder.build());
         }
 
-        FlowRule flowRule = createFlowRule(deviceId, PRIORITY, sBuilder.build(), tBuilder.build(), VLAN_TABLE);
-        flowRuleService.applyFlowRules(flowRule);
-        flowRuleSet.add(flowRule);
+        flowObjectiveService.filter(ingress.deviceId(), filteringObjectiveBuilder.add());
+        flowObjectiveMap.get(evc.id()).addFirst(Pair.of(ingress.deviceId(), filteringObjectiveBuilder.add()));
 
-        if (preSelector != null) {
+        ////////////////////////////////////////////////////
+        // Prepare and submit next and forwarding objectives
+        ////////////////////////////////////////////////////
 
-            flowRule = createFlowRule(deviceId, PRIORITY, preSelector.build(), preTreatment.build(), VLAN_TABLE);
-            flowRuleService.applyFlowRules(flowRule);
-            flowRuleSet.add(flowRule);
-        }
-
-        // TMAC Table defaults to Bridging Table
-
-        // Build group
-        GroupId groupId = createGroup(serviceId, vlanId, egress, first, last);
-
-        // ACL Table
-        // "IPv4 VLAN"
-
-        // NOTE: Directly adding vlanId match to builder causes rule to get continuously installed/uninstalled by ofdpa
-        sBuilder = DefaultTrafficSelector.builder()
-                .matchInPort(ingress.port())
-                .matchEthType(Ethernet.TYPE_IPV4);
-        //sBuilder.extension(new OfdpaMatchVlanVid(vlanId), deviceId);
-
-        // TODO: Check if there is existing FlowRule with same filtering and if yes modify this rule with an extra group
-        // TODO: NOTE: In OFDPA this probably will be done by first removing the existing flow
-
-        tBuilder = DefaultTrafficTreatment.builder().group(groupId);
-
-        flowRule = createFlowRule(deviceId, PRIORITY, sBuilder.build(), tBuilder.build(), ACL_TABLE);
-        flowRuleService.applyFlowRules(flowRule);
-        flowRuleSet.add(flowRule);
-
-        return flowRuleSet;
-    }
-
-    // Directly creates FlowRules using GROUP action (meant for OF1.3 non-OFDPA devices)
-    private Set<FlowRule> createOF13FlowRule(String serviceId, VlanId ceVlanId, VlanId vlanId,
-                                         ConnectPoint ingress, ConnectPoint egress,
-                                         boolean first, boolean last) {
-
-        Set<FlowRule> flowRuleSet = new HashSet<>();
-
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
-                .matchInPort(ingress.port());
-
-        TrafficTreatment.Builder tBuilder;
-        if (first) {
-            // If this is a virtual service, match also on CE-VLAN ID at first hop
-            if (ceVlanId != null) {
-                sBuilder.matchVlanId(ceVlanId);
-            }
-            tBuilder = DefaultTrafficTreatment.builder();
-            tBuilder.pushVlan().setVlanId(vlanId);
-        } else {
-            sBuilder.matchVlanId(vlanId);
-            tBuilder = DefaultTrafficTreatment.builder();
-        }
-
-        // Build group
-        GroupId groupId = createGroup(serviceId, vlanId, egress, first, last);
-        tBuilder.group(groupId);
-
-        // Check if flow with same selector already exists. If yes, modify existing flow rule if needed
-        flowRuleService.getFlowRulesById(appId).forEach(flowRule -> {
-            if (flowRule.deviceId().equals(egress.deviceId()) && flowRule.selector().equals(sBuilder.build())) {
-                flowRule.treatment().allInstructions().forEach(instruction -> {
-                    // If this is an GROUP instruction and group is different than existing, add the group
-                    if (instruction.type() == Instruction.Type.GROUP &&
-                            !(instruction.equals(Instructions.createGroup(groupId)))) {
-                        tBuilder.add(instruction);
-                    }
-                });
-            }
-        });
-
-        // FIXME: For efficiency do not send FlowMod again if the new treatment is exactly the same as the existing one
-        FlowRule flowRule = createFlowRule(egress.deviceId(), PRIORITY, sBuilder.build(), tBuilder.build(), 0);
-        flowRuleService.applyFlowRules(flowRule);
-        flowRuleSet.add(flowRule);
-
-        return flowRuleSet;
-    }
-
-    // Directly creates FlowRules using OUTPUT action (meant for OF1.0 non-OFDPA devices)
-    private Set<FlowRule> createOF10FlowRule(String serviceId, VlanId ceVlanId, VlanId vlanId,
-                                         ConnectPoint ingress, ConnectPoint egress,
-                                         boolean first, boolean last) {
-
-        Set<FlowRule> flowRuleSet = new HashSet<>();
-
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
-                .matchInPort(ingress.port());
-
-        TrafficTreatment.Builder tBuilder;
-        if (first) {
-            // If this is a virtual service, match also on CE-VLAN ID at first hop
-            if (ceVlanId != null) {
-                sBuilder.matchVlanId(ceVlanId);
-            }
-            tBuilder = DefaultTrafficTreatment.builder();
-            tBuilder.pushVlan().setVlanId(vlanId);
-        } else {
-            sBuilder.matchVlanId(vlanId);
-            tBuilder = DefaultTrafficTreatment.builder();
-        }
-
-        if (last) {
-            tBuilder.popVlan();
-        }
-        tBuilder.setOutput(egress.port());
-
-        // Check if flow with same selector already exists. If yes, modify existing flow rule if needed
-        flowRuleService.getFlowRulesById(appId).forEach(flowRule -> {
-            if (flowRule.deviceId().equals(egress.deviceId()) && flowRule.selector().equals(sBuilder.build())) {
-                flowRule.treatment().allInstructions().forEach(instruction -> {
-                    // If this is an OUTPUT instruction and output is different than existing, add the group
-                    if (instruction.type() == Instruction.Type.OUTPUT &&
-                            !(instruction.equals(Instructions.createOutput(egress.port())))) {
-                        tBuilder.add(instruction);
-                    }
-                });
-            }
-        });
-
-        // FIXME: For efficiency do not send FlowMod again if the new treatment is exactly the same as the existing one
-        FlowRule flowRule = createFlowRule(egress.deviceId(), PRIORITY, sBuilder.build(), tBuilder.build(), 0);
-        flowRuleService.applyFlowRules(flowRule);
-        flowRuleSet.add(flowRule);
-
-        return flowRuleSet;
-    }
-
-    /**
-     * Creates and submits FilteringObjective and ForwardingObjective with INDIRECT groups based on the role of the.
-     * specific device within the path.
-     *
-     * @param serviceId User-provided identifier of the CE service
-     * @param ceVlanId CE-VLAN id of the service, if present
-     * @param vlanId VLAN id of the service
-     * @param ingress ingress connect point at the particular device
-     * @param first indicates whether the current device is the first one in the path
-     * @param last indicates whether the current device is the last one in the path
-     */
-    private void createFilteringForwarding(String serviceId, VlanId ceVlanId, VlanId vlanId,
-                                           ConnectPoint ingress, ConnectPoint egress,
-                                           boolean first, boolean last) {
-
-        createFilteringObjective(ceVlanId, vlanId, ingress, first);
-        createForwardingObjective(serviceId, vlanId, ingress, egress, first, last);
-    }
-
-    /**
-     * Creates and submits FilteringObjective based on the role of the specific device within the path.
-     *
-     * @param ceVlanId the CE-VLAN id of the service, if present
-     * @param vlanId VLAN id of the service
-     * @param ingress ingress connect point at the particular device
-     * @param first indicates whether the current device is the first one in the path
-     */
-    private void createFilteringObjective(VlanId ceVlanId, VlanId vlanId, ConnectPoint ingress, boolean first) {
-
-        FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
-        TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
-
-        fob.withKey(Criteria.matchInPort(ingress.port()));
-        if (first) {
-            // If this is a virtual service, match also on CE-VLAN ID at first hop
-            if (ceVlanId != null) {
-                fob.addCondition(Criteria.matchVlanId(ceVlanId));
-            } else {
-                fob.addCondition(Criteria.matchVlanId(VlanId.NONE));
-            }
-            ttb.pushVlan().setVlanId(vlanId);
-        } else {
-            fob.addCondition(Criteria.matchVlanId(vlanId));
-        }
-
-        fob.withPriority(PRIORITY);
-        fob.withMeta(ttb.build());
-        fob.permit().fromApp(appId);
-
-        flowObjectiveService.filter(ingress.deviceId(), fob.add());
-
-    }
-
-    /**
-     * Creates and submits ForwardingObjective based on the role of the specific device within the path.
-     *
-     * @param serviceId the CE service id
-     * @param vlanId VLAN id of the service
-     * @param ingress ingress connect point at the particular device
-     * @param egress egress connect point at the particular device
-     * @param first indicates whether the current device is the first one in the path
-     * @param last indicates whether the current device is the last one in the path
-     */
-    private void createForwardingObjective(String serviceId, VlanId vlanId, ConnectPoint ingress, ConnectPoint egress,
-                                           boolean first, boolean last) {
-
-        TrafficSelector selector = DefaultTrafficSelector.builder()
-                .matchVlanId(vlanId)
+        TrafficSelector fwdSelector = DefaultTrafficSelector.builder()
+                .matchVlanId(evc.vlanId())
                 .matchInPort(ingress.port())
                 .matchEthType(Ethernet.TYPE_IPV4)
                 .build();
 
-        GroupId groupId = createGroup(serviceId, vlanId, egress, first, last);
+        TrafficTreatment.Builder nextTreatmentBuilder = DefaultTrafficTreatment.builder();
 
-        // Add group to original treatment
-        TrafficTreatment treatment = DefaultTrafficTreatment.builder().group(groupId).build();
+        // If last NI in FC is not UNI, keep the existing S-TAG - it will be translated at the entrance of the next FC
+        if (last && (dstNi instanceof CarrierEthernetUni)) {
+            nextTreatmentBuilder.popVlan();
+        }
+        Instruction outInstruction = Instructions.createOutput(egress.port());
+        nextTreatmentBuilder.add(outInstruction);
+
+        // Check if flow rule with same selector already exists (e.g. when branching in an E-LAN or E-Tree).
+        // If yes, it will be replaced, so we need to include its output in the currently prepared NextObjective
+        Iterator<FlowRule> flowRuleIt = flowRuleService.getFlowRulesById(appId).iterator();
+        while (flowRuleIt.hasNext()) {
+            FlowRule flowRule = flowRuleIt.next();
+            if (flowRule.deviceId().equals(egress.deviceId()) && flowRule.selector().equals(fwdSelector)) {
+                Iterator<Instruction> instructionIt = flowRule.treatment().allInstructions().iterator();
+                while (instructionIt.hasNext()) {
+                    Instruction instruction = instructionIt.next();
+                    // If this is an GROUP instruction and it's different than existing, add it to FlowObjective
+                    if (instruction.type().equals(outInstruction.type()) &&
+                            !(instruction.equals(outInstruction))) {
+                            nextTreatmentBuilder.add(instruction);
+                    }
+                }
+            }
+        }
+
+        Integer nextId = flowObjectiveService.allocateNextId();
+
+        NextObjective nextObjective = DefaultNextObjective.builder()
+                .fromApp(appId)
+                .makePermanent()
+                .withType(NextObjective.Type.SIMPLE)
+                .withPriority(PRIORITY)
+                .withMeta(fwdSelector)
+                .addTreatment(nextTreatmentBuilder.build())
+                .withId(nextId)
+                .add();
 
         ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
                 .fromApp(appId)
                 .makePermanent()
                 .withFlag(ForwardingObjective.Flag.VERSATILE)
                 .withPriority(PRIORITY)
-                .withSelector(selector)
-                .withTreatment(treatment)
+                .withSelector(fwdSelector)
+                .nextStep(nextId)
                 .add();
 
-        flowObjectiveService.forward(ingress.deviceId(), forwardingObjective);
-    }
+        flowObjectiveService.next(egress.deviceId(), nextObjective);
+        // Add all NextObjectives at the end of the list so that they will be removed last
+        flowObjectiveMap.get(evc.id()).addLast(Pair.of(ingress.deviceId(), nextObjective));
 
-    /**
-     * Creates INDIRECT group with single bucket and submits it to GroupService.
-     *
-     * @param serviceId User-provided identifier of the CE service
-     * @param vlanId VLAN id of the service
-     * @param egress egress connect point at the particular device
-     * @param first indicates whether the current device is the first one in the path
-     * @param last indicates whether the current device is the last one in the path
-     * @return The GroupId of the created Group
-     */
-    private GroupId createGroup(String serviceId, VlanId vlanId, ConnectPoint egress, boolean first, boolean last) {
+        flowObjectiveService.forward(egress.deviceId(), forwardingObjective);
+        flowObjectiveMap.get(evc.id()).addFirst(Pair.of(ingress.deviceId(), forwardingObjective));
 
-        checkNotNull(serviceId);
-
-        DeviceId deviceId = egress.deviceId();
-
-        GroupKey groupKey = getGroupKey(vlanId, egress);
-        Group group = groupService.getGroup(deviceId, groupKey);
-        GroupId groupId = getGroupId(vlanId, egress);
-
-        if (group != null) {
-            log.warn("Group {} already exists in {}", groupKey.toString(), deviceId);
-            return groupId;
-        }
-
-        GroupBuckets buckets = getGroupBuckets(egress, last);
-
-        GroupDescription groupDescription = new DefaultGroupDescription(
-                deviceId,
-                GroupDescription.Type.INDIRECT,
-                buckets,
-                groupKey,
-                groupId.id(),
-                appId);
-
-        groupService.addGroup(groupDescription);
-
-        return groupId;
-    }
-
-    /**
-     * Currently creates single group bucket to be used instead of an OUTPUT action.
-     *
-     * @param egress egress connect point at the particular device
-     * @param last indicates whether the current device is the last one in the path
-     * @return GroupBuckets which can be used to create a GroupDescription
-     */
-    private GroupBuckets getGroupBuckets(ConnectPoint egress, boolean last) {
-
-        List<GroupBucket> buckets = Lists.newArrayList();
-
-        TrafficTreatment.Builder treatmentBuilder;
-        if (last) {
-            treatmentBuilder = DefaultTrafficTreatment.builder();
-            treatmentBuilder.popVlan();
-        } else {
-            treatmentBuilder = DefaultTrafficTreatment.builder();
-        }
-        TrafficTreatment treatment = treatmentBuilder.setOutput(egress.port()).build();
-
-        buckets.add(DefaultGroupBucket.createIndirectGroupBucket(treatment));
-
-        return new GroupBuckets(buckets);
-    }
-
-    /**
-     * Returns globally unique group id according to OFDPA 2.0 specification for "L2 Interface" group types.
-     *
-     * @param vlanId VLAN id of the service
-     * @param egress egress connect point at the particular device
-     * @return group id
-     */
-    private GroupId getGroupId(VlanId vlanId, ConnectPoint egress) {
-        return new DefaultGroupId((vlanId.toShort()) << 16 | Integer.parseInt(egress.port().toString()));
-    }
-
-    /**
-     * Returns globally unique group key.
-     *
-     * @param vlanId VLAN id of the service
-     * @param egress egress connect point at the particular device
-     * @return group key
-     */
-    private GroupKey getGroupKey(VlanId vlanId, ConnectPoint egress) {
-        //TODO: Create GroupKey in a better way - perhaps the same as GroupId (unique per device)
-        return new DefaultGroupKey(Integer.toString(Objects.hash(egress.deviceId(), egress.port(), vlanId)).getBytes());
+        // FIXME: For efficiency do not send FlowObjective again if new treatment is exactly the same as existing one
     }
 
     @Override
-    void applyBandwidthProfileResources(String serviceId, CarrierEthernetUni uni) {
-
-        log.info("Trying to apply BW profile resources for service {}", serviceId);
+    void applyBandwidthProfileResources(CarrierEthernetVirtualConnection evc, CarrierEthernetUni uni) {
 
         Dpid dpid = Dpid.dpid(uni.cp().deviceId().uri());
         OpenFlowSwitch sw = controller.getSwitch(dpid);
 
-        // FIXME: Temporary hack: Do not apply meters to OFDPA2.0 switches
+        // Do not apply meters to OFDPA 2.0 switches since they are not currently supported
         if (sw.softwareDescription().equals("OF-DPA 2.0")) {
             return;
         }
 
         // Create meters and add them to global MeterId map
-        Set<DeviceMeterId> deviceMeterIdSet = deviceMeterIdMap.get(serviceId);
+        Set<DeviceMeterId> deviceMeterIdSet = deviceMeterIdMap.get(evc.id());
         if (deviceMeterIdSet == null) {
             deviceMeterIdSet = new HashSet<>();
         }
         deviceMeterIdSet.addAll(createMeters(uni));
-        deviceMeterIdMap.put(serviceId, deviceMeterIdSet);
+        deviceMeterIdMap.put(evc.id(), deviceMeterIdSet);
 
         // Apply meters to already installed flows
 
         Set<FlowRule> newFlowRuleSet = new HashSet<>();
 
         // Get flow rules belonging to service and having as in_port the UNI connect point
-        flowRuleMap.get(serviceId).forEach(flowRule -> {
-            PortNumber inPort = ((PortCriterion) flowRule.selector().getCriterion(Criterion.Type.IN_PORT)).port();
+
+        // FIXME: Check for flow rules associated with evcId
+        for (FlowRule flowRule : flowRuleService.getFlowRulesById(appId)) {
+            PortCriterion portCriterion = (PortCriterion) flowRule.selector().getCriterion(Criterion.Type.IN_PORT);
+            VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) flowRule.selector()
+                    .getCriterion(Criterion.Type.VLAN_VID);
+            if (portCriterion == null || vlanIdCriterion == null) {
+                continue;
+            }
+            PortNumber inPort = portCriterion.port();
+            VlanId flowInVlanId = vlanIdCriterion.vlanId();
+            if (inPort == null || flowInVlanId == null) {
+                continue;
+            }
             ConnectPoint flowInCp = new ConnectPoint(flowRule.deviceId(), inPort);
-            //VlanId flowInVlanId = ((VlanIdCriterion) flowRule.selector().
-            //        getCriterion(Criterion.Type.VLAN_VID)).vlanId();
-            // TODO: Compare also to the CE-VLAN ID (if it is not null)
-            // FIXME: Maybe check only in_port, vlanid, and if there is output port or group action?
-            if (uni.cp().equals(flowInCp)) {
-            //if (uni.cp().equals(flowInCp) && (uni.ceVlanId() == null || uni.ceVlanId().equals(flowInVlanId))) {
+            // FIXME: Maybe check also if there is a group action?
+            if (uni.cp().equals(flowInCp) && evc.vlanId().equals(flowInVlanId)) {
                 // Need to add to the flow the meters associated with the same device
                 Set<DeviceMeterId> tmpDeviceMeterIdSet = new HashSet<>();
-                deviceMeterIdMap.get(serviceId).forEach(deviceMeterId -> {
+                deviceMeterIdMap.get(evc.id()).forEach(deviceMeterId -> {
                     if (deviceMeterId.deviceId().equals(flowRule.deviceId())) {
                         tmpDeviceMeterIdSet.add(deviceMeterId);
                     }
@@ -672,9 +276,7 @@
             } else {
                 newFlowRuleSet.add(flowRule);
             }
-        });
-
-        flowRuleMap.put(serviceId, newFlowRuleSet);
+        }
     }
 
     /**
@@ -747,24 +349,14 @@
         return deviceMeterIdSet;
     }
 
-    private FlowRule addMeterToFlowRule(FlowRule flowRule, DeviceMeterId deviceMeterId) {
-
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
-                .builder(flowRule.treatment());
-
-        tBuilder.add(Instructions.meterTraffic(deviceMeterId.meterId()));
-
-        return createFlowRule(flowRule.deviceId(), flowRule.priority(),
-                flowRule.selector(), tBuilder.build(), flowRule.tableId());
-    }
-
     private FlowRule addMetersToFlowRule(FlowRule flowRule, Set<DeviceMeterId> deviceMeterIdSet) {
 
+        // FIXME: Refactor to use only single meter
+
         TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
                 .builder(flowRule.treatment());
 
         deviceMeterIdSet.forEach(deviceMeterId -> {
-            //tBuilder.add(Instructions.meterTraffic(deviceMeterId.meterId()));
             tBuilder.meter(deviceMeterId.meterId());
         });
 
@@ -815,59 +407,28 @@
     }
 
     @Override
-    void removeAllForwardingResources(CarrierEthernetVirtualConnection service) {
-        removeFlowRules(service.id());
-        removeGroups(service);
+    void removeAllForwardingResources(CarrierEthernetVirtualConnection evc) {
+        removeFlowObjectives(evc.id());
     }
 
     /**
-     * Removes all flow rules installed by the application which are associated with a specific CE service.
+     * Removes all flow objectives installed by the application which are associated with a specific EVC.
      *
-     * @param serviceId the CE service id
+     * @param evcId the EVC id
      * */
-    private void removeFlowRules(String serviceId) {
+    private void removeFlowObjectives(String evcId) {
         // Note: A Flow Rule cannot be shared by multiple services due to different VLAN or CE-VLAN ID match.
-        Set<FlowRule> flowRuleSet = flowRuleMap.remove(serviceId);
-        flowRuleSet.forEach(flowRule -> flowRuleService.removeFlowRules(flowRule));
+        List<Pair<DeviceId, Objective>> flowObjectiveList = flowObjectiveMap.remove(evcId);
+        // NextObjectives will be removed after all other Objectives
+        ListIterator<Pair<DeviceId, Objective>> objIter = flowObjectiveList.listIterator();
+        while (objIter.hasNext()) {
+            Pair<DeviceId, Objective> deviceObjectivePair = objIter.next();
+            flowObjectiveService.apply(deviceObjectivePair.getLeft(), deviceObjectivePair.getRight().copy().remove());
+        }
+
     }
 
-    /**
-     * Removes all groups installed by the application which are associated with a specific CE service.
-     *
-     * @param service the CE service definition
-     * */
-    // Note: A Group cannot be shared by multiple services since GroupIds/GroupKeys include the service VLAN ID
-    private void removeGroups(CarrierEthernetVirtualConnection service) {
-
-        Set<ConnectPoint> egressCpSet = egressCpMap.remove(service.id());
-        Set<ConnectPoint> uniCpSet = new HashSet<>();
-
-        service.uniSet().forEach(uni -> uniCpSet.add(uni.cp()));
-
-        egressCpSet.forEach(egress -> {
-            // The connect points associated with UNIs are the ones including the VLAN pop commands, i.e. the "last"
-            boolean last = (uniCpSet.contains(egress));
-            DeviceId deviceId = egress.deviceId();
-            GroupKey groupKey = getGroupKey(service.vlanId(), egress);
-            GroupBuckets buckets = getGroupBuckets(egress, last);
-            if (groupService.getGroup(deviceId, groupKey) != null) {
-                // Note: Removing buckets before removing group in CpQD causes warnings (but is needed in OFDPA2.0)
-                Dpid dpid = Dpid.dpid(deviceId.uri());
-                OpenFlowSwitch sw = controller.getSwitch(dpid);
-                if (sw.softwareDescription().equals("OF-DPA 2.0")) {
-                    groupService.removeBucketsFromGroup(
-                            deviceId,
-                            groupKey,
-                            buckets,
-                            groupKey,
-                            appId);
-                }
-                log.info("Trying to remove group with key {} from {}", groupKey, deviceId);
-                groupService.removeGroup(deviceId, groupKey, appId);
-            }
-        });
-    }
-
+    // FIXME: Replace with Pair<DeviceId, MeterId>
     /**
      * Utility class to compensate for the fact that MeterIds are not unique system-wide.
      * */
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetPacketNodeManager.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetPacketNodeManager.java
index c833df5..5b68d36 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetPacketNodeManager.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetPacketNodeManager.java
@@ -26,7 +26,7 @@
                                     CarrierEthernetNetworkInterface dstNi, ConnectPoint ingress, ConnectPoint egress,
                                     boolean first, boolean last);
 
-    abstract void applyBandwidthProfileResources(String serviceId, CarrierEthernetUni uni);
+    abstract void applyBandwidthProfileResources(CarrierEthernetVirtualConnection service, CarrierEthernetUni uni);
 
     abstract void removeBandwidthProfileResources(String serviceId, CarrierEthernetUni uni);
 
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetPacketProvisioner.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetPacketProvisioner.java
index f4ae5e1..38cd370 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetPacketProvisioner.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetPacketProvisioner.java
@@ -15,7 +15,6 @@
  */
 package org.onosproject.ecord.carrierethernet.app;
 
-import com.google.common.collect.ImmutableList;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
@@ -27,9 +26,6 @@
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.Path;
 import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.intent.Constraint;
-import org.onosproject.net.intent.constraint.BandwidthConstraint;
-import org.onosproject.net.intent.constraint.LatencyConstraint;
 import org.onosproject.net.topology.PathService;
 import org.slf4j.Logger;
 
@@ -64,7 +60,6 @@
     }
 
     // TODO: Get LTPs as input
-    // TODO: setNodeForwarding should be able to decide what to do depending on if some of the LTPs is a UNI
     public boolean setupConnectivity(CarrierEthernetNetworkInterface ni1, CarrierEthernetNetworkInterface ni2, CarrierEthernetVirtualConnection service) {
 
         // Find the paths for both directions at the same time, so that we can skip the pair if needed
@@ -164,29 +159,29 @@
         return i == 0;
     }
 
-    public void removeConnectivity(CarrierEthernetVirtualConnection service) {
+    public void removeConnectivity(CarrierEthernetVirtualConnection evc) {
         // TODO: Add here the same call for all node manager types
-        ceOfPktNodeManager.removeAllForwardingResources(service);
+        ceOfPktNodeManager.removeAllForwardingResources(evc);
     }
 
     /**
-     * Applies bandwidth profiles to the UNIs of a service.
+     * Applies bandwidth profiles to the UNIs of an EVC.
      *
-     * @param service the CE service definition
+     * @param evc the EVC representation
      */
-    public void applyBandwidthProfiles(CarrierEthernetVirtualConnection service) {
+    public void applyBandwidthProfiles(CarrierEthernetVirtualConnection evc) {
         //  TODO: Select node manager depending on device protocol
-        service.uniSet().forEach(uni -> ceOfPktNodeManager.applyBandwidthProfileResources(service.id(), uni));
+        evc.uniSet().forEach(uni -> ceOfPktNodeManager.applyBandwidthProfileResources(evc, uni));
     }
 
     /**
-     * Removes bandwidth profiles from the UNIs of a service.
+     * Removes bandwidth profiles from the UNIs of an ECV.
      *
-     * @param service the CE service definition
+     * @param evc the EVC representation
      */
-    public void removeBandwidthProfiles(CarrierEthernetVirtualConnection service) {
+    public void removeBandwidthProfiles(CarrierEthernetVirtualConnection evc) {
         //  TODO: Select node manager depending on device protocol
-        service.uniSet().forEach(uni -> ceOfPktNodeManager.removeBandwidthProfileResources(service.id(), uni));
+        evc.uniSet().forEach(uni -> ceOfPktNodeManager.removeBandwidthProfileResources(evc.id(), uni));
     }
 
 }
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetUni.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetUni.java
index a625e57..2071c8f 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetUni.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetUni.java
@@ -110,7 +110,7 @@
     public void addEvcUni(CarrierEthernetUni uni) {
 
         // Add CE-VLAN ID
-        if (uni.ceVlanId() != null) {
+        if (uni.ceVlanId() != VlanId.NONE) {
             this.ceVlanIdSet.add(uni.ceVlanId());
         }
 
@@ -145,7 +145,7 @@
     public void removeEvcUni(CarrierEthernetUni uni) {
 
         // Remove UNI CE-VLAN ID
-        if (uni.ceVlanId() != null) {
+        if (uni.ceVlanId() != VlanId.NONE) {
             ceVlanIdSet.remove(uni.ceVlanId());
         }
 
@@ -167,7 +167,7 @@
     public boolean validateEvcUni(CarrierEthernetUni uni) {
 
         // Check if the CE-VLAN ID of the UNI is already included in global UNI
-        if (uni.ceVlanId() != null) {
+        if (uni.ceVlanId() != VlanId.NONE) {
             if (ceVlanIdSet.contains(uni.ceVlanId())) {
                 log.error("CE-VLAN ID {} already exists in UNI {}", uni.ceVlanId().toString(), this.id());
                 return false;
@@ -219,13 +219,23 @@
      */
     public VlanId ceVlanId() {
         if (ceVlanIdSet.isEmpty()) {
-            return null;
+            return VlanId.NONE;
         } else {
             return ceVlanIdSet.iterator().next();
         }
     }
 
     /**
+     * Always returns null, since S-TAGs are not associated with UNIs.
+     *
+     * @return null
+     */
+    @Override
+    public VlanId sVlanId() {
+        return null;
+    }
+
+    /**
      * Returns the set of CE-VLAN ids associated with the UNI.
      *
      * @return CE-VLAN id set