Fixes [ONOS-5415] and [ONOS-5491]

Changes:
- Introduces ordering between core ports and egress ports;
- Introduces ordering between egress ports;
- Introduces ttl_in and tll_out in the link collection compiler logic;
- Optimized push/pop mpls/vlan actions;
- Optimizes dec ttl related actions;
- Optimizes ttl_in and ttl_out actions;
- Makes the optimizations configurable;

Change-Id: I60f187031e89c366ec9c79d1652a5fb0da96c206
diff --git a/core/api/src/test/java/org/onosproject/net/NetTestTools.java b/core/api/src/test/java/org/onosproject/net/NetTestTools.java
index 842c86d..d6227a7 100644
--- a/core/api/src/test/java/org/onosproject/net/NetTestTools.java
+++ b/core/api/src/test/java/org/onosproject/net/NetTestTools.java
@@ -258,4 +258,17 @@
         );
     }
 
+    /**
+     * Builds a treatment which contains the dec ttl
+     * actions.
+     *
+     * @return the treatment
+     */
+    public static TrafficTreatment decTtlTreatment() {
+        return DefaultTrafficTreatment.builder()
+                .decMplsTtl()
+                .decNwTtl()
+                .build();
+    }
+
 }
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/IntentConfigurableRegistrator.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/IntentConfigurableRegistrator.java
index 38be5e4..40f7f77 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/IntentConfigurableRegistrator.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/IntentConfigurableRegistrator.java
@@ -67,6 +67,18 @@
             label = "Defines the label selection algorithm - RANDOM or FIRST_FIT")
     private String labelSelection = DEFAULT_LABEL_SELECTION;
 
+    private static final boolean DEFAULT_FLOW_OPTIMIZATION = false;
+    @Property(name = "useFlowOptimization",
+            boolValue = DEFAULT_FLOW_OPTIMIZATION,
+            label = "Indicates whether or not to optimize the flows in the link collection compiler")
+    private boolean useFlowOptimization = DEFAULT_FLOW_OPTIMIZATION;
+
+    private static final boolean DEFAULT_COPY_TTL = false;
+    @Property(name = "useCopyTtl",
+            boolValue = DEFAULT_COPY_TTL,
+            label = "Indicates whether or not to use copy ttl in the link collection compiler")
+    private boolean useCopyTtl = DEFAULT_COPY_TTL;
+
     private final Map<Class<Intent>, IntentCompiler<Intent>> flowRuleBased = Maps.newConcurrentMap();
     private final Map<Class<Intent>, IntentCompiler<Intent>> flowObjectiveBased = Maps.newConcurrentMap();
 
@@ -87,6 +99,8 @@
         if (context == null) {
             log.info("Settings: useFlowObjectives={}", useFlowObjectives);
             log.info("Settings: labelSelection={}", labelSelection);
+            log.info("Settings: useFlowOptimization={}", useFlowOptimization);
+            log.info("Settings: useCopyTtl={}", useCopyTtl);
             return;
         }
 
@@ -117,6 +131,34 @@
             changeLabelSelections();
             log.info("Settings: labelSelection={}", labelSelection);
         }
+
+        boolean newFlowOptimization;
+        try {
+            String s = Tools.get(context.getProperties(), "useFlowOptimization");
+            newFlowOptimization = isNullOrEmpty(s) ? useFlowOptimization : Boolean.parseBoolean(s.trim());
+        } catch (ClassCastException e) {
+            newFlowOptimization = useFlowOptimization;
+        }
+
+        if (useFlowOptimization != newFlowOptimization) {
+            useFlowOptimization = newFlowOptimization;
+            changeFlowOptimization();
+            log.info("Settings: useFlowOptimization={}", useFlowOptimization);
+        }
+
+        boolean newCopyTtl;
+        try {
+            String s = Tools.get(context.getProperties(), "useCopyTtl");
+            newCopyTtl = isNullOrEmpty(s) ? useCopyTtl : Boolean.parseBoolean(s.trim());
+        } catch (ClassCastException e) {
+            newCopyTtl = useCopyTtl;
+        }
+
+        if (useCopyTtl != newCopyTtl) {
+            useCopyTtl = newCopyTtl;
+            changeCopyTtl();
+            log.info("Settings: useCopyTtl={}", useCopyTtl);
+        }
     }
 
     /**
@@ -173,4 +215,12 @@
         LinkCollectionCompiler.labelAllocator.setLabelSelection(labelSelection);
     }
 
+    private void changeFlowOptimization() {
+        LinkCollectionCompiler.optimize = useFlowOptimization;
+    }
+
+    private void changeCopyTtl() {
+        LinkCollectionCompiler.copyTtl = useCopyTtl;
+    }
+
 }
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionCompiler.java
index d5df116..2176a20 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionCompiler.java
@@ -16,6 +16,7 @@
 
 package org.onosproject.net.intent.impl.compiler;
 
+import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.SetMultimap;
 import com.google.common.collect.Sets;
@@ -29,9 +30,9 @@
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.EncapsulationType;
+import org.onosproject.net.FilteredConnectPoint;
 import org.onosproject.net.Link;
 import org.onosproject.net.PortNumber;
-import org.onosproject.net.FilteredConnectPoint;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficSelector;
@@ -47,17 +48,17 @@
 import org.onosproject.net.flow.instructions.L1ModificationInstruction;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsBosInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModTunnelIdInstruction;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanPcpInstruction;
-import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction;
-import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsBosInstruction;
-import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModTunnelIdInstruction;
 import org.onosproject.net.flow.instructions.L3ModificationInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpEthInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpIPInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpOpInstruction;
 import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPInstruction;
 import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPv6FlowLabelInstruction;
-import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpIPInstruction;
-import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpEthInstruction;
-import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpOpInstruction;
 import org.onosproject.net.flow.instructions.L4ModificationInstruction;
 import org.onosproject.net.flow.instructions.L4ModificationInstruction.ModTransportPortInstruction;
 import org.onosproject.net.intent.IntentCompilationException;
@@ -69,10 +70,9 @@
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.stream.Collectors;
 
-import static org.onosproject.net.flow.criteria.Criterion.Type.MPLS_LABEL;
-import static org.onosproject.net.flow.criteria.Criterion.Type.TUNNEL_ID;
-import static org.onosproject.net.flow.criteria.Criterion.Type.VLAN_VID;
+import static org.onosproject.net.flow.criteria.Criterion.Type.*;
 
 /**
  * Shared APIs and implementations for Link Collection compilers.
@@ -85,6 +85,18 @@
     static LabelAllocator labelAllocator;
 
     /**
+     * Influence compiler behavior. If true the compiler
+     * try to optimize the chain of the actions.
+     */
+    static boolean optimize;
+
+    /**
+     * Influence compiler behavior. If true the compiler
+     * try to optimize the copy ttl actions.
+     */
+    static boolean copyTtl;
+
+    /**
      * The allowed tag criterions.
      */
     private static final Set<Criterion.Type> TAG_CRITERION_TYPES =
