[ONOS-5239] Adds encapsulation to LinkCollectionIntent

Changes:
- Adds encapsulation to LinkCollectionIntent;
- Cleans the code and adds comments;
- fixes ethertype issue for mplsPop
- Adds unit tests;

Change-Id: I4398426e73c5a9dd085689cb56162bd2c3b8f5af
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 8898008..842c86d 100644
--- a/core/api/src/test/java/org/onosproject/net/NetTestTools.java
+++ b/core/api/src/test/java/org/onosproject/net/NetTestTools.java
@@ -15,12 +15,23 @@
  */
 package org.onosproject.net;
 
+import com.google.common.collect.ImmutableList;
 import org.onlab.junit.TestUtils;
 import org.onlab.packet.ChassisId;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.MplsLabel;
+import org.onlab.packet.VlanId;
 import org.onosproject.TestApplicationId;
 import org.onosproject.cluster.NodeId;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.event.EventDeliveryService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.intent.Constraint;
+import org.onosproject.net.intent.constraint.EncapsulationConstraint;
 import org.onosproject.net.provider.ProviderId;
 
 import java.lang.reflect.Field;
@@ -84,6 +95,17 @@
                                Link.Type.DIRECT, Link.State.ACTIVE);
     }
 
+    /**
+     * Short-hand for creating a link.
+     *
+     * @param src the src of the link
+     * @param dst the dst of the link
+     * @return a link
+     */
+    public static Link link(ConnectPoint src, ConnectPoint dst) {
+        return new DefaultLink(PID, src, dst, Link.Type.DIRECT, Link.State.ACTIVE);
+    }
+
     // Creates a path that leads through the given devices.
     public static Path createPath(String... ids) {
         List<Link> links = new ArrayList<>();
@@ -152,4 +174,88 @@
         }
     }
 
+    /**
+     * Builds an empty selector.
+     *
+     * @return the selector
+     */
+    public static TrafficSelector emptySelector() {
+        return DefaultTrafficSelector.emptySelector();
+    }
+
+    /**
+     * Builds a vlan selector.
+     *
+     * @return the selector
+     */
+    public static TrafficSelector vlanSelector(String vlanId) {
+        return DefaultTrafficSelector.builder()
+                .matchVlanId(VlanId.vlanId(vlanId))
+                .build();
+    }
+
+    /**
+     * Builds a mpls selector.
+     *
+     * @return the selector
+     */
+    public static TrafficSelector mplsSelector(String mplsLabel) {
+        return DefaultTrafficSelector.builder()
+                .matchMplsLabel(MplsLabel.mplsLabel(mplsLabel))
+                .build();
+    }
+
+    /**
+     * Builds an ip prefix dst selector.
+     *
+     * @return the selector
+     */
+    public static TrafficSelector ipPrefixDstSelector(String prefix) {
+        return DefaultTrafficSelector.builder()
+                .matchIPDst(IpPrefix.valueOf(prefix))
+                .build();
+    }
+
+    /**
+     * Builds an empty treatment.
+     *
+     * @return the treatment
+     */
+    public static TrafficTreatment emptyTreatment() {
+        return DefaultTrafficTreatment.emptyTreatment();
+    }
+
+    /**
+     * Builds a mac dst treatment.
+     *
+     * @return the treatment
+     */
+    public static TrafficTreatment macDstTreatment(String mac) {
+        return DefaultTrafficTreatment.builder()
+                .setEthDst(MacAddress.valueOf(mac))
+                .build();
+    }
+
+    /**
+     * Builds a list containing a vlan encapsulation constraint.
+     *
+     * @return the list of constraints
+     */
+    public static List<Constraint> vlanConstraint() {
+        return ImmutableList.of(
+                new EncapsulationConstraint(EncapsulationType.VLAN)
+        );
+    }
+
+    /**
+     * Builds a list containing a mpls encapsulation constraint.
+     *
+     * @return the list of constraints
+     */
+    public static List<Constraint> mplsConstraint() {
+        return ImmutableList.of(
+                new EncapsulationConstraint(EncapsulationType.MPLS)
+        );
+    }
+
 }
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 50578de..38be5e4 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
@@ -170,7 +170,7 @@
     }
 
     private void changeLabelSelections() {
-        PathCompiler.labelAllocator.setLabelSelection(labelSelection);
+        LinkCollectionCompiler.labelAllocator.setLabelSelection(labelSelection);
     }
 
 }
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 7701f29..d5df116 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,12 +16,19 @@
 
 package org.onosproject.net.intent.impl.compiler;
 
+import com.google.common.collect.Maps;
 import com.google.common.collect.SetMultimap;
 import com.google.common.collect.Sets;
+import org.onlab.packet.EthType;
+import org.onlab.packet.Ethernet;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MplsLabel;
+import org.onlab.packet.VlanId;
+import org.onlab.util.Identifier;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.EncapsulationType;
 import org.onosproject.net.Link;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.FilteredConnectPoint;
@@ -31,6 +38,7 @@
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.criteria.Criteria;
 import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.EthTypeCriterion;
 import org.onosproject.net.flow.criteria.MplsCriterion;
 import org.onosproject.net.flow.criteria.TunnelIdCriterion;
 import org.onosproject.net.flow.criteria.VlanIdCriterion;
@@ -54,27 +62,696 @@
 import org.onosproject.net.flow.instructions.L4ModificationInstruction.ModTransportPortInstruction;
 import org.onosproject.net.intent.IntentCompilationException;
 import org.onosproject.net.intent.LinkCollectionIntent;
+import org.onosproject.net.intent.constraint.EncapsulationConstraint;
+import org.onosproject.net.resource.impl.LabelAllocator;
 
 import java.util.List;
+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;
 