@@ -248,6 +260,151 @@
     }
 
     /**
+     * Helper method to generate the egress actions.
+     *
+     * @param treatmentBuilder the treatment builder to update
+     * @param egressPoints the egress points
+     * @param initialState the initial state of the transition
+     */
+    private void generateEgressActions(TrafficTreatment.Builder treatmentBuilder,
+                                       List<FilteredConnectPoint> egressPoints,
+                                       TrafficSelector initialState,
+                                       LinkCollectionIntent intent) {
+
+        TrafficSelector prevState = initialState;
+        for (FilteredConnectPoint egressPoint : egressPoints) {
+            /*
+             * If we are at the egress, we have to transit to the final
+             * state. First we add the Intent treatment.
+             */
+            intent.treatment().allInstructions().stream()
+                    .filter(inst -> inst.type() != Instruction.Type.NOACTION)
+                    .forEach(treatmentBuilder::add);
+            /*
+             * We generate the transition FIP->FEP.
+             */
+            TrafficTreatment forwardingTreatment =
+                    forwardingTreatment(prevState,
+                                        egressPoint.trafficSelector(),
+                                        getEthType(intent.selector()));
+            /*
+             * We add the instruction necessary to the transition.
+             * Potentially we override the intent treatment.
+             */
+            forwardingTreatment.allInstructions().stream()
+                    .filter(inst -> inst.type() != Instruction.Type.NOACTION)
+                    .forEach(treatmentBuilder::add);
+            /*
+             * Finally we set the output action.
+             */
+            treatmentBuilder.setOutput(egressPoint.connectPoint().port());
+            if (optimize) {
+                /*
+                 * We update the previous state. In this way instead of
+                 * transiting from FIP->FEP we do FEP->FEP and so on.
+                 */
+                prevState = egressPoint.trafficSelector();
+            }
+        }
+
+    }
+
+    /**
+     * Helper method to order the egress ports according to a
+     * specified criteria. The idea is to generate first the actions
+     * for the egress ports which are similar to the specified criteria
+     * then the others. In this way we can mitigate the problems related
+     * to the chain of actions and we can optimize also the number of
+     * actions.
+     *
+     * @param orderCriteria the ordering criteria
+     * @param pointsToOrder the egress points to order
+     * @return a list of port ordered
+     */
+    private List<FilteredConnectPoint> orderedEgressPoints(TrafficSelector orderCriteria,
+                                        List<FilteredConnectPoint> pointsToOrder) {
+        /*
+         * We are interested only to the labels. The idea is to order
+         * by the tags.
+         *
+         */
+        Criterion vlanIdCriterion = orderCriteria.getCriterion(VLAN_VID);
+        Criterion mplsLabelCriterion = orderCriteria.getCriterion(MPLS_LABEL);
+        /*
+         * We collect all the untagged points.
+         *
+         */
+        List<FilteredConnectPoint> untaggedEgressPoints = pointsToOrder
+                .stream()
+                .filter(pointToOrder -> {
+                    TrafficSelector selector = pointToOrder.trafficSelector();
+                    return selector.getCriterion(VLAN_VID) == null &&
+                            selector.getCriterion(MPLS_LABEL) == null;
+                }).collect(Collectors.toList());
+        /*
+         * We collect all the vlan points.
+         */
+        List<FilteredConnectPoint> vlanEgressPoints = pointsToOrder
+                .stream()
+                .filter(pointToOrder -> {
+                    TrafficSelector selector = pointToOrder.trafficSelector();
+                    return selector.getCriterion(VLAN_VID) != null &&
+                            selector.getCriterion(MPLS_LABEL) == null;
+                }).collect(Collectors.toList());
+        /*
+         * We collect all the mpls points.
+         */
+        List<FilteredConnectPoint> mplsEgressPoints = pointsToOrder
+                .stream()
+                .filter(pointToOrder -> {
+                    TrafficSelector selector = pointToOrder.trafficSelector();
+                    return selector.getCriterion(VLAN_VID) == null &&
+                            selector.getCriterion(MPLS_LABEL) != null;
+                }).collect(Collectors.toList());
+        /*
+         * We create the final list of ports.
+         */
+        List<FilteredConnectPoint> orderedList = Lists.newArrayList();
+        /*
+         * The ordering criteria is vlan id. First we add the vlan
+         * ports. Then the others.
+         */
+        if (vlanIdCriterion != null && mplsLabelCriterion == null) {
+            orderedList.addAll(vlanEgressPoints);
+            orderedList.addAll(untaggedEgressPoints);
+            orderedList.addAll(mplsEgressPoints);
+            return orderedList;
+        }
+        /*
+         * The ordering criteria is mpls label. First we add the mpls
+         * ports. Then the others.
+         */
+        if (vlanIdCriterion == null && mplsLabelCriterion != null) {
+            orderedList.addAll(mplsEgressPoints);
+            orderedList.addAll(untaggedEgressPoints);
+            orderedList.addAll(vlanEgressPoints);
+            return orderedList;
+        }
+        /*
+         * The ordering criteria is untagged. First we add the untagged
+         * ports. Then the others.
+         */
+        if (vlanIdCriterion == null && mplsLabelCriterion == null) {
+            orderedList.addAll(untaggedEgressPoints);
+            orderedList.addAll(vlanEgressPoints);
+            orderedList.addAll(mplsEgressPoints);
+            return orderedList;
+        }
+        /*
+         * Unhandled scenario.
+         */
+        orderedList.addAll(vlanEgressPoints);
+        orderedList.addAll(mplsEgressPoints);
+        orderedList.addAll(untaggedEgressPoints);
+        return orderedList;
+    }
+
+    /**
      * Manages the Intents with a single ingress point (p2p, sp2mp)
      * creating properly the selector builder and the treatment builder.
      *
@@ -285,42 +442,32 @@
                 .forEach(selectorBuilder::add);
         /*
          * In this scenario, potentially we can have several output
-         * ports.
+         * ports. First we have to insert in the treatment the actions
+         * for the core.
          */
+        List<FilteredConnectPoint> egressPoints = Lists.newArrayList();
         for (PortNumber outPort : outPorts) {
             Optional<FilteredConnectPoint> filteredEgressPoint =
                     getFilteredConnectPointFromIntent(deviceId, outPort, intent);
-            /*
-             * If we are at the egress, we have to transit to the final
-             * state.
-             */
-            if (filteredEgressPoint.isPresent()) {
-                /*
-                 * We add the Intent treatment.
-                 */
-                intent.treatment().allInstructions().stream()
-                        .filter(inst -> inst.type() != Instruction.Type.NOACTION)
-                        .forEach(treatmentBuilder::add);
-                /*
-                 * We generate the transition FIP->FEP.
-                 */
-                TrafficTreatment forwardingTreatment =
-                        forwardingTreatment(filteredIngressPoint.get().trafficSelector(),
-                                            filteredEgressPoint.get().trafficSelector(),
-                                            getEthType(intent.selector()));
-                /*
-                 * We add the instruction necessary to the transition.
-                 * Potentially we override the intent treatment.
-                 */
-                forwardingTreatment.allInstructions().stream()
-                        .filter(inst -> inst.type() != Instruction.Type.NOACTION)
-                        .forEach(treatmentBuilder::add);
+            if (!filteredEgressPoint.isPresent()) {
+                treatmentBuilder.setOutput(outPort);
+            } else {
+                egressPoints.add(filteredEgressPoint.get());
             }
-            /*
-             * Finally we set the output action.
-             */
-            treatmentBuilder.setOutput(outPort);
         }
+        /*
+         * The idea is to order the egress points. Before we deal
+         * with the egress points which looks like similar to the ingress
+         * point then the others.
+         */
+        TrafficSelector prevState = filteredIngressPoint.get().trafficSelector();
+        if (optimize) {
+            egressPoints = orderedEgressPoints(prevState, egressPoints);
+        }
+        /*
+         * Then we deal with the egress points.
+         */
+        generateEgressActions(treatmentBuilder, egressPoints, prevState, intent);
     }
 
     /**
@@ -605,9 +752,11 @@
         );
         /*
          * We need to order the actions. First the actions
-         * related to the not-egress points.
+         * related to the not-egress points. At the same time we collect
+         * also the egress points.
          */
-        outPorts.forEach(outPort -> {
+        List<FilteredConnectPoint> egressPoints = Lists.newArrayList();
+        for (PortNumber outPort : outPorts) {
             Optional<FilteredConnectPoint> filteredEgressPoint =
                     getFilteredConnectPointFromIntent(deviceId, outPort, intent);
             if (!filteredEgressPoint.isPresent()) {
@@ -651,45 +800,25 @@
                  * Finally we set the output action.
                  */
                 treatmentBuilder.setOutput(outPort);
+            } else {
+                egressPoints.add(filteredEgressPoint.get());
             }
-
-        });
+        }
+        /*
+         * The idea is to order the egress points. Before we deal
+         * with the egress points which looks like similar to the
+         * selector derived from the encpsulation constraint then
+         * the others.
+         */
+        TrafficSelector prevState = selectorBuilder.build();
+        if (optimize) {
+            egressPoints = orderedEgressPoints(prevState, egressPoints);
+        }
         /*
          * In this case, we have to transit to the final
          * state.
          */
-        outPorts.forEach(outPort -> {
-            Optional<FilteredConnectPoint> filteredEgressPoint =
-                    getFilteredConnectPointFromIntent(deviceId, outPort, intent);
-            if (filteredEgressPoint.isPresent()) {
-                /*
-                 * We add the Intent treatment to the final
-                 * treatment.
-                 */
-                intent.treatment().allInstructions().stream()
-                        .filter(inst -> inst.type() != Instruction.Type.NOACTION)
-                        .forEach(treatmentBuilder::add);
-                /*
-                 * We generate the transition FIP->FEP.
-                 */
-                TrafficTreatment forwardingTreatment =
-                        forwardingTreatment(selectorBuilder.build(),
-                                            filteredEgressPoint.get().trafficSelector(),
-                                            getEthType(intent.selector()));
-                /*
-                 * We add the instruction necessary to the transition.
-                 * Potentially we override the intent treatment.
-                 */
-                forwardingTreatment.allInstructions().stream()
-                        .filter(inst -> inst.type() != Instruction.Type.NOACTION)
-                        .forEach(treatmentBuilder::add);
-                /*
-                 * Finally we set the output action.
-                 */
-                treatmentBuilder.setOutput(outPort);
-            }
-        });
-
+        generateEgressActions(treatmentBuilder, egressPoints, prevState, intent);
     }
 
     /**
@@ -891,6 +1020,9 @@
                     break;
 
                 case MPLS_LABEL:
+                    if (copyTtl) {
+                        builder.copyTtlIn();
+                    }
                     builder.popMpls(ethType);
                     break;
 
@@ -909,6 +1041,9 @@
 
                 case MPLS_LABEL:
                     builder.pushMpls();
+                    if (copyTtl) {
+                        builder.copyTtlOut();
+                    }
                     break;
 
                 default:
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompiler.java
index e464e99..d93764b 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompiler.java
@@ -30,9 +30,16 @@
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction;
 import org.onosproject.net.intent.FlowRuleIntent;
 import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentCompilationException;
 import org.onosproject.net.intent.IntentCompiler;
 import org.onosproject.net.intent.LinkCollectionIntent;
 import org.onosproject.net.intent.constraint.EncapsulationConstraint;
@@ -48,6 +55,8 @@
 import java.util.Optional;
 import java.util.Set;
 
+import static org.onosproject.net.flow.instructions.Instruction.Type.NOACTION;
+
 /**
  * Compiler to produce flow rules from link collections.
  */
@@ -56,6 +65,9 @@
         extends LinkCollectionCompiler<FlowRule>
         implements IntentCompiler<LinkCollectionIntent> {
 
+    private static final String UNKNOWN_INSTRUCTION = "Unknown instruction type";
+    private static final String UNSUPPORTED_INSTRUCTION = "Unsupported %s instruction";
+
     private static Logger log = LoggerFactory.getLogger(LinkCollectionIntentCompiler.class);
 
 
@@ -138,6 +150,11 @@
                         labels
                 );
 
+                if (optimize) {
+                    TrafficTreatment compactedTreatment = compactActions(instructions.treatment());
+                    instructions = new ForwardingInstructions(compactedTreatment, instructions.selector());
+                }
+
                 FlowRule rule = DefaultFlowRule.builder()
                         .forDevice(deviceId)
                         .withSelector(instructions.selector())
@@ -152,4 +169,204 @@
 
         return rules;
     }