-
 /**
  * Shared APIs and implementations for Link Collection compilers.
  */
 public class LinkCollectionCompiler<T> {
 
+    /**
+     * Reference to the label allocator.
+     */
+    static LabelAllocator labelAllocator;
+
+    /**
+     * The allowed tag criterions.
+     */
     private static final Set<Criterion.Type> TAG_CRITERION_TYPES =
             Sets.immutableEnumSet(VLAN_VID, MPLS_LABEL, TUNNEL_ID);
 
     /**
-     * Helper class to encapsulate treatment and selector.
+     * Error message for wrong egress scenario.
+     */
+    private static final String WRONG_EGRESS = "Egress points not equal to 1 " +
+            "and apply treatment at ingress, " +
+            "which treatments should I apply ???";
+
+    /**
+     * Error message for wrong ingress scenario.
+     */
+    private static final String WRONG_INGRESS = "Ingress points not equal to 1 " +
+            "and apply treatment at egress, " +
+            "how can I match in the core ???";
+
+    /**
+     * Error message for wrong encapsulation scenario.
+     */
+    private static final String WRONG_ENCAPSULATION = "Wrong scenario - 1 hop with " +
+            "encapsualtion";
+
+    /**
+     * Error message for unavailable labels.
+     */
+    private static final String NO_LABELS = "No available label for %s";
+
+    /**
+     * Error message for wrong encapsulation.
+     */
+    private static final String UNKNOWN_ENCAPSULATION = "Unknown encapsulation type";
+
+    /**
+     * Error message for unsupported L0 instructions.
+     */
+    private static final String UNSUPPORTED_L0 = "L0 not supported";
+
+    /**
+     * Error message for unsupported L1 instructions.
+     */
+    private static final String UNSUPPORTED_L1 = "L1 not supported";
+
+    /**
+     * Error message for unsupported eth subtype.
+     */
+    private static final String UNSUPPORTED_ETH_SUBTYPE = "Bad eth subtype";
+
+    /**
+     * Error message for unsupported pop action.
+     */
+    private static final String UNSUPPORTED_POP_ACTION = "Can't handle pop label";
+
+    /**
+     * Error message for unsupported L2 instructions.
+     */
+    private static final String UNSUPPORTED_L2 = "Unknown L2 Modification instruction";
+
+    /**
+     * Error message for unsupported IP subtype.
+     */
+    private static final String UNSUPPORTED_IP_SUBTYPE = "Bad ip subtype";
+
+    /**
+     * Error message for unsupported ARP.
+     */
+    private static final String UNSUPPORTED_ARP = "IPv6 not supported for ARP";
+
+    /**
+     * Error message for unsupported L3 instructions.
+     */
+    private static final String UNSUPPORTED_L3 = "Unknown L3 Modification instruction";
+
+    /**
+     * Error message for unsupported L4 subtype.
+     */
+    private static final String UNSUPPORTED_L4_SUBTYPE = "Unknown L4 subtype";
+
+    /**
+     * Error message for unsupported L4 instructions.
+     */
+    private static final String UNSUPPORTED_L4 = "Unknown L4 Modification instruction";
+
+    /**
+     * Error message for unsupported instructions.
+     */
+    private static final String UNSUPPORTED_INSTRUCTION = "Unknown instruction type";
+
+    /**
+     * Creates the flows representations. This default implementation does
+     * nothing. Subclasses should override this method to create their
+     * specific flows representations (flow rule, flow objective).
+     *
+     * @param intent the intent to compile
+     * @param deviceId the affected device
+     * @param inPorts the input ports
+     * @param outPorts the output ports
+     * @param labels the labels for the label switching hop by hop
+     * @return the list of flows representations
+     */
+    protected List<T> createRules(LinkCollectionIntent intent,
+                                  DeviceId deviceId,
+                                  Set<PortNumber> inPorts,
+                                  Set<PortNumber> outPorts,
+                                  Map<ConnectPoint, Identifier<?>> labels) {
+        return null;
+    }
+
+    /**
+     * Helper method to handle the different scenario (not encap, single hop, encap).
+     *
+     * @param encapConstraint the encapsulation constraint if it is present
+     * @param intent the link collection intent
+     * @param inPort the in port
+     * @param outPorts the out ports
+     * @param deviceId the current device
+     * @param labels the labels used by the encapsulation
+     * @return the forwarding instruction
+     */
+    protected ForwardingInstructions createForwardingInstruction(Optional<EncapsulationConstraint> encapConstraint,
+                                                                 LinkCollectionIntent intent,
+                                                                 PortNumber inPort,
+                                                                 Set<PortNumber> outPorts,
+                                                                 DeviceId deviceId,
+                                                                 Map<ConnectPoint, Identifier<?>> labels) {
+        ForwardingInstructions instructions = null;
+        /*
+         * If not encapsulation or single hop.
+         */
+        if (!encapConstraint.isPresent() || intent.links().isEmpty()) {
+            instructions = this.createForwardingInstructions(
+                    intent,
+                    inPort,
+                    deviceId,
+                    outPorts
+            );
+        /*
+         * If encapsulation is present. We retrieve the labels
+         * for this iteration;
+         */
+        } else {
+            Identifier<?> inLabel = labels.get(new ConnectPoint(deviceId, inPort));
+            Map<ConnectPoint, Identifier<?>> outLabels = Maps.newHashMap();
+            outPorts.forEach(outPort -> {
+                ConnectPoint key = new ConnectPoint(deviceId, outPort);
+                outLabels.put(key, labels.get(key));
+            });
+            instructions = this.createForwardingInstructions(
+                    intent,
+                    inPort,
+                    inLabel,
+                    deviceId,
+                    outPorts,
+                    outLabels,
+                    encapConstraint.get().encapType()
+            );
+        }
+        return instructions;
+    }
+
+    /**
+     * Manages the Intents with a single ingress point (p2p, sp2mp)
+     * creating properly the selector builder and the treatment builder.
+     *
+     * @param selectorBuilder the selector builder to update
+     * @param treatmentBuilder the treatment builder to update
+     * @param intent the intent to compile
+     * @param deviceId the current device
+     * @param outPorts the output ports of this device
+     */
+    private void manageSpIntent(TrafficSelector.Builder selectorBuilder,
+                                TrafficTreatment.Builder treatmentBuilder,
+                                LinkCollectionIntent intent,
+                                DeviceId deviceId,
+                                Set<PortNumber> outPorts) {
+        /*
+         * Sanity check.
+         */
+        if (intent.filteredIngressPoints().size() != 1) {
+            throw new IntentCompilationException(WRONG_INGRESS);
+        }
+        /*
+         * For the p2p and sp2mp the transition initial state
+         * to final state is performed at the egress.
+         */
+        Optional<FilteredConnectPoint> filteredIngressPoint =
+                intent.filteredIngressPoints().stream().findFirst();
+        /*
+         * We build the final selector, adding the selector
+         * of the FIP to the Intent selector and potentially
+         * overriding its matches.
+         */
+        filteredIngressPoint.get()
+                .trafficSelector()
+                .criteria()
+                .forEach(selectorBuilder::add);
+        /*
+         * In this scenario, potentially we can have several output
+         * ports.
+         */
+        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);
+            }
+            /*
+             * Finally we set the output action.
+             */
+            treatmentBuilder.setOutput(outPort);
+        }
+    }
+
+    /**
+     * Manages the Intents with multiple ingress points creating properly
+     * the selector builder and the treatment builder.
+     *
+     * @param selectorBuilder the selector builder to update
+     * @param treatmentBuilder the treatment builder to update
+     * @param intent the intent to compile
+     * @param inPort the input port of the current device
+     * @param deviceId the current device
+     * @param outPorts the output ports of this device
+     */
+    private void manageMpIntent(TrafficSelector.Builder selectorBuilder,
+                                TrafficTreatment.Builder treatmentBuilder,
+                                LinkCollectionIntent intent,
+                                PortNumber inPort,
+                                DeviceId deviceId,
+                                Set<PortNumber> outPorts) {
+        /*
+         * Sanity check
+         */
+        if (intent.filteredEgressPoints().size() != 1) {
+            throw new IntentCompilationException(WRONG_EGRESS);
+        }
+        /*
+         * We try to understand if the device is one of the ingress points.
+         */
+        Optional<FilteredConnectPoint> filteredIngressPoint =
+                getFilteredConnectPointFromIntent(deviceId, inPort, intent);
+        /*
+         * We retrieve from the Intent the unique egress points.
+         */
+        Optional<FilteredConnectPoint> filteredEgressPoint =
+                intent.filteredEgressPoints().stream().findFirst();
+        /*
+         * We check if the device is the ingress device
+         */
+        if (filteredIngressPoint.isPresent()) {
+            /*
+             * We are at ingress, so basically what we have to do is this:
+             * apply a set of operations (treatment, FEP) in order to have
+             * a transition from the initial state to the final state.
+             *
+             * We initialize the treatment with the Intent treatment
+             */
+            intent.treatment().allInstructions().stream()
+                    .filter(inst -> inst.type() != Instruction.Type.NOACTION)
+                    .forEach(treatmentBuilder::add);
+            /*
+             * We build the final selector, adding the selector
+             * of the FIP to the Intent selector and potentially
+             * overriding its matches.
+             */
+            filteredIngressPoint.get()
+                    .trafficSelector()
+                    .criteria()
+                    .forEach(selectorBuilder::add);
+            /*
+             * We define the transition FIP->FEP, basically
+             * the set of the operations we need for reaching
+             * the final state.
+             */
+            TrafficTreatment forwardingTreatment =
+                    forwardingTreatment(filteredIngressPoint.get().trafficSelector(),
+                                        filteredEgressPoint.get().trafficSelector(),
+                                        getEthType(intent.selector()));
+            /*
+             * We add to the treatment the actions necessary for the
+             * transition, potentially overriding the treatment of the
+             * Intent. The Intent treatment has always a low priority
+             * in respect of the FEP.
+             */
+            forwardingTreatment.allInstructions().stream()
+                    .filter(inst -> inst.type() != Instruction.Type.NOACTION)
+                    .forEach(treatmentBuilder::add);
+        } else {
+            /*
+             * We are in the core or in the egress switch.
+             * The packets are in their final state. We need
+             * to match against this final state.
+             *
+             * we derive the final state defined by the intent
+             * treatment.
+             */
+            updateBuilder(selectorBuilder, intent.treatment());
+            /*
+             * We derive the final state defined by the unique
+             * FEP. We merge the two states.
+             */
+            filteredEgressPoint.get()
+                    .trafficSelector()
+                    .criteria()
+                    .forEach(selectorBuilder::add);
+        }
+        /*
+         * Finally we set the output action.
+         */
+        outPorts.forEach(treatmentBuilder::setOutput);
+    }
+
+    /**
+     * Computes treatment and selector which will be used
+     * in the flow representation (Rule, Objective).
+     *
+     * @param intent the intent to compile
+     * @param inPort the input port of this device
+     * @param deviceId the current device
+     * @param outPorts the output ports of this device
+     * @return the forwarding instruction object which encapsulates treatment and selector
+     */
+    protected ForwardingInstructions createForwardingInstructions(LinkCollectionIntent intent,
+                                                                  PortNumber inPort,
+                                                                  DeviceId deviceId,
+                                                                  Set<PortNumber> outPorts) {
+
+        /*
+         * We build an empty treatment and we initialize the selector with
+         * the intent selector.
+         */
+        TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
+                .builder();
+        TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector
+                .builder(intent.selector())
+                .matchInPort(inPort);
+
+        if (!intent.applyTreatmentOnEgress()) {
+            manageMpIntent(selectorBuilder,
+                           treatmentBuilder,
+                           intent,
+                           inPort,
+                           deviceId,
+                           outPorts
+            );
+        } else {
+            manageSpIntent(selectorBuilder,
+                           treatmentBuilder,
+                           intent,
+                           deviceId,
+                           outPorts
+            );
+        }
+        /*
+         * We return selector and treatment necessary to build the flow rule
+         * or the flow objective.
+         */
+        return new ForwardingInstructions(treatmentBuilder.build(), selectorBuilder.build());
+    }
+
+    /**
+     * Manages the ingress of the Intents (p2p, sp2mp, mp2sp) with encapsulation.
+     *
+     * @param selectorBuilder the selector builder to update
+     * @param treatmentBuilder the treatment builder to update
+     * @param intent the intent to compile
+     * @param inPort the input port of this device
+     * @param deviceId the current device
+     * @param outPorts the output ports of this device
+     * @param outLabels the labels associated to the output port
+     * @param type the encapsulation type
+     */
+    private void manageEncapAtIngress(TrafficSelector.Builder selectorBuilder,
+                                      TrafficTreatment.Builder treatmentBuilder,
+                                      LinkCollectionIntent intent,
+                                      PortNumber inPort,
+                                      DeviceId deviceId,
+                                      Set<PortNumber> outPorts,
+                                      Map<ConnectPoint, Identifier<?>> outLabels,
+                                      EncapsulationType type) {
+
+        Optional<FilteredConnectPoint> filteredIngressPoint =
+                getFilteredConnectPointFromIntent(deviceId, inPort, intent);
+        /*
+         * We fill the selector builder with the intent selector.
+         */
+        intent.selector().criteria().forEach(selectorBuilder::add);
+        /*
+         * We build the final selector, adding the selector
+         * of the FIP to the Intent selector and potentially
+         * overriding its matches.
+         */
+        filteredIngressPoint.get()
+                .trafficSelector()
+                .criteria()
+                .forEach(selectorBuilder::add);
+        /*
+         * In this scenario, we can have several output ports.
+         */
+        outPorts.forEach(outPort -> {
+            Optional<FilteredConnectPoint> filteredEgressPoint =
+                    getFilteredConnectPointFromIntent(deviceId, outPort, intent);
+            /*
+             * If we are at the egress, we don't handle
+             * with encapsulation. Error scenario
+             */
+            if (filteredEgressPoint.isPresent()) {
+                throw new IntentCompilationException(WRONG_ENCAPSULATION);
+            }
+            /*
+             * Transit/core, we have to transit to the intermediate
+             * state. We build a temporary selector for the encapsulation.
+             */
+            TrafficSelector.Builder encapBuilder = DefaultTrafficSelector.builder();
+            /*
+             * We retrieve the associated label to the output port.
+             */
+            ConnectPoint cp = new ConnectPoint(deviceId, outPort);
+            Identifier<?> outLabel = outLabels.get(cp);
+            /*
+             * If there aren't labels, we cannot handle.
+             */
+            if (outLabel == null) {
+                throw new IntentCompilationException(String.format(NO_LABELS, cp));
+            }
+            /*
+             * In the core we match using encapsulation.
+             */
+            updateSelectorFromEncapsulation(
+                    encapBuilder,
+                    type,
+                    outLabel
+            );
+            /*
+             * We generate the transition.
+             */
+            TrafficTreatment forwardingTreatment =
+                    forwardingTreatment(filteredIngressPoint.get().trafficSelector(),
+                                        encapBuilder.build(),
+                                        getEthType(intent.selector()));
+            /*
+             * We add the instruction necessary to the transition.
+             */
+            forwardingTreatment.allInstructions().stream()
+                    .filter(inst -> inst.type() != Instruction.Type.NOACTION)
+                    .forEach(treatmentBuilder::add);
+            /*
+             * Finally we set the output action.
+             */
+            treatmentBuilder.setOutput(outPort);
+        });
+
+    }
+
+    /**
+     * Manages the core and transit of the Intents (p2p, sp2mp, mp2sp)
+     * with encapsulation.
+     *
+     * @param selectorBuilder the selector builder to update
+     * @param treatmentBuilder the treatment builder to update
+     * @param intent the intent to compile
+     * @param inPort the input port of this device
+     * @param inLabel the label associated to the input port
+     * @param deviceId the current device
+     * @param outPorts the output ports of this device
+     * @param outLabels the labels associated to the output port
+     * @param type the encapsulation type
+     */
+    private void manageEncapAtCoreAndEgress(TrafficSelector.Builder selectorBuilder,
+                                            TrafficTreatment.Builder treatmentBuilder,
+                                            LinkCollectionIntent intent,
+                                            PortNumber inPort,
+                                            Identifier<?> inLabel,
+                                            DeviceId deviceId,
+                                            Set<PortNumber> outPorts,
+                                            Map<ConnectPoint, Identifier<?>> outLabels,
+                                            EncapsulationType type) {
+
+        /*
+         * If there are not labels, we cannot handle.
+         */
+        ConnectPoint inCp = new ConnectPoint(deviceId, inPort);
+        if (inLabel == null) {
+            throw new IntentCompilationException(String.format(NO_LABELS, inCp));
+        }
+        /*
+         * In the core and at egress we match using encapsulation.
+         */
+        updateSelectorFromEncapsulation(
+                selectorBuilder,
+                type,
+                inLabel
+        );
+        /*
+         * We need to order the actions. First the actions
+         * related to the not-egress points.
+         */
+        outPorts.forEach(outPort -> {
+            Optional<FilteredConnectPoint> filteredEgressPoint =
+                    getFilteredConnectPointFromIntent(deviceId, outPort, intent);
+            if (!filteredEgressPoint.isPresent()) {
+                /*
+                 * We build a temporary selector for the encapsulation.
+                 */
+                TrafficSelector.Builder encapBuilder = DefaultTrafficSelector.builder();
+                /*
+                 * We retrieve the associated label to the output port.
+                 */
+                ConnectPoint cp = new ConnectPoint(deviceId, outPort);
+                Identifier<?> outLabel = outLabels.get(cp);
+                /*
+                 * If there are not labels, we cannot handle.
+                 */
+                if (outLabel == null) {
+                    throw new IntentCompilationException(String.format(NO_LABELS, cp));
+                }
+                /*
+                 * In the core we match using encapsulation.
+                 */
+                updateSelectorFromEncapsulation(
+                        encapBuilder,
+                        type,
+                        outLabel
+                );
+                /*
+                 * We generate the transition.
+                 */
+                TrafficTreatment forwardingTreatment =
+                        forwardingTreatment(selectorBuilder.build(),
+                                            encapBuilder.build(),
+                                            getEthType(intent.selector()));
+                /*
+                 * We add the instruction necessary to the transition.
+                 */
+                forwardingTreatment.allInstructions().stream()
+                        .filter(inst -> inst.type() != Instruction.Type.NOACTION)
+                        .forEach(treatmentBuilder::add);
+                /*
+                 * Finally we set the output action.
+                 */
+                treatmentBuilder.setOutput(outPort);
+            }
+
+        });
+        /*
+         * 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);
+            }
+        });
+
+    }
+
+    /**
+     * Computes treatment and selector which will be used
+     * in the flow representation (Rule, Objective).
+     *
+     * @param intent the intent to compile
+     * @param inPort the input port of this device
+     * @param inLabel the label associated to the input port
+     * @param deviceId the current device
+     * @param outPorts the output ports of this device
+     * @param outLabels the labels associated to the output port
+     * @param type the encapsulation type
+     * @return the forwarding instruction object which encapsulates treatment and selector
+     */
+    protected ForwardingInstructions createForwardingInstructions(LinkCollectionIntent intent,
+                                                                  PortNumber inPort,
+                                                                  Identifier<?> inLabel,
+                                                                  DeviceId deviceId,
+                                                                  Set<PortNumber> outPorts,
+                                                                  Map<ConnectPoint, Identifier<?>> outLabels,
+                                                                  EncapsulationType type) {
+        /*
+         * We build an empty treatment and an empty selector.
+         */
+        TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
+        TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
+        selectorBuilder.matchInPort(inPort);
+        Optional<FilteredConnectPoint> filteredIngressPoint =
+                getFilteredConnectPointFromIntent(deviceId, inPort, intent);
+
+        if (filteredIngressPoint.isPresent()) {
+            manageEncapAtIngress(selectorBuilder,
+                                 treatmentBuilder,
+                                 intent,
+                                 inPort,
+                                 deviceId,
+                                 outPorts,
+                                 outLabels,
+                                 type
+            );
+        } else {
+            manageEncapAtCoreAndEgress(selectorBuilder,
+                                       treatmentBuilder,
+                                       intent,
+                                       inPort,
+                                       inLabel,
+                                       deviceId,
+                                       outPorts,
+                                       outLabels,
+                                       type);
+        }
+        /*
+         * We return selector and treatment necessary to build the flow rule
+         * or the flow objective.
+         */
+        return new ForwardingInstructions(treatmentBuilder.build(), selectorBuilder.build());
+    }
+
+    /**
+     * Helper class to encapsulate treatment and selector
+     * in an unique abstraction.
      */
     protected class ForwardingInstructions {
 
@@ -100,7 +777,8 @@
     }
 
     /**
-     * Helper method to compute input and output ports.
+     * Helper method to compute input and output ports
+     * for each device crossed in the path.
      *
      * @param intent the related intents
      * @param inputPorts the input ports to compute
@@ -122,169 +800,25 @@
         for (ConnectPoint egressPoint : intent.egressPoints()) {
             outputPorts.put(egressPoint.deviceId(), egressPoint.port());
         }
-    }
-
-    /**
-     * Gets ingress and egress port number of specific device.
-     *
-     * @param intent the related
-     * @param deviceId device Id
-     * @param ingressPorts the ingress ports to compute
-     * @param egressPorts the egress ports to compute
-     */
-    protected void computePorts(LinkCollectionIntent intent,
-                                DeviceId deviceId,
-                                Set<PortNumber> ingressPorts,
-                                Set<PortNumber> egressPorts) {
-
-        if (!intent.applyTreatmentOnEgress()) {
-            ingressPorts.addAll(intent.ingressPoints().stream()
-                    .filter(point -> point.deviceId().equals(deviceId))
-                    .map(ConnectPoint::port)
-                    .collect(Collectors.toSet()));
-        } else {
-            egressPorts.addAll(intent.egressPoints().stream()
-                    .filter(point -> point.deviceId().equals(deviceId))
-                    .map(ConnectPoint::port)
-                    .collect(Collectors.toSet()));
-        }
 
     }
 
     /**
-     * Creates the flows representations.
+     * Retrieves the encapsulation constraint from the link collection intent.
      *
-     * @param intent the intent to compile
-     * @param deviceId the affected device
-     * @param inPorts the input ports
-     * @param outPorts the output ports
-     * @return the list of flows representations
+     * @param intent the intent to analyze
+     * @return the encapsulation constraint
      */
-    protected List<T> createRules(LinkCollectionIntent intent, DeviceId deviceId,
-                                       Set<PortNumber> inPorts, Set<PortNumber> outPorts) {
-        return null;
+    protected Optional<EncapsulationConstraint> getIntentEncapConstraint(LinkCollectionIntent intent) {
+        return intent.constraints().stream()
+                .filter(constraint -> constraint instanceof EncapsulationConstraint)
+                .map(x -> (EncapsulationConstraint) x).findAny();
     }
 
 
     /**
-     * Computes treatment and selector which will be used
-     * in the flow representation (Rule, Objective).
-     *
-     * @param intent the intent to compile
-     * @param inPort the input port of this device
-     * @param deviceId the current device
-     * @param outPorts the output ports of this device
-     * @param ingressPorts intent ingress ports of this device
-     * @param egressPorts intent egress ports of this device
-     * @return the forwarding instruction object which encapsulates treatment and selector
-     */
-    protected ForwardingInstructions createForwardingInstructions(LinkCollectionIntent intent,
-                                                                  PortNumber inPort,
-                                                                  DeviceId deviceId,
-                                                                  Set<PortNumber> outPorts,
-                                                                  Set<PortNumber> ingressPorts,
-                                                                  Set<PortNumber> egressPorts) {
-
-        TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
-        TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder(intent.selector());
-        selectorBuilder.matchInPort(inPort);
-
-        if (!intent.applyTreatmentOnEgress()) {
-            // FIXME: currently, we assume this intent is compile from mp2sp intent
-            Optional<FilteredConnectPoint> filteredIngressPoint =
-                    getFilteredConnectPointFromIntent(deviceId, inPort, intent);
-            Optional<FilteredConnectPoint> filteredEgressPoint =
-                    intent.filteredEgressPoints().stream().findFirst();
-
-            if (filteredIngressPoint.isPresent()) {
-                // Ingress device
-                intent.treatment().allInstructions().stream()
-                        .filter(inst -> inst.type() != Instruction.Type.NOACTION)
-                        .forEach(treatmentBuilder::add);
-
-                if (filteredEgressPoint.isPresent()) {
-                    // Apply selector from ingress point
-                    filteredIngressPoint.get()
-                            .trafficSelector()
-                            .criteria()
-                            .forEach(selectorBuilder::add);
-
-                    TrafficTreatment forwardingTreatment =
-                            forwardingTreatment(filteredIngressPoint.get(),
-                                                filteredEgressPoint.get());
-
-                    forwardingTreatment.allInstructions().stream()
-                            .filter(inst -> inst.type() != Instruction.Type.NOACTION)
-                            .forEach(treatmentBuilder::add);
-                } else {
-                    throw new IntentCompilationException("Can't find filtered connection point");
-                }
-
-            } else {
-                // Not ingress device, won't apply treatments.
-                // Use selector by treatment from intent.
-                updateBuilder(selectorBuilder, intent.treatment());
-
-                // Selector should be overridden by selector from connect point.
-                if (filteredEgressPoint.isPresent()) {
-                    filteredEgressPoint.get()
-                            .trafficSelector()
-                            .criteria()
-                            .forEach(selectorBuilder::add);
-                }
-
-            }
-
-            outPorts.forEach(treatmentBuilder::setOutput);
-
-        } else {
-            // FIXME: currently, we assume this intent is compile from sp2mp intent
-            Optional<FilteredConnectPoint> filteredIngressPoint =
-                    intent.filteredIngressPoints().stream().findFirst();
-
-            if (filteredIngressPoint.isPresent()) {
-                // Apply selector from ingress point
-                filteredIngressPoint.get()
-                        .trafficSelector()
-                        .criteria()
-                        .forEach(selectorBuilder::add);
-            } else {
-                throw new IntentCompilationException(
-                        "Filtered connection point for ingress" +
-                                "point does not exist");
-            }
-
-            for (PortNumber outPort : outPorts) {
-                Optional<FilteredConnectPoint> filteredEgressPoint =
-                        getFilteredConnectPointFromIntent(deviceId, outPort, intent);
-
-                if (filteredEgressPoint.isPresent()) {
-                    // Egress port, apply treatment + forwarding treatment
-                    intent.treatment().allInstructions().stream()
-                            .filter(inst -> inst.type() != Instruction.Type.NOACTION)
-                            .forEach(treatmentBuilder::add);
-
-                    TrafficTreatment forwardingTreatment =
-                            forwardingTreatment(filteredIngressPoint.get(),
-                                                filteredEgressPoint.get());
-                    forwardingTreatment.allInstructions().stream()
-                            .filter(inst -> inst.type() != Instruction.Type.NOACTION)
-                            .forEach(treatmentBuilder::add);
-                }
-
-                treatmentBuilder.setOutput(outPort);
-
-            }
-
-        }
-
-
-        return new ForwardingInstructions(treatmentBuilder.build(), selectorBuilder.build());
-
-    }
-
-    /**
      * Get FilteredConnectPoint from LinkCollectionIntent.
+     *
      * @param deviceId device Id for connect point
      * @param portNumber port number
      * @param intent source intent
@@ -320,15 +854,17 @@
      * Compares tag type between ingress and egress point and generate
      * treatment for egress point of intent.
      *
-     * @param ingress ingress point for the intent
-     * @param egress egress point for the intent
+     * @param ingress ingress selector for the intent
+     * @param egress egress selector for the intent
+     * @param ethType the ethertype to use in mpls_pop
      * @return Builder of TrafficTreatment
      */
-    private TrafficTreatment forwardingTreatment(FilteredConnectPoint ingress,
-                                                         FilteredConnectPoint egress) {
+    private TrafficTreatment forwardingTreatment(TrafficSelector ingress,
+                                                 TrafficSelector egress,
+                                                 EthType ethType) {
 
 
-        if (ingress.trafficSelector().equals(egress.trafficSelector())) {
+        if (ingress.equals(egress)) {
             return DefaultTrafficTreatment.emptyTreatment();
         }
 
@@ -338,8 +874,8 @@
          * "null" means there is no tag for the port
          * Tag criterion will be null if port is normal connection point
          */
-        Criterion ingressTagCriterion = getTagCriterion(ingress.trafficSelector());
-        Criterion egressTagCriterion = getTagCriterion(egress.trafficSelector());
+        Criterion ingressTagCriterion = getTagCriterion(ingress);
+        Criterion egressTagCriterion = getTagCriterion(egress);
 
         if (ingressTagCriterion.type() != egressTagCriterion.type()) {
 
@@ -353,11 +889,14 @@
                 case VLAN_VID:
                     builder.popVlan();
                     break;
+
                 case MPLS_LABEL:
-                    builder.popMpls();
+                    builder.popMpls(ethType);
                     break;
+
                 default:
                     break;
+
             }
 
             /*
@@ -367,11 +906,14 @@
                 case VLAN_VID:
                     builder.pushVlan();
                     break;
+
                 case MPLS_LABEL:
                     builder.pushMpls();
                     break;
+
                 default:
                     break;
+
             }
         }
 
@@ -380,16 +922,20 @@
                 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) egressTagCriterion;
                 builder.setVlanId(vlanIdCriterion.vlanId());
                 break;
+
             case MPLS_LABEL:
                 MplsCriterion mplsCriterion = (MplsCriterion) egressTagCriterion;
                 builder.setMpls(mplsCriterion.label());
                 break;
+
             case TUNNEL_ID:
                 TunnelIdCriterion tunnelIdCriterion = (TunnelIdCriterion) egressTagCriterion;
                 builder.setTunnelId(tunnelIdCriterion.tunnelId());
                 break;
+
             default:
                 break;
+
         }
 
         return builder.build();
@@ -402,7 +948,7 @@
      * @param l0instruction the l0 instruction to use
      */
     private void updateBuilder(TrafficSelector.Builder builder, L0ModificationInstruction l0instruction) {
-        throw new IntentCompilationException("L0 not supported");
+        throw new IntentCompilationException(UNSUPPORTED_L0);
     }
 
     /**
@@ -412,7 +958,7 @@
      * @param l1instruction the l1 instruction to use
      */
     private void updateBuilder(TrafficSelector.Builder builder, L1ModificationInstruction l1instruction) {
-        throw new IntentCompilationException("L1 not supported");
+        throw new IntentCompilationException(UNSUPPORTED_L1);
     }
 
     /**
@@ -430,49 +976,59 @@
                     case ETH_SRC:
                         builder.matchEthSrc(ethInstr.mac());
                         break;
+
                     case ETH_DST:
                         builder.matchEthDst(ethInstr.mac());
                         break;
+
                     default:
-                        throw new IntentCompilationException("Bad eth subtype");
+                        throw new IntentCompilationException(UNSUPPORTED_ETH_SUBTYPE);
                 }
                 break;
+
             case VLAN_ID:
                 ModVlanIdInstruction vlanIdInstr = (ModVlanIdInstruction) l2instruction;
                 builder.matchVlanId(vlanIdInstr.vlanId());
                 break;
+
             case VLAN_PUSH:
                 //FIXME
                 break;
+
             case VLAN_POP:
                 //TODO how do we handle dropped label? remove the selector?
-                throw new IntentCompilationException("Can't handle pop label");
+                throw new IntentCompilationException(UNSUPPORTED_POP_ACTION);
             case VLAN_PCP:
                 ModVlanPcpInstruction vlanPcpInstruction = (ModVlanPcpInstruction) l2instruction;
                 builder.matchVlanPcp(vlanPcpInstruction.vlanPcp());
                 break;
+
             case MPLS_LABEL:
             case MPLS_PUSH:
                 //FIXME
                 ModMplsLabelInstruction mplsInstr = (ModMplsLabelInstruction) l2instruction;
                 builder.matchMplsLabel(mplsInstr.label());
                 break;
+
             case MPLS_POP:
                 //TODO how do we handle dropped label? remove the selector?
-                throw new IntentCompilationException("Can't handle pop label");
+                throw new IntentCompilationException(UNSUPPORTED_POP_ACTION);
             case DEC_MPLS_TTL:
                 // no-op
                 break;
+
             case MPLS_BOS:
                 ModMplsBosInstruction mplsBosInstr = (ModMplsBosInstruction) l2instruction;
                 builder.matchMplsBos(mplsBosInstr.mplsBos());
                 break;
+
             case TUNNEL_ID:
                 ModTunnelIdInstruction tunInstr = (ModTunnelIdInstruction) l2instruction;
                 builder.matchTunnelId(tunInstr.tunnelId());
                 break;
+
             default:
-                throw new IntentCompilationException("Unknown L2 Modification instruction");
+                throw new IntentCompilationException(UNSUPPORTED_L2);
         }
 
     }
@@ -497,51 +1053,63 @@
                     case IPV4_SRC:
                         builder.matchIPSrc(prefix);
                         break;
+
                     case IPV4_DST:
                         builder.matchIPSrc(prefix);
                         break;
+
                     case IPV6_SRC:
                         builder.matchIPv6Src(prefix);
                         break;
+
                     case IPV6_DST:
                         builder.matchIPv6Dst(prefix);
                         break;
+
                     default:
-                        throw new IntentCompilationException("Bad type for IP instruction");
+                        throw new IntentCompilationException(UNSUPPORTED_IP_SUBTYPE);
                 }
                 break;
+
             case IPV6_FLABEL:
                 ModIPv6FlowLabelInstruction ipFlowInstr = (ModIPv6FlowLabelInstruction) l3instruction;
                 builder.matchIPv6FlowLabel(ipFlowInstr.flowLabel());
                 break;
+
             case DEC_TTL:
                 // no-op
                 break;
+
             case TTL_OUT:
                 // no-op
                 break;
+
             case TTL_IN:
                 // no-op
                 break;
+
             case ARP_SPA:
                 ModArpIPInstruction arpIpInstr = (ModArpIPInstruction) l3instruction;
                 if (arpIpInstr.ip().isIp4()) {
                     builder.matchArpSpa((Ip4Address) arpIpInstr.ip());
                 } else {
-                    throw new IntentCompilationException("IPv6 not supported for ARP");
+                    throw new IntentCompilationException(UNSUPPORTED_ARP);
                 }
                 break;
+
             case ARP_SHA:
                 ModArpEthInstruction arpEthInstr = (ModArpEthInstruction) l3instruction;
                 builder.matchArpSha(arpEthInstr.mac());
                 break;
+
             case ARP_OP:
                 ModArpOpInstruction arpOpInstr = (ModArpOpInstruction) l3instruction;
                 //FIXME is the long to int cast safe?
                 builder.matchArpOp((int) arpOpInstr.op());
                 break;
+
             default:
-                throw new IntentCompilationException("Unknown L3 Modification instruction");
+                throw new IntentCompilationException(UNSUPPORTED_L3);
         }
     }
 
@@ -559,20 +1127,24 @@
                 case TCP_SRC:
                     builder.matchTcpSrc(l4mod.port());
                     break;
+
                 case TCP_DST:
                     builder.matchTcpDst(l4mod.port());
                     break;
+
                 case UDP_SRC:
                     builder.matchUdpSrc(l4mod.port());
                     break;
+
                 case UDP_DST:
                     builder.matchUdpDst(l4mod.port());
                     break;
+
                 default:
-                    throw new IntentCompilationException("Unknown L4 Modification instruction");
+                    throw new IntentCompilationException(UNSUPPORTED_L4_SUBTYPE);
             }
         } else {
-            throw new IntentCompilationException("Unknown L4 Modification instruction");
+            throw new IntentCompilationException(UNSUPPORTED_L4);
         }
     }
 
@@ -589,18 +1161,23 @@
                 case L0MODIFICATION:
                     updateBuilder(builder, (L0ModificationInstruction) instruction);
                     break;
+
                 case L1MODIFICATION:
                     updateBuilder(builder, (L1ModificationInstruction) instruction);
                     break;
+
                 case L2MODIFICATION:
                     updateBuilder(builder, (L2ModificationInstruction) instruction);
                     break;
+
                 case L3MODIFICATION:
                     updateBuilder(builder, (L3ModificationInstruction) instruction);
                     break;
+
                 case L4MODIFICATION:
                     updateBuilder(builder, (L4ModificationInstruction) instruction);
                     break;
+
                 case NOACTION:
                 case OUTPUT:
                 case GROUP:
@@ -611,11 +1188,57 @@
                 case EXTENSION: // TODO is extension no-op or unsupported?
                     // Nothing to do
                     break;
+
                 default:
-                    throw new IntentCompilationException("Unknown instruction type");
+                    throw new IntentCompilationException(UNSUPPORTED_INSTRUCTION);
             }
         });
 
     }
 
+    /**
+     * The method generates a selector starting from
+     * the encapsulation information (type and label to match).
+     *
+     * @param selectorBuilder the builder to update
+     * @param type the type of encapsulation
+     * @param identifier the label to match
+     */
+    private void updateSelectorFromEncapsulation(TrafficSelector.Builder selectorBuilder,
+                                                 EncapsulationType type,
+                                                 Identifier<?> identifier) {
+        switch (type) {
+            case MPLS:
+                MplsLabel label = (MplsLabel) identifier;
+                selectorBuilder.matchMplsLabel(label);
+                selectorBuilder.matchEthType(Ethernet.MPLS_UNICAST);
+                break;
+
+            case VLAN:
+                VlanId id = (VlanId) identifier;
+                selectorBuilder.matchVlanId(id);
+                break;
+
+            default:
+                throw new IntentCompilationException(UNKNOWN_ENCAPSULATION);
+        }
+    }
+
+    /**
+     * Helper function to define the match on the ethertype.
+     * If the selector define an ethertype we will use it,
+     * otherwise IPv4 will be used by default.
+     *
+     * @param selector the traffic selector
+     * @return the ethertype we should match
+     */
+    private EthType getEthType(TrafficSelector selector) {
+        Criterion c = selector.getCriterion(Criterion.Type.ETH_TYPE);
+        if (c != null && c instanceof EthTypeCriterion) {
+            EthTypeCriterion ethertype = (EthTypeCriterion) c;
+            return ethertype.ethType();
+        }
+        return EthType.EtherType.IPV4.ethType();
+    }
+
 }
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 1f22429..e464e99 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
@@ -16,16 +16,17 @@
 package org.onosproject.net.intent.impl.compiler;
 
 import com.google.common.collect.HashMultimap;
-import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.SetMultimap;
-import com.google.common.collect.Sets;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.util.Identifier;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
+import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.flow.DefaultFlowRule;
@@ -34,12 +35,17 @@
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentCompiler;
 import org.onosproject.net.intent.LinkCollectionIntent;
+import org.onosproject.net.intent.constraint.EncapsulationConstraint;
+import org.onosproject.net.resource.ResourceService;
+import org.onosproject.net.resource.impl.LabelAllocator;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 
 /**
@@ -59,12 +65,18 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CoreService coreService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ResourceService resourceService;
+
     private ApplicationId appId;
 
     @Activate
     public void activate() {
         appId = coreService.registerApplication("org.onosproject.net.intent");
         registrator.registerCompiler(LinkCollectionIntent.class, this, false);
+        if (labelAllocator == null) {
+            labelAllocator = new LabelAllocator(resourceService);
+        }
     }
 
     @Deactivate
@@ -77,36 +89,55 @@
 
         SetMultimap<DeviceId, PortNumber> inputPorts = HashMultimap.create();
         SetMultimap<DeviceId, PortNumber> outputPorts = HashMultimap.create();
+        Map<ConnectPoint, Identifier<?>> labels = ImmutableMap.of();
+
+        Optional<EncapsulationConstraint> encapConstraint = this.getIntentEncapConstraint(intent);
 
         computePorts(intent, inputPorts, outputPorts);
 
+        if (encapConstraint.isPresent()) {
+            labels = labelAllocator.assignLabelToPorts(intent.links(),
+                                                       intent.id(),
+                                                       encapConstraint.get().encapType());
+        }
+
         List<FlowRule> rules = new ArrayList<>();
         for (DeviceId deviceId: outputPorts.keySet()) {
-            rules.addAll(createRules(intent, deviceId, inputPorts.get(deviceId), outputPorts.get(deviceId)));
+            rules.addAll(createRules(
+                    intent,
+                    deviceId,
+                    inputPorts.get(deviceId),
+                    outputPorts.get(deviceId),
+                    labels)
+            );
         }
         return Collections.singletonList(new FlowRuleIntent(appId, rules, intent.resources()));
     }
 
     @Override
-    protected List<FlowRule> createRules(LinkCollectionIntent intent, DeviceId deviceId,
-                                       Set<PortNumber> inPorts, Set<PortNumber> outPorts) {
-
-        Set<PortNumber> ingressPorts = Sets.newHashSet();
-        Set<PortNumber> egressPorts = Sets.newHashSet();
-
-        computePorts(intent, deviceId, ingressPorts, egressPorts);
+    protected List<FlowRule> createRules(LinkCollectionIntent intent,
+                                         DeviceId deviceId,
+                                         Set<PortNumber> inPorts,
+                                         Set<PortNumber> outPorts,
+                                         Map<ConnectPoint, Identifier<?>> labels) {
 
         List<FlowRule> rules = new ArrayList<>(inPorts.size());
-        Set<PortNumber> copyIngressPorts = ImmutableSet.copyOf(ingressPorts);
-        Set<PortNumber> copyEgressPorts = ImmutableSet.copyOf(egressPorts);
+        /*
+         * Looking for the encapsulation constraint
+         */
+        Optional<EncapsulationConstraint> encapConstraint = this.getIntentEncapConstraint(intent);
 
         inPorts.forEach(inport -> {
-                ForwardingInstructions instructions = this.createForwardingInstructions(intent,
-                                                                                        inport,
-                                                                                        deviceId,
-                                                                                        outPorts,
-                                                                                        copyIngressPorts,
-                                                                                        copyEgressPorts);
+
+                ForwardingInstructions instructions = this.createForwardingInstruction(
+                        encapConstraint,
+                        intent,
+                        inport,
+                        outPorts,
+                        deviceId,
+                        labels
+                );
+
                 FlowRule rule = DefaultFlowRule.builder()
                         .forDevice(deviceId)
                         .withSelector(instructions.selector())
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentFlowObjectiveCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentFlowObjectiveCompiler.java
index 0d6ad0a..0a15097 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentFlowObjectiveCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentFlowObjectiveCompiler.java
@@ -16,16 +16,17 @@
 package org.onosproject.net.intent.impl.compiler;
 
 import com.google.common.collect.HashMultimap;
-import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.SetMultimap;
-import com.google.common.collect.Sets;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.util.Identifier;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
+import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.flowobjective.DefaultForwardingObjective;
@@ -38,10 +39,15 @@
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentCompiler;
 import org.onosproject.net.intent.LinkCollectionIntent;
+import org.onosproject.net.intent.constraint.EncapsulationConstraint;
+import org.onosproject.net.resource.ResourceService;
+import org.onosproject.net.resource.impl.LabelAllocator;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 
 /**
@@ -61,12 +67,19 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected FlowObjectiveService flowObjectiveService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ResourceService resourceService;
+
     private ApplicationId appId;
 
     @Activate
     public void activate() {
         appId = coreService.registerApplication("org.onosproject.net.intent");
         registrator.registerCompiler(LinkCollectionIntent.class, this, true);
+        if (labelAllocator == null) {
+            labelAllocator = new LabelAllocator(resourceService);
+        }
+
     }
 
     @Deactivate
@@ -79,9 +92,18 @@
 
         SetMultimap<DeviceId, PortNumber> inputPorts = HashMultimap.create();
         SetMultimap<DeviceId, PortNumber> outputPorts = HashMultimap.create();
+        Map<ConnectPoint, Identifier<?>> labels = ImmutableMap.of();
+
+        Optional<EncapsulationConstraint> encapConstraint = this.getIntentEncapConstraint(intent);
 
         computePorts(intent, inputPorts, outputPorts);
 
+        if (encapConstraint.isPresent()) {
+            labels = labelAllocator.assignLabelToPorts(intent.links(),
+                                                       intent.id(),
+                                                       encapConstraint.get().encapType());
+        }
+
         List<Objective> objectives = new ArrayList<>();
         List<DeviceId> devices = new ArrayList<>();
         for (DeviceId deviceId: outputPorts.keys()) {
@@ -89,7 +111,8 @@
                     createRules(intent,
                                 deviceId,
                                 inputPorts.get(deviceId),
-                                outputPorts.get(deviceId));
+                                outputPorts.get(deviceId),
+                                labels);
             deviceObjectives.forEach(objective -> {
                 objectives.add(objective);
                 devices.add(deviceId);
@@ -100,25 +123,29 @@
     }
 
     @Override
-    protected List<Objective> createRules(LinkCollectionIntent intent, DeviceId deviceId,
-                                       Set<PortNumber> inPorts, Set<PortNumber> outPorts) {
-
-        Set<PortNumber> ingressPorts = Sets.newHashSet();
-        Set<PortNumber> egressPorts = Sets.newHashSet();
-
-        computePorts(intent, deviceId, ingressPorts, egressPorts);
+    protected List<Objective> createRules(LinkCollectionIntent intent,
+                                          DeviceId deviceId,
+                                          Set<PortNumber> inPorts,
+                                          Set<PortNumber> outPorts,
+                                          Map<ConnectPoint, Identifier<?>> labels) {
 
         List<Objective> objectives = new ArrayList<>(inPorts.size());
-        Set<PortNumber> copyIngressPorts = ImmutableSet.copyOf(ingressPorts);
-        Set<PortNumber> copyEgressPorts = ImmutableSet.copyOf(egressPorts);
+
+        /*
+         * Looking for the encapsulation constraint
+         */
+        Optional<EncapsulationConstraint> encapConstraint = this.getIntentEncapConstraint(intent);
 
         inPorts.forEach(inport -> {
-            ForwardingInstructions instructions = this.createForwardingInstructions(intent,
-                                                                                    inport,
-                                                                                    deviceId,
-                                                                                    outPorts,
-                                                                                    copyIngressPorts,
-                                                                                    copyEgressPorts);
+
+            ForwardingInstructions instructions = this.createForwardingInstruction(
+                    encapConstraint,
+                    intent,
+                    inport,
+                    outPorts,
+                    deviceId,
+                    labels
+            );
 
             NextObjective nextObjective = DefaultNextObjective.builder()
                     .withId(flowObjectiveService.allocateNextId())
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
new file mode 100644
index 0000000..9a8324c
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/AbstractLinkCollectionTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.onosproject.TestApplicationId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.intent.Constraint;
+import org.onosproject.net.intent.IntentExtensionService;
+import org.onosproject.net.intent.LinkCollectionIntent;
+import org.onosproject.net.intent.MockIdGenerator;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.onosproject.net.NetTestTools.*;
+import static org.onosproject.net.NetTestTools.macDstTreatment;
+
+/**
+ * Abstract class to hold the common variables and pieces
+ * of code.
+ */
+class AbstractLinkCollectionTest {
+
+    static final String LABEL_SELECTION = "FIRST_FIT";
+    static final String LABEL = "1";
+
+    final ApplicationId appId = new TestApplicationId("test");
+
+    final ConnectPoint d2p0 = connectPoint("s2", 0);
+    final ConnectPoint d2p1 = connectPoint("s2", 1);
+    ConnectPoint d2p10 = connectPoint("s2", 10);
+
+    final ConnectPoint d3p0 = connectPoint("s3", 0);
+    final ConnectPoint d3p1 = connectPoint("s3", 1);
+    final ConnectPoint d3p10 = connectPoint("s3", 10);
+
+    final DeviceId of1Id = DeviceId.deviceId("of:of1");
+    final DeviceId of2Id = DeviceId.deviceId("of:of2");
+    final DeviceId of3Id = DeviceId.deviceId("of:of3");
+    final DeviceId of4Id = DeviceId.deviceId("of:of4");
+
+    final ConnectPoint of1p1 = connectPoint("of1", 1);
+    final ConnectPoint of1p2 = connectPoint("of1", 2);
+    final ConnectPoint of2p1 = connectPoint("of2", 1);
+    final ConnectPoint of2p2 = connectPoint("of2", 2);
+    final ConnectPoint of2p3 = connectPoint("of2", 3);
+    final ConnectPoint of3p1 = connectPoint("of3", 1);
+    final ConnectPoint of3p2 = connectPoint("of3", 2);
+    final ConnectPoint of4p1 = connectPoint("of4", 1);
+    final ConnectPoint of4p2 = connectPoint("of4", 2);
+
+    final ConnectPoint d1p0 = connectPoint("s1", 0);
+    final ConnectPoint d1p1 = connectPoint("s1", 1);
+    final ConnectPoint d1p10 = connectPoint("s1", 10);
+    final ConnectPoint d1p11 = connectPoint("s1", 11);
+
+    final Set<Link> links = ImmutableSet.of(
+            link(d1p1, d2p0),
+            link(d2p1, d3p1),
+            link(d1p1, d3p1)
+    );
+
+    final Set<Link> linksForMp2Sp = ImmutableSet.of(
+            link(d1p0, d2p0),
+            link(d2p1, d3p0)
+    );
+
+
+
+    final Set<Link> linksForSp2Mp = ImmutableSet.of(
+            link(d3p0, d2p1),
+            link(d2p0, d1p0)
+    );
+
+    final TrafficTreatment treatment = emptyTreatment();
+
+    final TrafficSelector selector = emptySelector();
+    final TrafficSelector vlan69Selector = vlanSelector("69");
+    final TrafficSelector vlan100Selector = vlanSelector("100");
+    final TrafficSelector vlan200Selector = vlanSelector("200");
+    final TrafficSelector vlan300Selector = vlanSelector("300");
+    final TrafficSelector mpls69Selector = mplsSelector("69");
+    final TrafficSelector mpls80Selector = mplsSelector("80");
+    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();
+
+    CoreService coreService;
+    IntentExtensionService intentExtensionService;
+    IntentConfigurableRegistrator registrator;
+    IdGenerator idGenerator = new MockIdGenerator();
+
+    LinkCollectionIntent intent;
+
+    LinkCollectionIntentCompiler sut;
+
+    List<FlowRule> getFlowRulesByDevice(DeviceId deviceId, Collection<FlowRule> flowRules) {
+        return flowRules.stream()
+                .filter(fr -> fr.deviceId().equals(deviceId))
+                .collect(Collectors.toList());
+    }
+
+}
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
new file mode 100644
index 0000000..588880a
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionEncapIntentCompilerTest.java
@@ -0,0 +1,1354 @@
+/*
+ * 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.PortCriterion;
+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.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+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 LinkCollectionEncapIntentCompilerTest 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();
+
+        replay(coreService, intentExtensionService);
+    }
+
+    @After
+    public void tearDown() {
+        Intent.unbindIdGenerator(idGenerator);
+    }
+
+    /**
+     * We test the proper compilation of mp2Sp1 with the VLAN
+     * encapsulation, trivial selector.
+     */
+    @Test
+    public void testVlanEncapsulationForMp() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(treatment)
+                .constraints(constraintsForVlan)
+                .links(linksForMp2Sp)
+                .filteredIngressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d1p10),
+                        new FilteredConnectPoint(d1p11),
+                        new FilteredConnectPoint(d2p10)
+                ))
+                .filteredEgressPoints(ImmutableSet.of(new FilteredConnectPoint(d3p10)))
+                .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(5));
+
+        Collection<FlowRule> rulesS1 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d1p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS1, hasSize(2));
+        FlowRule ruleS1 = rulesS1.stream()
+                .filter(rule -> {
+                        PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                        return inPort.port().equals(d1p10.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS1.selector(), is(
+                DefaultTrafficSelector
+                        .builder(intent.selector())
+                        .matchInPort(d1p10.port())
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .pushVlan()
+                        .setVlanId(VlanId.vlanId(LABEL))
+                        .setOutput(d1p0.port())
+                        .build()
+        ));
+
+        ruleS1 = rulesS1.stream()
+                .filter(rule -> {
+                    PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                    return inPort.port().equals(d1p11.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS1.selector(), is(
+                DefaultTrafficSelector
+                        .builder(intent.selector())
+                        .matchInPort(d1p11.port())
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .pushVlan()
+                        .setVlanId(VlanId.vlanId(LABEL))
+                        .setOutput(d1p0.port())
+                        .build()
+        ));
+
+        Collection<FlowRule> rulesS2 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d2p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS2, hasSize(2));
+        FlowRule ruleS2 = rulesS2.stream()
+                .filter(rule -> {
+                    PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                    return inPort.port().equals(d2p10.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS2.selector(), is(
+                DefaultTrafficSelector
+                        .builder(intent.selector())
+                        .matchInPort(d2p10.port())
+                        .build()
+        ));
+        assertThat(ruleS2.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .pushVlan()
+                        .setVlanId(VlanId.vlanId(LABEL))
+                        .setOutput(d2p1.port())
+                        .build()
+        ));
+
+        ruleS2 = rulesS2.stream()
+                .filter(rule -> {
+                    PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                    return inPort.port().equals(d2p0.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS2.selector(), is(
+                DefaultTrafficSelector
+                        .builder()
+                        .matchInPort(d2p0.port())
+                        .matchVlanId(VlanId.vlanId(LABEL))
+                        .build()
+        ));
+        assertThat(ruleS2.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setVlanId(VlanId.vlanId(LABEL))
+                        .setOutput(d2p1.port())
+                        .build()
+        ));
+
+        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(d3p0.port())
+                        .matchVlanId(VlanId.vlanId(LABEL))
+                        .build()
+        ));
+        assertThat(ruleS3.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .popVlan()
+                        .setOutput(d3p10.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+    }
+
+    /**
+     * We test the proper compilation of sp2mp with the MPLS
+     * encapsulation and trivial selector.
+     */
+    @Test
+    public void testMplsEncapsulationForSp() {
+
+        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()
+                        .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())
+                        .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()
+                        .popMpls(IPV4.ethType())
+                        .setOutput(d1p10.port())
+                        .popMpls(IPV4.ethType())
+                        .setOutput(d1p11.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+
+    }
+
+    /**
+     * We test the proper compilation of mp2sp with the MPLS
+     * encapsulation and filtered selector.
+     */
+    @Test
+    public void testMplsEncapsulationFilteredForMp() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(treatment)
+                .constraints(constraintsForMPLS)
+                .links(linksForMp2Sp)
+                .filteredEgressPoints(ImmutableSet.of(new FilteredConnectPoint(d3p10, vlan69Selector)))
+                .filteredIngressPoints(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(5));
+
+        Collection<FlowRule> rulesS1 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d1p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS1, hasSize(2));
+        FlowRule ruleS1 = rulesS1.stream()
+                .filter(rule -> {
+                    PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                    return inPort.port().equals(d1p10.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS1.selector(), is(
+                DefaultTrafficSelector
+                        .builder(vlan100Selector)
+                        .matchInPort(d1p10.port())
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .popVlan()
+                        .pushMpls()
+                        .setMpls(MplsLabel.mplsLabel(LABEL))
+                        .setOutput(d1p0.port())
+                        .build()
+        ));
+
+        ruleS1 = rulesS1.stream()
+                .filter(rule -> {
+                    PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                    return inPort.port().equals(d1p11.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS1.selector(), is(
+                DefaultTrafficSelector
+                        .builder(vlan200Selector)
+                        .matchInPort(d1p11.port())
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .popVlan()
+                        .pushMpls()
+                        .setMpls(MplsLabel.mplsLabel(LABEL))
+                        .setOutput(d1p0.port())
+                        .build()
+        ));
+
+        Collection<FlowRule> rulesS2 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d2p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS2, hasSize(2));
+        FlowRule ruleS2 = rulesS2.stream()
+                .filter(rule -> {
+                    PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                    return inPort.port().equals(d2p10.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS2.selector(), is(
+                DefaultTrafficSelector
+                        .builder(vlan300Selector)
+                        .matchInPort(d2p10.port())
+                        .build()
+        ));
+        assertThat(ruleS2.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .popVlan()
+                        .pushMpls()
+                        .setMpls(MplsLabel.mplsLabel(LABEL))
+                        .setOutput(d2p1.port())
+                        .build()
+        ));
+
+        ruleS2 = rulesS2.stream()
+                .filter(rule -> {
+                    PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                    return inPort.port().equals(d2p0.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS2.selector(), is(
+                DefaultTrafficSelector
+                        .builder()
+                        .matchInPort(d2p0.port())
+                        .matchMplsLabel(MplsLabel.mplsLabel(LABEL))
+                        .matchEthType(Ethernet.MPLS_UNICAST)
+                        .build()
+        ));
+        assertThat(ruleS2.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setMpls(MplsLabel.mplsLabel(LABEL))
+                        .setOutput(d2p1.port())
+                        .build()
+        ));
+
+        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(d3p0.port())
+                        .matchMplsLabel(MplsLabel.mplsLabel(LABEL))
+                        .matchEthType(Ethernet.MPLS_UNICAST)
+                        .build()
+        ));
+        assertThat(ruleS3.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .popMpls(IPV4.ethType())
+                        .pushVlan()
+                        .setVlanId(((VlanIdCriterion) vlan69Selector.getCriterion(VLAN_VID)).vlanId())
+                        .setOutput(d3p10.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+
+    }
+
+    /**
+     * We test the proper compilation of sp2mp with the VLAN
+     * encapsulation and filtered selector.
+     */
+    @Test
+    public void testVlanEncapsulationFilteredForSp() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(treatment)
+                .constraints(constraintsForVlan)
+                .links(linksForSp2Mp)
+                .filteredIngressPoints(ImmutableSet.of(new FilteredConnectPoint(d3p10, mpls69Selector)))
+                .filteredEgressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d1p10, mpls100Selector),
+                        new FilteredConnectPoint(d1p11, mpls200Selector),
+                        new FilteredConnectPoint(d2p10, mpls80Selector)
+                ))
+                .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(mpls69Selector)
+                        .matchInPort(d3p10.port())
+                        .build()
+        ));
+        assertThat(ruleS3.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .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()
+                        .setMpls(((MplsCriterion) mpls80Selector.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()
+                        .setMpls(((MplsCriterion) mpls100Selector.getCriterion(MPLS_LABEL)).label())
+                        .setOutput(d1p10.port())
+                        .popVlan()
+                        .pushMpls()
+                        .setMpls(((MplsCriterion) mpls200Selector.getCriterion(MPLS_LABEL)).label())
+                        .setOutput(d1p11.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+
+    }
+
+    /**
+     * We test the proper compilation of mp2sp with the VLAN
+     * encapsulation, filtered selectors, intent selector and intent treatment.
+     */
+    @Test
+    public void testVlanEncapsulationNonTrivialForMp() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(ipPrefixSelector)
+                .treatment(ethDstTreatment)
+                .constraints(constraintsForVlan)
+                .links(linksForMp2Sp)
+                .filteredEgressPoints(ImmutableSet.of(new FilteredConnectPoint(d3p10, mpls69Selector)))
+                .filteredIngressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d1p10, mpls80Selector),
+                        new FilteredConnectPoint(d1p11, mpls100Selector),
+                        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(5));
+
+        Collection<FlowRule> rulesS1 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d1p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS1, hasSize(2));
+        FlowRule ruleS1 = rulesS1.stream()
+                .filter(rule -> {
+                    PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                    return inPort.port().equals(d1p10.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS1.selector(), is(
+                DefaultTrafficSelector
+                        .builder(ipPrefixSelector)
+                        .matchInPort(d1p10.port())
+                        .matchMplsLabel(((MplsCriterion) mpls80Selector.getCriterion(MPLS_LABEL)).label())
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .popMpls(IPV4.ethType())
+                        .pushVlan()
+                        .setVlanId(VlanId.vlanId(LABEL))
+                        .setOutput(d1p0.port())
+                        .build()
+        ));
+
+        ruleS1 = rulesS1.stream()
+                .filter(rule -> {
+                    PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                    return inPort.port().equals(d1p11.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS1.selector(), is(
+                DefaultTrafficSelector
+                        .builder(ipPrefixSelector)
+                        .matchInPort(d1p11.port())
+                        .matchMplsLabel(((MplsCriterion) mpls100Selector.getCriterion(MPLS_LABEL)).label())
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .popMpls(IPV4.ethType())
+                        .pushVlan()
+                        .setVlanId(VlanId.vlanId(LABEL))
+                        .setOutput(d1p0.port())
+                        .build()
+        ));
+
+        Collection<FlowRule> rulesS2 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d2p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS2, hasSize(2));
+        FlowRule ruleS2 = rulesS2.stream()
+                .filter(rule -> {
+                    PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                    return inPort.port().equals(d2p10.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS2.selector(), is(
+                DefaultTrafficSelector
+                        .builder(ipPrefixSelector)
+                        .matchInPort(d2p10.port())
+                        .matchMplsLabel(((MplsCriterion) mpls200Selector.getCriterion(MPLS_LABEL)).label())
+                        .build()
+        ));
+        assertThat(ruleS2.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .popMpls(IPV4.ethType())
+                        .pushVlan()
+                        .setVlanId(VlanId.vlanId(LABEL))
+                        .setOutput(d2p1.port())
+                        .build()
+        ));
+
+        ruleS2 = rulesS2.stream()
+                .filter(rule -> {
+                    PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                    return inPort.port().equals(d2p0.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS2.selector(), is(
+                DefaultTrafficSelector
+                        .builder()
+                        .matchInPort(d2p0.port())
+                        .matchVlanId(VlanId.vlanId(LABEL))
+                        .build()
+        ));
+        assertThat(ruleS2.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setVlanId(VlanId.vlanId(LABEL))
+                        .setOutput(d2p1.port())
+                        .build()
+        ));
+
+        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(d3p0.port())
+                        .matchVlanId(VlanId.vlanId(LABEL))
+                        .build()
+        ));
+        assertThat(ruleS3.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder(ethDstTreatment)
+                        .popVlan()
+                        .pushMpls()
+                        .setMpls(((MplsCriterion) mpls69Selector.getCriterion(MPLS_LABEL)).label())
+                        .setOutput(d3p10.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+
+    }
+
+    /**
+     * We test the proper compilation of sp2mp with the MPLS
+     * encapsulation, filtered selector, intent selector, and
+     * intent treatment.
+     */
+    @Test
+    public void testMplsEncapsulationNonTrivialForSp() {
+
+        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()
+                        .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())
+                        .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())
+                        .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())
+                        .popMpls(IPV4.ethType())
+                        .pushVlan()
+                        .setVlanId(((VlanIdCriterion) vlan200Selector.getCriterion(VLAN_VID)).vlanId())
+                        .setOutput(d1p11.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+
+    }
+
+    /**
+     * We test the proper compilation of mp2sp with the MPLS
+     * encapsulation and filtered selectors of different type.
+     */
+    @Test
+    public void testMplsEncapsulationDifferentFilterForMp() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(treatment)
+                .constraints(constraintsForMPLS)
+                .links(linksForMp2Sp)
+                .filteredEgressPoints(ImmutableSet.of(new FilteredConnectPoint(d3p10, mpls100Selector)))
+                .filteredIngressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d1p10, vlan100Selector),
+                        new FilteredConnectPoint(d1p11, mpls200Selector),
+                        new FilteredConnectPoint(d2p10, vlan200Selector)
+                ))
+                .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(5));
+
+        Collection<FlowRule> rulesS1 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d1p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS1, hasSize(2));
+        FlowRule ruleS1 = rulesS1.stream()
+                .filter(rule -> {
+                    PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                    return inPort.port().equals(d1p10.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS1.selector(), is(
+                DefaultTrafficSelector
+                        .builder(vlan100Selector)
+                        .matchInPort(d1p10.port())
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .popVlan()
+                        .pushMpls()
+                        .setMpls(MplsLabel.mplsLabel(LABEL))
+                        .setOutput(d1p0.port())
+                        .build()
+        ));
+
+        ruleS1 = rulesS1.stream()
+                .filter(rule -> {
+                    PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                    return inPort.port().equals(d1p11.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS1.selector(), is(
+                DefaultTrafficSelector
+                        .builder(mpls200Selector)
+                        .matchInPort(d1p11.port())
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setMpls(MplsLabel.mplsLabel(LABEL))
+                        .setOutput(d1p0.port())
+                        .build()
+        ));
+
+        Collection<FlowRule> rulesS2 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d2p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS2, hasSize(2));
+        FlowRule ruleS2 = rulesS2.stream()
+                .filter(rule -> {
+                    PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                    return inPort.port().equals(d2p10.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS2.selector(), is(
+                DefaultTrafficSelector
+                        .builder(vlan200Selector)
+                        .matchInPort(d2p10.port())
+                        .build()
+        ));
+        assertThat(ruleS2.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .popVlan()
+                        .pushMpls()
+                        .setMpls(MplsLabel.mplsLabel(LABEL))
+                        .setOutput(d2p1.port())
+                        .build()
+        ));
+
+        ruleS2 = rulesS2.stream()
+                .filter(rule -> {
+                    PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                    return inPort.port().equals(d2p0.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS2.selector(), is(
+                DefaultTrafficSelector
+                        .builder()
+                        .matchInPort(d2p0.port())
+                        .matchMplsLabel(MplsLabel.mplsLabel(LABEL))
+                        .matchEthType(Ethernet.MPLS_UNICAST)
+                        .build()
+        ));
+        assertThat(ruleS2.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setMpls(MplsLabel.mplsLabel(LABEL))
+                        .setOutput(d2p1.port())
+                        .build()
+        ));
+
+        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(d3p0.port())
+                        .matchMplsLabel(MplsLabel.mplsLabel(LABEL))
+                        .matchEthType(Ethernet.MPLS_UNICAST)
+                        .build()
+        ));
+        assertThat(ruleS3.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setMpls(((MplsCriterion) mpls100Selector.getCriterion(MPLS_LABEL)).label())
+                        .setOutput(d3p10.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+
+    }
+
+    /**
+     * We test the proper compilation of sp2mp with the VLAN
+     * encapsulation and filtered selectors of different type.
+     */
+    @Test
+    public void testVlanEncapsulationDifferentFilter() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(treatment)
+                .constraints(constraintsForVlan)
+                .links(linksForSp2Mp)
+                .filteredIngressPoints(ImmutableSet.of(new FilteredConnectPoint(d3p10, vlan200Selector)))
+                .filteredEgressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d1p10, mpls100Selector),
+                        new FilteredConnectPoint(d1p11, vlan100Selector),
+                        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(vlan200Selector)
+                        .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()
+                        .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()
+                        .popVlan()
+                        .pushMpls()
+                        .setMpls(((MplsCriterion) mpls100Selector.getCriterion(MPLS_LABEL)).label())
+                        .setOutput(d1p10.port())
+                        .setVlanId(((VlanIdCriterion) vlan100Selector.getCriterion(VLAN_VID)).vlanId())
+                        .setOutput(d1p11.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+
+    }
+
+    /**
+     * We test the proper compilation of p2p with the VLAN
+     * encapsulation and filtered points.
+     */
+    @Test
+    public void testVlanEncapsulationForP2P() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(treatment)
+                .constraints(constraintsForVlan)
+                .links(linksForMp2Sp)
+                .filteredIngressPoints(ImmutableSet.of(new FilteredConnectPoint(d1p10, vlan100Selector)))
+                .filteredEgressPoints(ImmutableSet.of(new FilteredConnectPoint(d3p10, 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> 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(d1p10.port())
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setVlanId(VlanId.vlanId(LABEL))
+                        .setOutput(d1p0.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(d2p0.port())
+                        .matchVlanId(VlanId.vlanId(LABEL))
+                        .build()
+        ));
+        assertThat(ruleS2.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setVlanId(VlanId.vlanId(LABEL))
+                        .setOutput(d2p1.port())
+                        .build()
+        ));
+
+        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(d3p0.port())
+                        .matchVlanId(VlanId.vlanId(LABEL))
+                        .build()
+        ));
+        assertThat(ruleS3.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .popVlan()
+                        .pushMpls()
+                        .setMpls(((MplsCriterion) mpls200Selector.getCriterion(MPLS_LABEL)).label())
+                        .setOutput(d3p10.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+
+    }
+
+    /**
+     * We test the proper compilation of p2p with the MPLS
+     * encapsulation and filtered points.
+     */
+    @Test
+    public void testMplsEncapsulationForP2P() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(treatment)
+                .constraints(constraintsForMPLS)
+                .links(linksForMp2Sp)
+                .filteredIngressPoints(ImmutableSet.of(new FilteredConnectPoint(d1p10, vlan100Selector)))
+                .filteredEgressPoints(ImmutableSet.of(new FilteredConnectPoint(d3p10, 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> 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(d1p10.port())
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .popVlan()
+                        .pushMpls()
+                        .setMpls(MplsLabel.mplsLabel(LABEL))
+                        .setOutput(d1p0.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(d2p0.port())
+                        .matchMplsLabel(MplsLabel.mplsLabel(LABEL))
+                        .matchEthType(Ethernet.MPLS_UNICAST)
+                        .build()
+        ));
+        assertThat(ruleS2.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setMpls(MplsLabel.mplsLabel(LABEL))
+                        .setOutput(d2p1.port())
+                        .build()
+        ));
+
+        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(d3p0.port())
+                        .matchMplsLabel(MplsLabel.mplsLabel(LABEL))
+                        .matchEthType(Ethernet.MPLS_UNICAST)
+                        .build()
+        ));
+        assertThat(ruleS3.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setMpls(((MplsCriterion) mpls200Selector.getCriterion(MPLS_LABEL)).label())
+                        .setOutput(d3p10.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+
+    }
+
+}
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 e3b662a..87103aa 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
@@ -16,20 +16,16 @@
 package org.onosproject.net.intent.impl.compiler;
 
 import com.google.common.collect.ImmutableSet;
+import org.hamcrest.core.Is;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
-import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
-import org.onosproject.TestApplicationId;
 import org.onosproject.cfg.ComponentConfigAdapter;
-import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
-import org.onosproject.core.IdGenerator;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DefaultLink;
-import org.onosproject.net.DeviceId;
 import org.onosproject.net.FilteredConnectPoint;
 import org.onosproject.net.Link;
 import org.onosproject.net.PortNumber;
@@ -38,11 +34,13 @@
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.MplsCriterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+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.intent.MockIdGenerator;
 
 import java.util.Collection;
 import java.util.Collections;
@@ -58,75 +56,15 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.Matchers.is;
+import static org.onlab.packet.EthType.EtherType.IPV4;
 import static org.onosproject.net.Link.Type.DIRECT;
 import static org.onosproject.net.NetTestTools.*;
+import static org.onosproject.net.flow.criteria.Criterion.Type.IN_PORT;
+import static org.onosproject.net.flow.criteria.Criterion.Type.MPLS_LABEL;
+import static org.onosproject.net.flow.criteria.Criterion.Type.VLAN_VID;
+import static org.onosproject.net.flow.instructions.L2ModificationInstruction.*;
 
-public class LinkCollectionIntentCompilerTest {
-
-    private final ApplicationId appId = new TestApplicationId("test");
-
-    private final ConnectPoint d1p1 = connectPoint("s1", 1);
-    private final ConnectPoint d2p0 = connectPoint("s2", 0);
-    private final ConnectPoint d2p1 = connectPoint("s2", 1);
-    private final ConnectPoint d3p1 = connectPoint("s3", 1);
-    private final ConnectPoint d3p0 = connectPoint("s3", 10);
-    private final ConnectPoint d1p0 = connectPoint("s1", 10);
-
-    private final DeviceId of1Id = DeviceId.deviceId("of:of1");
-    private final DeviceId of2Id = DeviceId.deviceId("of:of2");
-    private final DeviceId of3Id = DeviceId.deviceId("of:of3");
-    private final DeviceId of4Id = DeviceId.deviceId("of:of4");
-
-    private final ConnectPoint of1p1 = connectPoint("of1", 1);
-    private final ConnectPoint of1p2 = connectPoint("of1", 2);
-    private final ConnectPoint of2p1 = connectPoint("of2", 1);
-    private final ConnectPoint of2p2 = connectPoint("of2", 2);
-    private final ConnectPoint of2p3 = connectPoint("of2", 3);
-    private final ConnectPoint of3p1 = connectPoint("of3", 1);
-    private final ConnectPoint of3p2 = connectPoint("of3", 2);
-    private final ConnectPoint of4p1 = connectPoint("of4", 1);
-    private final ConnectPoint of4p2 = connectPoint("of4", 2);
-
-    private final Set<Link> links = ImmutableSet.of(
-            DefaultLink.builder().providerId(PID).src(d1p1).dst(d2p0).type(DIRECT).build(),
-            DefaultLink.builder().providerId(PID).src(d2p1).dst(d3p1).type(DIRECT).build(),
-            DefaultLink.builder().providerId(PID).src(d1p1).dst(d3p1).type(DIRECT).build());
-
-    private final TrafficSelector selector = DefaultTrafficSelector.builder().build();
-    private final TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
-
-    private final TrafficSelector vlan100Selector = DefaultTrafficSelector.builder()
-            .matchVlanId(VlanId.vlanId("100"))
-            .build();
-
-    private final TrafficSelector vlan200Selector = DefaultTrafficSelector.builder()
-            .matchVlanId(VlanId.vlanId("200"))
-            .build();
-
-    private final TrafficSelector ipPrefixSelector = DefaultTrafficSelector.builder()
-            .matchIPDst(IpPrefix.valueOf("192.168.100.0/24"))
-            .build();
-
-    private final TrafficTreatment ethDstTreatment = DefaultTrafficTreatment.builder()
-            .setEthDst(MacAddress.valueOf("C0:FF:EE:C0:FF:EE"))
-            .build();
-
-    private CoreService coreService;
-    private IntentExtensionService intentExtensionService;
-    private IntentConfigurableRegistrator registrator;
-    private IdGenerator idGenerator = new MockIdGenerator();
-
-    private LinkCollectionIntent intent;
-
-    private LinkCollectionIntentCompiler sut;
-
-
-
-    private List<FlowRule> getFlowRulesByDevice(DeviceId deviceId, Collection<FlowRule> flowRules) {
-        return flowRules.stream()
-                .filter(fr -> fr.deviceId().equals(deviceId))
-                .collect(Collectors.toList());
-    }
+public class LinkCollectionIntentCompilerTest extends AbstractLinkCollectionTest {
 
     @Before
     public void setUp() {
@@ -181,7 +119,7 @@
 
         // if not found, get() raises an exception
         FlowRule rule1 = rules.stream()
-                .filter(rule -> rule.deviceId().equals(d1p0.deviceId()))
+                .filter(rule -> rule.deviceId().equals(d1p10.deviceId()))
                 .findFirst()
                 .get();
         assertThat(rule1.selector(), is(
@@ -226,7 +164,7 @@
      *             `-1 of4 2-
      */
     @Test
-    public void testFilteredConnectPoint1() {
+    public void testFilteredConnectPointForSp() {
         sut.activate();
         Set<Link> testLinks = ImmutableSet.of(
                 DefaultLink.builder().providerId(PID).src(of1p2).dst(of2p1).type(DIRECT).build(),
@@ -346,7 +284,7 @@
      * -1 of3 2---/
      */
     @Test
-    public void testFilteredConnectPoint2() {
+    public void testFilteredConnectPointForMp() {
         sut.activate();
         Set<Link> testlinks = ImmutableSet.of(
                 DefaultLink.builder().providerId(PID).src(of1p2).dst(of2p1).type(DIRECT).build(),
@@ -467,7 +405,7 @@
      *             `-1 of4 2-
      */
     @Test
-    public void nonTrivialTranslation1() {
+    public void nonTrivialTranslationForSp() {
         sut.activate();
         Set<Link> testLinks = ImmutableSet.of(
                 DefaultLink.builder().providerId(PID).src(of1p2).dst(of2p1).type(DIRECT).build(),
@@ -587,7 +525,7 @@
      * -1 of3 2---/
      */
     @Test
-    public void nonTrivialTranslation2() {
+    public void nonTrivialTranslationForMp() {
         sut.activate();
         Set<Link> testlinks = ImmutableSet.of(
                 DefaultLink.builder().providerId(PID).src(of1p2).dst(of2p1).type(DIRECT).build(),
@@ -701,4 +639,442 @@
 
         sut.deactivate();
     }
+
+    /**
+     * We test the proper compilation of mp2sp with
+     * trivial selector, trivial treatment and 1 hop.
+     */
+    @Test
+    public void singleHopTestForMp() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(treatment)
+                .links(ImmutableSet.of())
+                .filteredIngressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d1p10),
+                        new FilteredConnectPoint(d1p11)
+                ))
+                .filteredEgressPoints(ImmutableSet.of(new FilteredConnectPoint(d1p0)))
+                .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(2));
+
+        Collection<FlowRule> rulesS1 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d1p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS1, hasSize(2));
+        FlowRule ruleS1 = rulesS1.stream()
+                .filter(rule -> {
+                    PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                    return inPort.port().equals(d1p10.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS1.selector(), Is.is(
+                DefaultTrafficSelector
+                        .builder()
+                        .matchInPort(d1p10.port())
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), Is.is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setOutput(d1p0.port())
+                        .build()
+        ));
+
+        ruleS1 = rulesS1.stream()
+                .filter(rule -> {
+                    PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                    return inPort.port().equals(d1p11.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS1.selector(), Is.is(
+                DefaultTrafficSelector
+                        .builder()
+                        .matchInPort(d1p11.port())
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), Is.is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setOutput(d1p0.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+
+    }
+
+    /**
+     * We test the proper compilation of sp2mp with
+     * trivial selector, trivial treatment and 1 hop.
+     */
+    @Test
+    public void singleHopTestForSp() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(treatment)
+                .applyTreatmentOnEgress(true)
+                .links(ImmutableSet.of())
+                .filteredEgressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d1p10),
+                        new FilteredConnectPoint(d1p11)
+                ))
+                .filteredIngressPoints(ImmutableSet.of(new FilteredConnectPoint(d1p0)))
+                .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(1));
+
+        Collection<FlowRule> rulesS1 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d1p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS1, hasSize(1));
+        FlowRule ruleS1 = rulesS1.stream()
+                .filter(rule -> {
+                    PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                    return inPort.port().equals(d1p0.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS1.selector(), Is.is(
+                DefaultTrafficSelector
+                        .builder()
+                        .matchInPort(d1p0.port())
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), Is.is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setOutput(d1p10.port())
+                        .setOutput(d1p11.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+
+    }
+
+    /**
+     * We test the proper compilation of mp2sp with
+     * trivial selector, trivial treatment, filtered
+     * points and 1 hop.
+     */
+    @Test
+    public void singleHopTestFilteredForMp() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(treatment)
+                .links(ImmutableSet.of())
+                .filteredIngressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d1p10, vlan100Selector),
+                        new FilteredConnectPoint(d1p11, mpls69Selector)
+                ))
+                .filteredEgressPoints(ImmutableSet.of(new FilteredConnectPoint(d1p0, vlan200Selector)))
+                .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(2));
+
+        Collection<FlowRule> rulesS1 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d1p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS1, hasSize(2));
+        FlowRule ruleS1 = rulesS1.stream()
+                .filter(rule -> {
+                    PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                    return inPort.port().equals(d1p10.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS1.selector(), Is.is(
+                DefaultTrafficSelector
+                        .builder(vlan100Selector)
+                        .matchInPort(d1p10.port())
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), Is.is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setVlanId(((VlanIdCriterion) vlan200Selector.getCriterion(VLAN_VID)).vlanId())
+                        .setOutput(d1p0.port())
+                        .build()
+        ));
+
+        ruleS1 = rulesS1.stream()
+                .filter(rule -> {
+                    PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                    return inPort.port().equals(d1p11.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS1.selector(), Is.is(
+                DefaultTrafficSelector
+                        .builder(mpls69Selector)
+                        .matchInPort(d1p11.port())
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), Is.is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .popMpls(IPV4.ethType())
+                        .pushVlan()
+                        .setVlanId(((VlanIdCriterion) vlan200Selector.getCriterion(VLAN_VID)).vlanId())
+                        .setOutput(d1p0.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+
+    }
+
+    /**
+     * We test the proper compilation of sp2mp with
+     * trivial selector, trivial treatment and 1 hop.
+     */
+    @Test
+    public void singleHopTestFilteredForSp() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(treatment)
+                .applyTreatmentOnEgress(true)
+                .links(ImmutableSet.of())
+                .filteredEgressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d1p10, vlan100Selector),
+                        new FilteredConnectPoint(d1p11, mpls80Selector)
+                ))
+                .filteredIngressPoints(ImmutableSet.of(new FilteredConnectPoint(d1p0, vlan200Selector)))
+                .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(1));
+
+        Collection<FlowRule> rulesS1 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d1p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS1, hasSize(1));
+        FlowRule ruleS1 = rulesS1.stream()
+                .filter(rule -> {
+                    PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                    return inPort.port().equals(d1p0.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS1.selector(), Is.is(
+                DefaultTrafficSelector
+                        .builder(vlan200Selector)
+                        .matchInPort(d1p0.port())
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), Is.is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setVlanId(((VlanIdCriterion) vlan100Selector.getCriterion(VLAN_VID)).vlanId())
+                        .setOutput(d1p10.port())
+                        .popVlan()
+                        .pushMpls()
+                        .setMpls(((MplsCriterion) mpls80Selector.getCriterion(MPLS_LABEL)).label())
+                        .setOutput(d1p11.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+
+    }
+
+    /**
+     * We test the proper compilation of mp2sp with
+     * selector, treatment, filtered
+     * points and 1 hop.
+     */
+    @Test
+    public void singleHopNonTrivialForMp() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(ipPrefixSelector)
+                .treatment(ethDstTreatment)
+                .links(ImmutableSet.of())
+                .filteredIngressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d1p10, vlan100Selector),
+                        new FilteredConnectPoint(d1p11, mpls100Selector)
+                ))
+                .filteredEgressPoints(ImmutableSet.of(new FilteredConnectPoint(d1p0, vlan200Selector)))
+                .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(2));
+
+        Collection<FlowRule> rulesS1 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d1p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS1, hasSize(2));
+        FlowRule ruleS1 = rulesS1.stream()
+                .filter(rule -> {
+                    PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                    return inPort.port().equals(d1p10.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS1.selector(), Is.is(
+                DefaultTrafficSelector
+                        .builder(ipPrefixSelector)
+                        .matchVlanId(((VlanIdCriterion) vlan100Selector.getCriterion(VLAN_VID)).vlanId())
+                        .matchInPort(d1p10.port())
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), Is.is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setEthDst(((ModEtherInstruction) ethDstTreatment
+                                .allInstructions()
+                                .stream()
+                                .filter(instruction -> instruction instanceof ModEtherInstruction)
+                                .findFirst().get()).mac())
+                        .setVlanId(((VlanIdCriterion) vlan200Selector.getCriterion(VLAN_VID)).vlanId())
+                        .setOutput(d1p0.port())
+                        .build()
+        ));
+
+        ruleS1 = rulesS1.stream()
+                .filter(rule -> {
+                    PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                    return inPort.port().equals(d1p11.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS1.selector(), Is.is(
+                DefaultTrafficSelector
+                        .builder(ipPrefixSelector)
+                        .matchMplsLabel(((MplsCriterion) mpls100Selector.getCriterion(MPLS_LABEL)).label())
+                        .matchInPort(d1p11.port())
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), Is.is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setEthDst(((ModEtherInstruction) ethDstTreatment
+                                .allInstructions()
+                                .stream()
+                                .filter(instruction -> instruction instanceof ModEtherInstruction)
+                                .findFirst().get()).mac())
+                        .popMpls(IPV4.ethType())
+                        .pushVlan()
+                        .setVlanId(((VlanIdCriterion) vlan200Selector.getCriterion(VLAN_VID)).vlanId())
+                        .setOutput(d1p0.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+
+    }
+
+    /**
+     * We test the proper compilation of sp2mp with
+     * selector, treatment and 1 hop.
+     */
+    @Test
+    public void singleHopNonTrivialForSp() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(ipPrefixSelector)
+                .treatment(ethDstTreatment)
+                .applyTreatmentOnEgress(true)
+                .links(ImmutableSet.of())
+                .filteredEgressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d1p10, vlan100Selector),
+                        new FilteredConnectPoint(d1p11, mpls200Selector)
+                ))
+                .filteredIngressPoints(ImmutableSet.of(new FilteredConnectPoint(d1p0, vlan200Selector)))
+                .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(1));
+
+        Collection<FlowRule> rulesS1 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d1p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(rulesS1, hasSize(1));
+        FlowRule ruleS1 = rulesS1.stream()
+                .filter(rule -> {
+                    PortCriterion inPort = (PortCriterion) rule.selector().getCriterion(IN_PORT);
+                    return inPort.port().equals(d1p0.port());
+                })
+                .findFirst()
+                .get();
+        assertThat(ruleS1.selector(), Is.is(
+                DefaultTrafficSelector
+                        .builder(ipPrefixSelector)
+                        .matchVlanId(((VlanIdCriterion) vlan200Selector.getCriterion(VLAN_VID)).vlanId())
+                        .matchInPort(d1p0.port())
+                        .build()
+        ));
+        assertThat(ruleS1.treatment(), Is.is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setEthDst(((ModEtherInstruction) ethDstTreatment
+                                .allInstructions()
+                                .stream()
+                                .filter(instruction -> instruction instanceof ModEtherInstruction)
+                                .findFirst().get()).mac())
+                        .setVlanId(((VlanIdCriterion) vlan100Selector.getCriterion(VLAN_VID)).vlanId())
+                        .setOutput(d1p10.port())
+                        .setEthDst(((ModEtherInstruction) ethDstTreatment
+                                .allInstructions()
+                                .stream()
+                                .filter(instruction -> instruction instanceof ModEtherInstruction)
+                                .findFirst().get()).mac())
+                        .popVlan()
+                        .pushMpls()
+                        .setMpls(((MplsCriterion) mpls200Selector.getCriterion(MPLS_LABEL)).label())
+                        .setOutput(d1p11.port())
+                        .build()
+        ));
+
+        sut.deactivate();
+
+    }
+
 }