+
+    /**
+     * This method tries to optimize the chain of actions.
+     *
+     * @param oldTreatment the list of instructions to optimize
+     * @return the optimized set of actions
+     */
+    private TrafficTreatment compactActions(TrafficTreatment oldTreatment) {
+
+        TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
+        Instruction instruction;
+        Instruction newInstruction;
+
+        for (int index = 0; index < oldTreatment.allInstructions().size(); index++) {
+            instruction = oldTreatment.allInstructions().get(index);
+            /*
+             * if the action is not optimizable. We simply add
+             * to the builder.
+             */
+            if (checkInstruction(instruction)) {
+                treatmentBuilder.add(instruction);
+                continue;
+            }
+            /*
+             * We try to run an optimization;
+             */
+            newInstruction = optimizeInstruction(index, instruction, oldTreatment.allInstructions());
+            if (!newInstruction.type().equals(NOACTION)) {
+                treatmentBuilder.add(newInstruction);
+            }
+        }
+
+        return treatmentBuilder.build();
+    }
+
+    /**
+     * Verifies if the given L2 instruction can be optimized.
+     *
+     * @param l2instruction the l2 instruction to verify
+     * @return true if the instruction cannot be optimized. False otherwise
+     */
+    private boolean checkL2Instructions(L2ModificationInstruction l2instruction) {
+        switch (l2instruction.subtype()) {
+            /*
+             * These actions can be performed safely.
+             */
+            case ETH_SRC:
+            case ETH_DST:
+            case VLAN_ID:
+            case VLAN_PCP:
+            case MPLS_LABEL:
+            case MPLS_BOS:
+            case TUNNEL_ID:
+            case VLAN_PUSH:
+            case VLAN_POP:
+            case MPLS_PUSH:
+            case MPLS_POP:
+                return true;
+            /*
+             * We should avoid dec mpls ttl multiple
+             * times.
+             */
+            case DEC_MPLS_TTL:
+                return false;
+
+            default:
+                throw new IntentCompilationException(String.format(UNSUPPORTED_INSTRUCTION, "L2"));
+        }
+
+    }
+
+    /**
+     * Verifies if the given L3 instruction can be optimized.
+     *
+     * @param l3instruction the l3 instruction to verify
+     * @return true if the instruction cannot be optimized. False otherwise
+     */
+    private boolean checkL3Instructions(L3ModificationInstruction l3instruction) {
+        switch (l3instruction.subtype()) {
+            /*
+             * These actions can be performed several times.
+             */
+            case IPV4_SRC:
+            case IPV4_DST:
+            case IPV6_SRC:
+            case IPV6_DST:
+            case IPV6_FLABEL:
+            case ARP_SPA:
+            case ARP_SHA:
+            case ARP_OP:
+            case TTL_OUT:
+            case TTL_IN:
+                return true;
+            /*
+             * This action should be executed one time;
+             */
+            case DEC_TTL:
+                return false;
+            default:
+                throw new IntentCompilationException(String.format(UNSUPPORTED_INSTRUCTION, "L3"));
+        }
+    }
+
+    /**
+     * Helper method to handle the optimization of the ttl instructions.
+     *
+     * @param index the index of the instruction
+     * @param instruction the instruction to optimize
+     * @param instructions the list of instructions to optimize
+     * @return no action if the action can be removed. The same instruction
+     *         if we have to perform it
+     */
+    private Instruction optimizeTtlInstructions(int index, Instruction instruction, List<Instruction> instructions) {
+        /**
+         * Here we handle the optimization of decrement mpls ttl. The optimization
+         * is to come back to the start of the list looking for the same
+         * action. If we find the same action, we can optimize.
+         */
+        Instruction currentInstruction;
+        for (int i = index - 1; i >= 0; i--) {
+            currentInstruction = instructions.get(i);
+            if (currentInstruction.equals(instruction)) {
+                return Instructions.createNoAction();
+
+            }
+        }
+        return instruction;
+    }
+
+    /**
+     * Helper method to handle the optimization of the instructions.
+     *
+     * @param index the index of the instruction
+     * @param instruction the instruction to optimize
+     * @param instructions the list of instructions to optimize
+     * @return no action if the action can be removed. The same instruction
+     *         if we have to perform it
+     */
+    private Instruction optimizeInstruction(int index, Instruction instruction, List<Instruction> instructions) {
+
+        switch (instruction.type()) {
+            /*
+             * Here we have the chance to optimize the dec mpls ttl action.
+             */
+            case L2MODIFICATION:
+            /*
+             * Here we have the chance to optimize the ttl related actions.
+             */
+            case L3MODIFICATION:
+                return optimizeTtlInstructions(index, instruction, instructions);
+
+            default:
+                throw new IntentCompilationException(UNKNOWN_INSTRUCTION);
+
+        }
+
+    }
+
+    /**
+     * Helper method to verify if the instruction can be optimized.
+     *
+     * @param instruction the instruction to verify
+     * @return true if the action can be optimized. False otherwise.
+     */
+    private boolean checkInstruction(Instruction instruction) {
+
+        switch (instruction.type()) {
+            /*
+             * The following instructions are not supported.
+             */
+            case L0MODIFICATION:
+            case L1MODIFICATION:
+            case L4MODIFICATION:
+            case NOACTION:
+            case OUTPUT:
+            case GROUP:
+            case QUEUE:
+            case TABLE:
+            case METER:
+            case METADATA:
+            case EXTENSION:
+                return true;
+            /*
+             * Here we have the chance to optimize actions like dec mpls ttl.
+             */
+            case L2MODIFICATION:
+                return checkL2Instructions((L2ModificationInstruction) instruction);
+            /*
+             * Here we have the chance to optimize the ttl related actions.
+             */
+            case L3MODIFICATION:
+                return checkL3Instructions((L3ModificationInstruction) instruction);
+
+            default:
+                throw new IntentCompilationException(UNKNOWN_INSTRUCTION);
+
+        }
+
+    }
+
 }
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/AbstractLinkCollectionTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/AbstractLinkCollectionTest.java
index 9a8324c..f7389e7 100644
--- a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/AbstractLinkCollectionTest.java
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/AbstractLinkCollectionTest.java
@@ -53,7 +53,7 @@
 
     final ConnectPoint d2p0 = connectPoint("s2", 0);
     final ConnectPoint d2p1 = connectPoint("s2", 1);
-    ConnectPoint d2p10 = connectPoint("s2", 10);
+    final ConnectPoint d2p10 = connectPoint("s2", 10);
 
     final ConnectPoint d3p0 = connectPoint("s3", 0);
     final ConnectPoint d3p1 = connectPoint("s3", 1);
@@ -78,6 +78,7 @@
     final ConnectPoint d1p1 = connectPoint("s1", 1);
     final ConnectPoint d1p10 = connectPoint("s1", 10);
     final ConnectPoint d1p11 = connectPoint("s1", 11);
+    final ConnectPoint d1p12 = connectPoint("s1", 12);
 
     final Set<Link> links = ImmutableSet.of(
             link(d1p1, d2p0),
@@ -98,6 +99,8 @@
     );
 
     final TrafficTreatment treatment = emptyTreatment();
+    final TrafficTreatment ethDstTreatment = macDstTreatment("C0:FF:EE:C0:FF:EE");
+    final TrafficTreatment decTllTreatment = decTtlTreatment();
 
     final TrafficSelector selector = emptySelector();
     final TrafficSelector vlan69Selector = vlanSelector("69");
@@ -109,7 +112,6 @@
     final TrafficSelector mpls100Selector = mplsSelector("100");
     final TrafficSelector mpls200Selector = mplsSelector("200");
     final TrafficSelector ipPrefixSelector = ipPrefixDstSelector("192.168.100.0/24");
-    final TrafficTreatment ethDstTreatment = macDstTreatment("C0:FF:EE:C0:FF:EE");
 
     final List<Constraint> constraintsForVlan = vlanConstraint();
     final List<Constraint> constraintsForMPLS = mplsConstraint();
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionEncapIntentCompilerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionEncapIntentCompilerTest.java
index 588880a..295a998 100644
--- a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionEncapIntentCompilerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionEncapIntentCompilerTest.java
@@ -82,6 +82,9 @@
         sut.registrator = registrator;
         sut.resourceService = new MockResourceService();
 
+        LinkCollectionCompiler.optimize = false;
+        LinkCollectionCompiler.copyTtl = false;
+
         replay(coreService, intentExtensionService);
     }
 
@@ -290,7 +293,7 @@
                 DefaultTrafficTreatment
                         .builder()
                         .pushMpls()
-                        .setMpls(MplsLabel.mplsLabel((LABEL)))
+                        .setMpls(MplsLabel.mplsLabel(LABEL))
                         .setOutput(d3p0.port())
                         .build()
         ));
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompilerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompilerTest.java
index 87103aa..cfbddea 100644
--- a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompilerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompilerTest.java
@@ -96,8 +96,10 @@
 
         sut.registrator = registrator;
 
-        replay(coreService, intentExtensionService);
+        LinkCollectionCompiler.optimize = false;
+        LinkCollectionCompiler.copyTtl = false;
 
+        replay(coreService, intentExtensionService);
 
     }
 
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionOptimizationTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionOptimizationTest.java
new file mode 100644
index 0000000..4b58fba
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionOptimizationTest.java
@@ -0,0 +1,1060 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.intent.impl.compiler;
+
+import com.google.common.collect.ImmutableSet;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.MplsLabel;
+import org.onlab.packet.VlanId;
+import org.onosproject.cfg.ComponentConfigAdapter;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.FilteredConnectPoint;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.criteria.MplsCriterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
+import org.onosproject.net.intent.FlowRuleIntent;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentExtensionService;
+import org.onosproject.net.intent.LinkCollectionIntent;
+import org.onosproject.net.resource.MockResourceService;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.easymock.EasyMock.*;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.core.Is.is;
+import static org.onlab.packet.EthType.EtherType.IPV4;
+import static org.onosproject.net.NetTestTools.*;
+import static org.onosproject.net.flow.criteria.Criterion.Type.*;
+import static org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
+
+/**
+ * This set of tests are meant to test the encapsulation
+ * in the LinkCollectionIntent.
+ */
+public class LinkCollectionOptimizationTest extends AbstractLinkCollectionTest {
+
+    @Before
+    public void setUp() {
+        sut = new LinkCollectionIntentCompiler();
+        coreService = createMock(CoreService.class);
+        expect(coreService.registerApplication("org.onosproject.net.intent"))
+                .andReturn(appId);
+        sut.coreService = coreService;
+
+        Intent.bindIdGenerator(idGenerator);
+
+        intentExtensionService = createMock(IntentExtensionService.class);
+        intentExtensionService.registerCompiler(LinkCollectionIntent.class, sut);
+        intentExtensionService.unregisterCompiler(LinkCollectionIntent.class);
+
+        registrator = new IntentConfigurableRegistrator();
+        registrator.extensionService = intentExtensionService;
+        registrator.cfgService = new ComponentConfigAdapter();
+        registrator.activate();
+
+        sut.registrator = registrator;
+        sut.resourceService = new MockResourceService();
+
+        /*
+         * We activate the optimizations.
+         */
+        LinkCollectionCompiler.optimize = true;
+        LinkCollectionCompiler.copyTtl = true;
+
+        replay(coreService, intentExtensionService);
+    }
+
+    @After
+    public void tearDown() {
+        Intent.unbindIdGenerator(idGenerator);
+    }
+
+    /**
+     * We test the proper optimization of sp2mp with dec tll
+     * and dec mpls ttl.
+     */
+    @Test
+    public void testDecTtlOptimization() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(decTllTreatment)
+                .links(linksForSp2Mp)
+                .filteredIngressPoints(ImmutableSet.of(new FilteredConnectPoint(d3p10)))
+                .filteredEgressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d1p10),
+                        new FilteredConnectPoint(d1p11),
+                        new FilteredConnectPoint(d2p10)
+                ))
+                .applyTreatmentOnEgress(true)
+                .build();
+
+        sut.activate();
+
+        List<Intent> compiled = sut.compile(intent, Collections.emptyList());
+        assertThat(compiled, hasSize(1));
+
+        Collection<FlowRule> rules = ((FlowRuleIntent) compiled.get(0)).flowRules();
+        assertThat(rules, hasSize(3));
+
+        Collection<FlowRule> rulesS3 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d3p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS3, hasSize(1));
+        FlowRule ruleS3 = rulesS3.iterator().next();
+        assertThat(ruleS3.selector(), is(
+                DefaultTrafficSelector
+                        .builder()
+                        .matchInPort(d3p10.port())
+                        .build()
+        ));
+        assertThat(ruleS3.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setOutput(d3p0.port())
+                        .build()
+        ));
+
+        Collection<FlowRule> rulesS2 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d2p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS2, hasSize(1));
+        FlowRule ruleS2 = rulesS2.iterator().next();
+        assertThat(ruleS2.selector(), is(
+                DefaultTrafficSelector
+                        .builder()
+                        .matchInPort(d2p1.port())
+                        .build()
+        ));
+        assertThat(ruleS2.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setOutput(d2p0.port())
+                        .decMplsTtl()
+                        .decNwTtl()
+                        .setOutput(d2p10.port())
+                        .build()
+        ));
+
+        Collection<FlowRule> rulesS1 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d1p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS1, hasSize(1));
+        FlowRule ruleS1 = rulesS1.iterator().next();
+        assertThat(ruleS1.selector(), is(
+                DefaultTrafficSelector
+                        .builder()
+                        .matchInPort(d1p0.port())
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .decMplsTtl()
+                        .decNwTtl()
+                        .setOutput(d1p10.port())
+                        .setOutput(d1p11.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+
+    }
+
+    /**
+     * We test the proper optimization of sp2mp with VLAN
+     * ingress point and different egress points: 1) it is
+     * a simple ingress point; 2) it is a vlan ingress point;
+     * 3) It is a simple ingress point. 1) and 2) share the same
+     * egress switch. The outcomes of the test are the re-ordering
+     * of the actions and the proper optimization of the chain.
+     */
+    @Test
+    public void testVlanOrder() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(treatment)
+                .links(linksForSp2Mp)
+                .applyTreatmentOnEgress(true)
+                .filteredIngressPoints(ImmutableSet.of(new FilteredConnectPoint(d3p10, vlan100Selector)))
+                .filteredEgressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d1p10),
+                        new FilteredConnectPoint(d1p11, vlan200Selector),
+                        new FilteredConnectPoint(d2p10)
+                ))
+                .build();
+
+        sut.activate();
+
+        List<Intent> compiled = sut.compile(intent, Collections.emptyList());
+        assertThat(compiled, hasSize(1));
+
+        Collection<FlowRule> rules = ((FlowRuleIntent) compiled.get(0)).flowRules();
+        assertThat(rules, hasSize(3));
+
+        Collection<FlowRule> rulesS3 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d3p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS3, hasSize(1));
+        FlowRule ruleS3 = rulesS3.iterator().next();
+        assertThat(ruleS3.selector(), is(
+                DefaultTrafficSelector
+                        .builder(vlan100Selector)
+                        .matchInPort(d3p10.port())
+                        .build()
+        ));
+        assertThat(ruleS3.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setOutput(d3p0.port())
+                        .build()
+        ));
+
+        Collection<FlowRule> rulesS2 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d2p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS2, hasSize(1));
+        FlowRule ruleS2 = rulesS2.iterator().next();
+        assertThat(ruleS2.selector(), is(
+                DefaultTrafficSelector
+                        .builder(vlan100Selector)
+                        .matchInPort(d2p1.port())
+                        .build()
+        ));
+        assertThat(ruleS2.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setOutput(d2p0.port())
+                        .popVlan()
+                        .setOutput(d2p10.port())
+                        .build()
+        ));
+
+        Collection<FlowRule> rulesS1 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d1p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS1, hasSize(1));
+        FlowRule ruleS1 = rulesS1.iterator().next();
+        assertThat(ruleS1.selector(), is(
+                DefaultTrafficSelector
+                        .builder(vlan100Selector)
+                        .matchInPort(d1p0.port())
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setVlanId(((VlanIdCriterion) vlan200Selector.getCriterion(VLAN_VID)).vlanId())
+                        .setOutput(d1p11.port())
+                        .popVlan()
+                        .setOutput(d1p10.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+
+    }
+
+    /**
+     * We test the proper optimization of sp2mp with MPLS
+     * ingress point and different egress points: 1) it is
+     * a vlan ingress point; 2) it is a mpls ingress point;
+     * 3) It is a simple ingress point. 1) and 2) share the same
+     * egress switch. The outcomes of the test are the re-ordering
+     * of the actions and the proper optimization of the chain.
+     */
+    @Test
+    public void testMplsOrder() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(treatment)
+                .links(linksForSp2Mp)
+                .applyTreatmentOnEgress(true)
+                .filteredIngressPoints(ImmutableSet.of(new FilteredConnectPoint(d3p10, mpls100Selector)))
+                .filteredEgressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d1p10, vlan100Selector),
+                        new FilteredConnectPoint(d1p11, mpls200Selector),
+                        new FilteredConnectPoint(d2p10)
+                ))
+                .build();
+
+        sut.activate();
+
+        List<Intent> compiled = sut.compile(intent, Collections.emptyList());
+        assertThat(compiled, hasSize(1));
+
+        Collection<FlowRule> rules = ((FlowRuleIntent) compiled.get(0)).flowRules();
+        assertThat(rules, hasSize(3));
+
+        Collection<FlowRule> rulesS3 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d3p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS3, hasSize(1));
+        FlowRule ruleS3 = rulesS3.iterator().next();
+        assertThat(ruleS3.selector(), is(
+                DefaultTrafficSelector
+                        .builder(mpls100Selector)
+                        .matchInPort(d3p10.port())
+                        .build()
+        ));
+        assertThat(ruleS3.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setOutput(d3p0.port())
+                        .build()
+        ));
+
+        Collection<FlowRule> rulesS2 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d2p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS2, hasSize(1));
+        FlowRule ruleS2 = rulesS2.iterator().next();
+        assertThat(ruleS2.selector(), is(
+                DefaultTrafficSelector
+                        .builder(mpls100Selector)
+                        .matchInPort(d2p1.port())
+                        .build()
+        ));
+        assertThat(ruleS2.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setOutput(d2p0.port())
+                        .copyTtlIn()
+                        .popMpls(IPV4.ethType())
+                        .setOutput(d2p10.port())
+                        .build()
+        ));
+
+        Collection<FlowRule> rulesS1 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d1p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS1, hasSize(1));
+        FlowRule ruleS1 = rulesS1.iterator().next();
+        assertThat(ruleS1.selector(), is(
+                DefaultTrafficSelector
+                        .builder(mpls100Selector)
+                        .matchInPort(d1p0.port())
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setMpls(((MplsCriterion) mpls200Selector.getCriterion(MPLS_LABEL)).label())
+                        .setOutput(d1p11.port())
+                        .copyTtlIn()
+                        .popMpls(IPV4.ethType())
+                        .pushVlan()
+                        .setVlanId(((VlanIdCriterion) vlan100Selector.getCriterion(VLAN_VID)).vlanId())
+                        .setOutput(d1p10.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+
+    }
+
+    /**
+     * We test the proper optimization of sp2mp with untagged
+     * ingress point and different egress points: 1) it is
+     * a vlan ingress point; 2) it is a mpls ingress point;
+     * 3) It is a simple ingress point. 1) and 2) share the same
+     * egress switch. The outcomes of the test are the re-ordering
+     * of the actions and the proper optimization of the chain.
+     */
+    @Test
+    public void testUntaggedOrder() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(treatment)
+                .links(linksForSp2Mp)
+                .applyTreatmentOnEgress(true)
+                .filteredIngressPoints(ImmutableSet.of(new FilteredConnectPoint(d3p10)))
+                .filteredEgressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d1p10, vlan100Selector),
+                        new FilteredConnectPoint(d1p11, mpls200Selector),
+                        new FilteredConnectPoint(d1p12),
+                        new FilteredConnectPoint(d2p10)
+                ))
+                .build();
+
+        sut.activate();
+
+        List<Intent> compiled = sut.compile(intent, Collections.emptyList());
+        assertThat(compiled, hasSize(1));
+
+        Collection<FlowRule> rules = ((FlowRuleIntent) compiled.get(0)).flowRules();
+        assertThat(rules, hasSize(3));
+
+        Collection<FlowRule> rulesS3 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d3p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS3, hasSize(1));
+        FlowRule ruleS3 = rulesS3.iterator().next();
+        assertThat(ruleS3.selector(), is(
+                DefaultTrafficSelector
+                        .builder()
+                        .matchInPort(d3p10.port())
+                        .build()
+        ));
+        assertThat(ruleS3.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setOutput(d3p0.port())
+                        .build()
+        ));
+
+
+        Collection<FlowRule> rulesS2 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d2p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS2, hasSize(1));
+        FlowRule ruleS2 = rulesS2.iterator().next();
+        assertThat(ruleS2.selector(), is(
+                DefaultTrafficSelector
+                        .builder()
+                        .matchInPort(d2p1.port())
+                        .build()
+        ));
+        assertThat(ruleS2.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setOutput(d2p0.port())
+                        .setOutput(d2p10.port())
+                        .build()
+        ));
+
+
+        Collection<FlowRule> rulesS1 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d1p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS1, hasSize(1));
+        FlowRule ruleS1 = rulesS1.iterator().next();
+        assertThat(ruleS1.selector(), is(
+                DefaultTrafficSelector
+                        .builder()
+                        .matchInPort(d1p0.port())
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setOutput(d1p12.port())
+                        .pushVlan()
+                        .setVlanId(((VlanIdCriterion) vlan100Selector.getCriterion(VLAN_VID)).vlanId())
+                        .setOutput(d1p10.port())
+                        .popVlan()
+                        .pushMpls()
+                        .copyTtlOut()
+                        .setMpls(((MplsCriterion) mpls200Selector.getCriterion(MPLS_LABEL)).label())
+                        .setOutput(d1p11.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+
+    }
+
+    /**
+     * We test the proper optimization of sp2mp with VLAN ingress point,
+     * simple egress point, non trivial intent treatment and non trivial
+     * intent selector. The outcomes of the test are the proper optimization
+     * of the actions. It is expected that the actions related to the intent
+     * treatment and intent selector are not optimized. The optimization is
+     * performed only on the dec ttl actions and on the actions which create
+     * traffic slices. The rationale is that in the intent treatment we expect
+     * actions like set something so it is fine to perform them several times.
+     */
+    @Test
+    public void testPoPVlanOptimization() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(ipPrefixSelector)
+                .treatment(ethDstTreatment)
+                .links(linksForSp2Mp)
+                .applyTreatmentOnEgress(true)
+                .filteredIngressPoints(ImmutableSet.of(new FilteredConnectPoint(d3p10, vlan100Selector)))
+                .filteredEgressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d1p10),
+                        new FilteredConnectPoint(d1p11),
+                        new FilteredConnectPoint(d1p12),
+                        new FilteredConnectPoint(d2p10)
+                ))
+                .build();
+
+        sut.activate();
+
+        List<Intent> compiled = sut.compile(intent, Collections.emptyList());
+        assertThat(compiled, hasSize(1));
+
+        Collection<FlowRule> rules = ((FlowRuleIntent) compiled.get(0)).flowRules();
+        assertThat(rules, hasSize(3));
+
+        Collection<FlowRule> rulesS3 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d3p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS3, hasSize(1));
+        FlowRule ruleS3 = rulesS3.iterator().next();
+        assertThat(ruleS3.selector(), is(
+                DefaultTrafficSelector
+                        .builder(ipPrefixSelector)
+                        .matchVlanId(((VlanIdCriterion) vlan100Selector.getCriterion(VLAN_VID)).vlanId())
+                        .matchInPort(d3p10.port())
+                        .build()
+        ));
+        assertThat(ruleS3.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setOutput(d3p0.port())
+                        .build()
+        ));
+
+        Collection<FlowRule> rulesS2 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d2p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS2, hasSize(1));
+        FlowRule ruleS2 = rulesS2.iterator().next();
+        assertThat(ruleS2.selector(), is(
+                DefaultTrafficSelector
+                        .builder(ipPrefixSelector)
+                        .matchInPort(d2p1.port())
+                        .matchVlanId(((VlanIdCriterion) vlan100Selector.getCriterion(VLAN_VID)).vlanId())
+                        .build()
+        ));
+        assertThat(ruleS2.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setOutput(d2p0.port())
+                        .setEthDst(((ModEtherInstruction) ethDstTreatment
+                                .allInstructions()
+                                .stream()
+                                .filter(instruction -> instruction instanceof ModEtherInstruction)
+                                .findFirst().get()).mac())
+                        .popVlan()
+                        .setOutput(d2p10.port())
+                        .build()
+        ));
+
+        Collection<FlowRule> rulesS1 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d1p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS1, hasSize(1));
+        FlowRule ruleS1 = rulesS1.iterator().next();
+        assertThat(ruleS1.selector(), is(
+                DefaultTrafficSelector
+                        .builder(ipPrefixSelector)
+                        .matchInPort(d1p0.port())
+                        .matchVlanId(((VlanIdCriterion) vlan100Selector.getCriterion(VLAN_VID)).vlanId())
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setEthDst(((ModEtherInstruction) ethDstTreatment
+                                .allInstructions()
+                                .stream()
+                                .filter(instruction -> instruction instanceof ModEtherInstruction)
+                                .findFirst().get()).mac())
+                        .popVlan()
+                        .setOutput(d1p12.port())
+                        .setEthDst(((ModEtherInstruction) ethDstTreatment
+                                .allInstructions()
+                                .stream()
+                                .filter(instruction -> instruction instanceof ModEtherInstruction)
+                                .findFirst().get()).mac())
+                        .setOutput(d1p10.port())
+                        .setEthDst(((ModEtherInstruction) ethDstTreatment
+                                .allInstructions()
+                                .stream()
+                                .filter(instruction -> instruction instanceof ModEtherInstruction)
+                                .findFirst().get()).mac())
+                        .setOutput(d1p11.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+
+    }
+
+    /**
+     * We test the proper optimization of sp2mp with the VLAN
+     * encapsulation and trivial selector.
+     */
+    @Test
+    public void testOptimizationMplsEncapTrivialSelectors() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(treatment)
+                .constraints(constraintsForMPLS)
+                .links(linksForSp2Mp)
+                .filteredIngressPoints(ImmutableSet.of(new FilteredConnectPoint(d3p10)))
+                .filteredEgressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d1p10),
+                        new FilteredConnectPoint(d1p11),
+                        new FilteredConnectPoint(d2p10)
+                ))
+                .build();
+
+        sut.activate();
+        /*
+         * We use the FIRST_FIT to simplify tests.
+         */
+        LinkCollectionCompiler.labelAllocator.setLabelSelection(LABEL_SELECTION);
+
+        List<Intent> compiled = sut.compile(intent, Collections.emptyList());
+        assertThat(compiled, hasSize(1));
+
+        Collection<FlowRule> rules = ((FlowRuleIntent) compiled.get(0)).flowRules();
+        assertThat(rules, hasSize(3));
+
+        Collection<FlowRule> rulesS3 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d3p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS3, hasSize(1));
+        FlowRule ruleS3 = rulesS3.iterator().next();
+        assertThat(ruleS3.selector(), is(
+                DefaultTrafficSelector
+                        .builder()
+                        .matchInPort(d3p10.port())
+                        .build()
+        ));
+        assertThat(ruleS3.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .pushMpls()
+                        .copyTtlOut()
+                        .setMpls(MplsLabel.mplsLabel(LABEL))
+                        .setOutput(d3p0.port())
+                        .build()
+        ));
+
+        Collection<FlowRule> rulesS2 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d2p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS2, hasSize(1));
+        FlowRule ruleS2 = rulesS2.iterator().next();
+        assertThat(ruleS2.selector(), is(
+                DefaultTrafficSelector
+                        .builder()
+                        .matchInPort(d2p1.port())
+                        .matchMplsLabel(MplsLabel.mplsLabel((LABEL)))
+                        .matchEthType(Ethernet.MPLS_UNICAST)
+                        .build()
+        ));
+        assertThat(ruleS2.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setMpls(MplsLabel.mplsLabel(LABEL))
+                        .setOutput(d2p0.port())
+                        .copyTtlIn()
+                        .popMpls(IPV4.ethType())
+                        .setOutput(d2p10.port())
+                        .build()
+        ));
+
+        Collection<FlowRule> rulesS1 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d1p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS1, hasSize(1));
+        FlowRule ruleS1 = rulesS1.iterator().next();
+        assertThat(ruleS1.selector(), is(
+                DefaultTrafficSelector
+                        .builder()
+                        .matchInPort(d1p0.port())
+                        .matchMplsLabel(MplsLabel.mplsLabel((LABEL)))
+                        .matchEthType(Ethernet.MPLS_UNICAST)
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .copyTtlIn()
+                        .popMpls(IPV4.ethType())
+                        .setOutput(d1p10.port())
+                        .setOutput(d1p11.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+
+    }
+
+    /**
+     * We test the proper optimization of sp2mp with the VLAN
+     * encapsulation, filtered selectors.
+     */
+    @Test
+    public void testOptimizationVlanEncapMplsSelectors() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(treatment)
+                .constraints(constraintsForVlan)
+                .links(linksForSp2Mp)
+                .filteredIngressPoints(ImmutableSet.of(new FilteredConnectPoint(d3p10, mpls80Selector)))
+                .filteredEgressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d1p10, mpls100Selector),
+                        new FilteredConnectPoint(d1p11, mpls200Selector),
+                        new FilteredConnectPoint(d2p10, mpls69Selector)
+                ))
+                .build();
+
+        sut.activate();
+        /*
+         * We use the FIRST_FIT to simplify tests.
+         */
+        LinkCollectionCompiler.labelAllocator.setLabelSelection(LABEL_SELECTION);
+
+        List<Intent> compiled = sut.compile(intent, Collections.emptyList());
+        assertThat(compiled, hasSize(1));
+
+        Collection<FlowRule> rules = ((FlowRuleIntent) compiled.get(0)).flowRules();
+        assertThat(rules, hasSize(3));
+
+        Collection<FlowRule> rulesS3 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d3p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS3, hasSize(1));
+        FlowRule ruleS3 = rulesS3.iterator().next();
+        assertThat(ruleS3.selector(), is(
+                DefaultTrafficSelector
+                        .builder(mpls80Selector)
+                        .matchInPort(d3p10.port())
+                        .build()
+        ));
+        assertThat(ruleS3.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .copyTtlIn()
+                        .popMpls(IPV4.ethType())
+                        .pushVlan()
+                        .setVlanId(VlanId.vlanId(LABEL))
+                        .setOutput(d3p0.port())
+                        .build()
+        ));
+
+        Collection<FlowRule> rulesS2 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d2p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS2, hasSize(1));
+        FlowRule ruleS2 = rulesS2.iterator().next();
+        assertThat(ruleS2.selector(), is(
+                DefaultTrafficSelector
+                        .builder()
+                        .matchInPort(d2p1.port())
+                        .matchVlanId(VlanId.vlanId(LABEL))
+                        .build()
+        ));
+        assertThat(ruleS2.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setVlanId(VlanId.vlanId(LABEL))
+                        .setOutput(d2p0.port())
+                        .popVlan()
+                        .pushMpls()
+                        .copyTtlOut()
+                        .setMpls(((MplsCriterion) mpls69Selector.getCriterion(MPLS_LABEL)).label())
+                        .setOutput(d2p10.port())
+                        .build()
+        ));
+
+        Collection<FlowRule> rulesS1 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d1p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS1, hasSize(1));
+        FlowRule ruleS1 = rulesS1.iterator().next();
+        assertThat(ruleS1.selector(), is(
+                DefaultTrafficSelector
+                        .builder()
+                        .matchInPort(d1p0.port())
+                        .matchVlanId(VlanId.vlanId(LABEL))
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .popVlan()
+                        .pushMpls()
+                        .copyTtlOut()
+                        .setMpls(((MplsCriterion) mpls100Selector.getCriterion(MPLS_LABEL)).label())
+                        .setOutput(d1p10.port())
+                        .setMpls(((MplsCriterion) mpls200Selector.getCriterion(MPLS_LABEL)).label())
+                        .setOutput(d1p11.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+
+    }
+
+    /**
+     * We test the proper optimization of sp2mp with the MPLS
+     * encapsulation, filtered selectors, intent selector, and
+     * intent treatment.
+     */
+    @Test
+    public void testOptimizationMplsEncapNonTrivial() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(ipPrefixSelector)
+                .treatment(ethDstTreatment)
+                .constraints(constraintsForMPLS)
+                .links(linksForSp2Mp)
+                .filteredIngressPoints(ImmutableSet.of(new FilteredConnectPoint(d3p10, vlan69Selector)))
+                .filteredEgressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d1p10, vlan100Selector),
+                        new FilteredConnectPoint(d1p11, vlan200Selector),
+                        new FilteredConnectPoint(d2p10, vlan300Selector)
+                ))
+                .build();
+
+        sut.activate();
+        /*
+         * We use the FIRST_FIT to simplify tests.
+         */
+        LinkCollectionCompiler.labelAllocator.setLabelSelection(LABEL_SELECTION);
+
+        List<Intent> compiled = sut.compile(intent, Collections.emptyList());
+        assertThat(compiled, hasSize(1));
+
+        Collection<FlowRule> rules = ((FlowRuleIntent) compiled.get(0)).flowRules();
+        assertThat(rules, hasSize(3));
+
+        Collection<FlowRule> rulesS3 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d3p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS3, hasSize(1));
+        FlowRule ruleS3 = rulesS3.iterator().next();
+        assertThat(ruleS3.selector(), is(
+                DefaultTrafficSelector
+                        .builder(ipPrefixSelector)
+                        .matchInPort(d3p10.port())
+                        .matchVlanId(((VlanIdCriterion) vlan69Selector.getCriterion(VLAN_VID)).vlanId())
+                        .build()
+        ));
+        assertThat(ruleS3.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .popVlan()
+                        .pushMpls()
+                        .copyTtlOut()
+                        .setMpls(MplsLabel.mplsLabel(LABEL))
+                        .setOutput(d3p0.port())
+                        .build()
+        ));
+
+        Collection<FlowRule> rulesS2 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d2p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS2, hasSize(1));
+        FlowRule ruleS2 = rulesS2.iterator().next();
+        assertThat(ruleS2.selector(), is(
+                DefaultTrafficSelector
+                        .builder()
+                        .matchInPort(d2p1.port())
+                        .matchMplsLabel(MplsLabel.mplsLabel(LABEL))
+                        .matchEthType(Ethernet.MPLS_UNICAST)
+                        .build()
+        ));
+        assertThat(ruleS2.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setMpls(MplsLabel.mplsLabel(LABEL))
+                        .setOutput(d2p0.port())
+                        .setEthDst(((ModEtherInstruction) ethDstTreatment
+                                           .allInstructions()
+                                           .stream()
+                                           .filter(instruction -> instruction instanceof ModEtherInstruction)
+                                           .findFirst().get()).mac())
+                        .copyTtlIn()
+                        .popMpls(IPV4.ethType())
+                        .pushVlan()
+                        .setVlanId(((VlanIdCriterion) vlan300Selector.getCriterion(VLAN_VID)).vlanId())
+                        .setOutput(d2p10.port())
+                        .build()
+        ));
+
+        Collection<FlowRule> rulesS1 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d1p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS1, hasSize(1));
+        FlowRule ruleS1 = rulesS1.iterator().next();
+        assertThat(ruleS1.selector(), is(
+                DefaultTrafficSelector
+                        .builder()
+                        .matchInPort(d1p0.port())
+                        .matchMplsLabel(MplsLabel.mplsLabel(LABEL))
+                        .matchEthType(Ethernet.MPLS_UNICAST)
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setEthDst(((ModEtherInstruction) ethDstTreatment
+                                .allInstructions()
+                                .stream()
+                                .filter(instruction -> instruction instanceof ModEtherInstruction)
+                                .findFirst().get()).mac())
+                        .copyTtlIn()
+                        .popMpls(IPV4.ethType())
+                        .pushVlan()
+                        .setVlanId(((VlanIdCriterion) vlan100Selector.getCriterion(VLAN_VID)).vlanId())
+                        .setOutput(d1p10.port())
+                        .setEthDst(((ModEtherInstruction) ethDstTreatment
+                                .allInstructions()
+                                .stream()
+                                .filter(instruction -> instruction instanceof ModEtherInstruction)
+                                .findFirst().get()).mac())
+                        .setVlanId(((VlanIdCriterion) vlan200Selector.getCriterion(VLAN_VID)).vlanId())
+                        .setOutput(d1p11.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+
+    }
+
+    /**
+     * We test the proper compilation of sp2mp with the VLAN
+     * encapsulation and filtered selectors of different type.
+     */
+    @Test
+    public void testOptimizationVlanEncapDifferentSelectors() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(treatment)
+                .constraints(constraintsForVlan)
+                .links(linksForSp2Mp)
+                .filteredIngressPoints(ImmutableSet.of(new FilteredConnectPoint(d3p10, vlan69Selector)))
+                .filteredEgressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d1p10, mpls100Selector),
+                        new FilteredConnectPoint(d1p11, vlan200Selector),
+                        new FilteredConnectPoint(d1p12),
+                        new FilteredConnectPoint(d2p10, mpls200Selector)
+                ))
+                .build();
+
+        sut.activate();
+        /*
+         * We use the FIRST_FIT to simplify tests.
+         */
+        LinkCollectionCompiler.labelAllocator.setLabelSelection(LABEL_SELECTION);
+
+        List<Intent> compiled = sut.compile(intent, Collections.emptyList());
+        assertThat(compiled, hasSize(1));
+
+        Collection<FlowRule> rules = ((FlowRuleIntent) compiled.get(0)).flowRules();
+        assertThat(rules, hasSize(3));
+
+        Collection<FlowRule> rulesS3 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d3p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS3, hasSize(1));
+        FlowRule ruleS3 = rulesS3.iterator().next();
+        assertThat(ruleS3.selector(), is(
+                DefaultTrafficSelector
+                        .builder(vlan69Selector)
+                        .matchInPort(d3p10.port())
+                        .build()
+        ));
+        assertThat(ruleS3.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setVlanId(VlanId.vlanId(LABEL))
+                        .setOutput(d3p0.port())
+                        .build()
+        ));
+
+        Collection<FlowRule> rulesS2 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d2p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS2, hasSize(1));
+        FlowRule ruleS2 = rulesS2.iterator().next();
+        assertThat(ruleS2.selector(), is(
+                DefaultTrafficSelector
+                        .builder()
+                        .matchInPort(d2p1.port())
+                        .matchVlanId(VlanId.vlanId(LABEL))
+                        .build()
+        ));
+        assertThat(ruleS2.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setVlanId(VlanId.vlanId(LABEL))
+                        .setOutput(d2p0.port())
+                        .popVlan()
+                        .pushMpls()
+                        .copyTtlOut()
+                        .setMpls(((MplsCriterion) mpls200Selector.getCriterion(MPLS_LABEL)).label())
+                        .setOutput(d2p10.port())
+                        .build()
+        ));
+
+        Collection<FlowRule> rulesS1 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d1p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS1, hasSize(1));
+        FlowRule ruleS1 = rulesS1.iterator().next();
+        assertThat(ruleS1.selector(), is(
+                DefaultTrafficSelector
+                        .builder()
+                        .matchInPort(d1p0.port())
+                        .matchVlanId(VlanId.vlanId(LABEL))
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setVlanId(((VlanIdCriterion) vlan200Selector.getCriterion(VLAN_VID)).vlanId())
+                        .setOutput(d1p11.port())
+                        .popVlan()
+                        .setOutput(d1p12.port())
+                        .pushMpls()
+                        .copyTtlOut()
+                        .setMpls(((MplsCriterion) mpls100Selector.getCriterion(MPLS_LABEL)).label())
+                        .setOutput(d1p10.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+
+    }
+
+}