Add path intent compiler that generates flow objective intents

Change-Id: I11bee398d927f0e3f32b7cf81d98cfe5816db477
diff --git a/core/api/src/main/java/org/onosproject/net/intent/FlowObjectiveIntent.java b/core/api/src/main/java/org/onosproject/net/intent/FlowObjectiveIntent.java
index 18dcb6e..55c883a 100644
--- a/core/api/src/main/java/org/onosproject/net/intent/FlowObjectiveIntent.java
+++ b/core/api/src/main/java/org/onosproject/net/intent/FlowObjectiveIntent.java
@@ -17,19 +17,25 @@
 package org.onosproject.net.intent;
 
 import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
 import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.NetworkResource;
 import org.onosproject.net.flowobjective.Objective;
 
 import java.util.Collection;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkArgument;
 
 /**
  * Intent expressed as (and backed by) a collection of flow objectives through
  * which the intent is to be accomplished.
  */
-public class FlowObjectiveIntent extends Intent {
+public final class FlowObjectiveIntent extends Intent {
 
-    private final Collection<Objective> objectives;
+    private final List<Objective> objectives;
+    private final List<DeviceId> devices;
 
     /**
      * Constructor for serialization.
@@ -37,6 +43,7 @@
     protected FlowObjectiveIntent() {
         super();
         this.objectives = null;
+        this.devices = null;
     }
 
     /**
@@ -44,13 +51,15 @@
      * resources.
      *
      * @param appId      application id
+     * @param devices    list of target devices; in same order as the objectives
      * @param objectives backing flow objectives
      * @param resources  backing network resources
      */
     public FlowObjectiveIntent(ApplicationId appId,
-                               Collection<Objective> objectives,
+                               List<DeviceId> devices,
+                               List<Objective> objectives,
                                Collection<NetworkResource> resources) {
-        this(appId, null, objectives, resources);
+        this(appId, null, devices, objectives, resources);
     }
 
     /**
@@ -59,14 +68,20 @@
      *
      * @param appId      application id
      * @param key        intent key
+     * @param devices    list of target devices; in same order as the objectives
      * @param objectives backing flow objectives
      * @param resources  backing network resources
      */
-    public FlowObjectiveIntent(ApplicationId appId, Key key,
-                               Collection<Objective> objectives,
+    public FlowObjectiveIntent(ApplicationId appId,
+                               Key key,
+                               List<DeviceId> devices,
+                               List<Objective> objectives,
                                Collection<NetworkResource> resources) {
         super(appId, key, resources, DEFAULT_INTENT_PRIORITY);
-        this.objectives = objectives;
+        checkArgument(devices.size() == objectives.size(),
+                      "Number of devices and objectives does not match");
+        this.objectives = ImmutableList.copyOf(objectives);
+        this.devices = ImmutableList.copyOf(devices);
     }
 
     /**
@@ -74,10 +89,19 @@
      *
      * @return flow objectives
      */
-    Collection<Objective> objectives() {
+    public List<Objective> objectives() {
         return objectives;
     }
 
+    /**
+     * Returns the list of devices for the flow objectives.
+     *
+     * @return devices
+     */
+    public List<DeviceId> devices() {
+        return devices;
+    }
+
 
     @Override
     public boolean isInstallable() {
@@ -91,7 +115,8 @@
                 .add("key", key())
                 .add("appId", appId())
                 .add("resources", resources())
-                .add("objectives", objectives)
+                .add("device", devices())
+                .add("objectives", objectives())
                 .toString();
     }
 }
diff --git a/core/api/src/test/java/org/onosproject/net/intent/FlowObjectiveIntentTest.java b/core/api/src/test/java/org/onosproject/net/intent/FlowObjectiveIntentTest.java
index ec3e334..54f9aa7 100644
--- a/core/api/src/test/java/org/onosproject/net/intent/FlowObjectiveIntentTest.java
+++ b/core/api/src/test/java/org/onosproject/net/intent/FlowObjectiveIntentTest.java
@@ -16,11 +16,13 @@
 
 package org.onosproject.net.intent;
 
-import com.google.common.collect.ImmutableSet;
-import com.google.common.testing.EqualsTester;
+import java.util.Collection;
+import java.util.List;
+
 import org.junit.Test;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.NetworkResource;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
@@ -30,7 +32,9 @@
 import org.onosproject.net.flowobjective.ForwardingObjective;
 import org.onosproject.net.flowobjective.Objective;
 
-import java.util.Collection;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.testing.EqualsTester;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -52,8 +56,9 @@
             .withSelector(DefaultTrafficSelector.builder().matchEthType((short) 123).build())
             .withTreatment(DefaultTrafficTreatment.emptyTreatment())
             .withFlag(ForwardingObjective.Flag.VERSATILE).add();
-    private static final Collection<Objective> OBJECTIVES = ImmutableSet.of(FO1, FO2);
+    private static final List<Objective> OBJECTIVES = ImmutableList.of(FO1, FO2);
     private static final Collection<NetworkResource> RESOURCES = ImmutableSet.of();
+    private static final List<DeviceId> DEVICE = ImmutableList.of(DeviceId.NONE, DeviceId.NONE);
 
     /**
      * Tests basics of construction and getters.
@@ -61,7 +66,7 @@
     @Test
     public void basics() {
         FlowObjectiveIntent intent =
-                new FlowObjectiveIntent(APP_ID, KEY, OBJECTIVES, RESOURCES);
+                new FlowObjectiveIntent(APP_ID, KEY, DEVICE, OBJECTIVES, RESOURCES);
         assertEquals("incorrect app id", APP_ID, intent.appId());
         assertEquals("incorrect key", KEY, intent.key());
         assertEquals("incorrect objectives", OBJECTIVES, intent.objectives());
@@ -89,11 +94,11 @@
 
     @Override
     protected Intent createOne() {
-        return new FlowObjectiveIntent(APP_ID, OBJECTIVES, RESOURCES);
+        return new FlowObjectiveIntent(APP_ID, DEVICE, OBJECTIVES, RESOURCES);
     }
 
     @Override
     protected Intent createAnother() {
-        return new FlowObjectiveIntent(APP_ID, OBJECTIVES, RESOURCES);
+        return new FlowObjectiveIntent(APP_ID, DEVICE, OBJECTIVES, RESOURCES);
     }
-}
\ No newline at end of file
+}
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathCompiler.java
new file mode 100644
index 0000000..7514250
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathCompiler.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2016 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 java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.onlab.packet.VlanId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.LinkKey;
+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.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.intent.PathIntent;
+import org.onosproject.net.intent.constraint.EncapsulationConstraint;
+import org.onosproject.net.intent.impl.IntentCompilationException;
+import org.onosproject.net.newresource.Resource;
+import org.onosproject.net.newresource.ResourceService;
+import org.onosproject.net.newresource.Resources;
+import org.slf4j.Logger;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+
+import static org.onosproject.net.LinkKey.linkKey;
+
+/**
+ * Shared APIs and implementations for path compilers.
+ */
+
+public class PathCompiler<T> {
+
+    /**
+     * Defines methods used to create objects representing flows.
+     */
+    public interface PathCompilerCreateFlow<T> {
+
+        void createFlow(TrafficSelector originalSelector,
+                        TrafficTreatment originalTreatment,
+                        ConnectPoint ingress, ConnectPoint egress,
+                        int priority,
+                        boolean applyTreatment,
+                        List<T> flows,
+                        List<DeviceId> devices);
+
+        Logger log();
+
+        ResourceService resourceService();
+    }
+
+    private boolean isLast(List<Link> links, int i) {
+        return i == links.size() - 2;
+    }
+
+    private Map<LinkKey, VlanId> assignVlanId(PathCompilerCreateFlow creator, PathIntent intent) {
+        Set<LinkKey> linkRequest =
+                Sets.newHashSetWithExpectedSize(intent.path()
+                        .links().size() - 2);
+        for (int i = 1; i <= intent.path().links().size() - 2; i++) {
+            LinkKey link = linkKey(intent.path().links().get(i));
+            linkRequest.add(link);
+            // add the inverse link. I want that the VLANID is reserved both for
+            // the direct and inverse link
+            linkRequest.add(linkKey(link.dst(), link.src()));
+        }
+
+        Map<LinkKey, VlanId> vlanIds = findVlanIds(creator, linkRequest);
+        if (vlanIds.isEmpty()) {
+            creator.log().warn("No VLAN IDs available");
+            return Collections.emptyMap();
+        }
+
+        //same VLANID is used for both directions
+        Set<Resource> resources = vlanIds.entrySet().stream()
+                .flatMap(x -> Stream.of(
+                        Resources.discrete(x.getKey().src().deviceId(), x.getKey().src().port(), x.getValue())
+                                .resource(),
+                        Resources.discrete(x.getKey().dst().deviceId(), x.getKey().dst().port(), x.getValue())
+                                .resource()
+                ))
+                .collect(Collectors.toSet());
+        List<org.onosproject.net.newresource.ResourceAllocation> allocations =
+                creator.resourceService().allocate(intent.id(), ImmutableList.copyOf(resources));
+        if (allocations.isEmpty()) {
+            Collections.emptyMap();
+        }
+
+        return vlanIds;
+    }
+
+    private Map<LinkKey, VlanId> findVlanIds(PathCompilerCreateFlow creator, Set<LinkKey> links) {
+        Map<LinkKey, VlanId> vlanIds = new HashMap<>();
+        for (LinkKey link : links) {
+            Set<VlanId> forward = findVlanId(creator, link.src());
+            Set<VlanId> backward = findVlanId(creator, link.dst());
+            Set<VlanId> common = Sets.intersection(forward, backward);
+            if (common.isEmpty()) {
+                continue;
+            }
+            vlanIds.put(link, common.iterator().next());
+        }
+        return vlanIds;
+    }
+
+    private Set<VlanId> findVlanId(PathCompilerCreateFlow creator, ConnectPoint cp) {
+        return creator.resourceService().getAvailableResourceValues(
+                Resources.discrete(cp.deviceId(), cp.port()).id(),
+                VlanId.class);
+    }
+
+    private void manageVlanEncap(PathCompilerCreateFlow<T> creator, List<T> flows,
+                                 List<DeviceId> devices,
+                                 PathIntent intent) {
+        Map<LinkKey, VlanId> vlanIds = assignVlanId(creator, intent);
+
+        Iterator<Link> links = intent.path().links().iterator();
+        Link srcLink = links.next();
+
+        Link link = links.next();
+
+        // Ingress traffic
+        VlanId vlanId = vlanIds.get(linkKey(link));
+        if (vlanId == null) {
+            throw new IntentCompilationException("No available VLAN ID for " + link);
+        }
+        VlanId prevVlanId = vlanId;
+
+        Optional<VlanIdCriterion> vlanCriterion = intent.selector().criteria()
+                .stream().filter(criterion -> criterion.type() == Criterion.Type.VLAN_VID)
+                .map(criterion -> (VlanIdCriterion) criterion)
+                .findAny();
+
+        //Push VLAN if selector does not include VLAN
+        TrafficTreatment.Builder treatBuilder = DefaultTrafficTreatment.builder();
+        if (!vlanCriterion.isPresent()) {
+            treatBuilder.pushVlan();
+        }
+        //Tag the traffic with the new encapsulation VLAN
+        treatBuilder.setVlanId(vlanId);
+        creator.createFlow(intent.selector(), treatBuilder.build(),
+                           srcLink.dst(), link.src(), intent.priority(), true,
+                           flows, devices);
+
+        ConnectPoint prev = link.dst();
+
+        while (links.hasNext()) {
+
+            link = links.next();
+
+            if (links.hasNext()) {
+                // Transit traffic
+                VlanId egressVlanId = vlanIds.get(linkKey(link));
+                if (egressVlanId == null) {
+                    throw new IntentCompilationException("No available VLAN ID for " + link);
+                }
+                prevVlanId = egressVlanId;
+
+                TrafficSelector transitSelector = DefaultTrafficSelector.builder()
+                        .matchInPort(prev.port())
+                        .matchVlanId(prevVlanId).build();
+
+                TrafficTreatment.Builder transitTreat = DefaultTrafficTreatment.builder();
+
+                // Set the new vlanId only if the previous one is different
+                if (!prevVlanId.equals(egressVlanId)) {
+                    transitTreat.setVlanId(egressVlanId);
+                }
+                creator.createFlow(transitSelector,
+                                   transitTreat.build(), prev, link.src(),
+                                   intent.priority(), true, flows, devices);
+                prev = link.dst();
+            } else {
+                // Egress traffic
+                TrafficSelector egressSelector = DefaultTrafficSelector.builder()
+                        .matchInPort(prev.port())
+                        .matchVlanId(prevVlanId).build();
+                TrafficTreatment.Builder egressTreat = DefaultTrafficTreatment.builder(intent.treatment());
+
+                Optional<L2ModificationInstruction.ModVlanIdInstruction> modVlanIdInstruction = intent.treatment()
+                        .allInstructions().stream().filter(
+                                instruction -> instruction instanceof L2ModificationInstruction.ModVlanIdInstruction)
+                        .map(x -> (L2ModificationInstruction.ModVlanIdInstruction) x).findAny();
+
+                Optional<L2ModificationInstruction.PopVlanInstruction> popVlanInstruction = intent.treatment()
+                        .allInstructions().stream().filter(
+                                instruction -> instruction instanceof L2ModificationInstruction.PopVlanInstruction)
+                        .map(x -> (L2ModificationInstruction.PopVlanInstruction) x).findAny();
+
+                if (!modVlanIdInstruction.isPresent() && !popVlanInstruction.isPresent()) {
+                    if (vlanCriterion.isPresent()) {
+                        egressTreat.setVlanId(vlanCriterion.get().vlanId());
+                    } else {
+                        egressTreat.popVlan();
+                    }
+                }
+
+                creator.createFlow(egressSelector,
+                                   egressTreat.build(), prev, link.src(),
+                                   intent.priority(), true, flows, devices);
+            }
+        }
+    }
+
+    /**
+     * Compiles an intent down to flows.
+     *
+     * @param creator how to create the flows
+     * @param intent intent to process
+     * @param flows list of generated flows
+     * @param devices list of devices that correspond to the flows
+     */
+    public void compile(PathCompilerCreateFlow<T> creator,
+                        PathIntent intent,
+                        List<T> flows,
+                        List<DeviceId> devices) {
+        // Note: right now recompile is not considered
+        // TODO: implement recompile behavior
+
+        List<Link> links = intent.path().links();
+
+        Optional<EncapsulationConstraint> encapConstraint = intent.constraints().stream()
+                .filter(constraint -> constraint instanceof EncapsulationConstraint)
+                .map(x -> (EncapsulationConstraint) x).findAny();
+        //if no encapsulation or is involved only a single switch use the default behaviour
+        if (!encapConstraint.isPresent() || links.size() == 1) {
+            for (int i = 0; i < links.size() - 1; i++) {
+                ConnectPoint ingress = links.get(i).dst();
+                ConnectPoint egress = links.get(i + 1).src();
+                creator.createFlow(intent.selector(), intent.treatment(),
+                                   ingress, egress, intent.priority(),
+                                   isLast(links, i), flows, devices);
+            }
+        }
+
+        encapConstraint.map(EncapsulationConstraint::encapType)
+                .map(type -> {
+                    switch (type) {
+                        case VLAN:
+                            manageVlanEncap(creator, flows, devices, intent);
+                            // TODO: implement MPLS case here
+                        default:
+                            // Nothing to do
+                    }
+                    return 0;
+                });
+    }
+
+}
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentCompiler.java
index cad9cb7..045b0ef 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentCompiler.java
@@ -15,57 +15,43 @@
  */
 package org.onosproject.net.intent.impl.compiler;
 
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Sets;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
 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.packet.VlanId;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.Link;
-import org.onosproject.net.LinkKey;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.flow.DefaultFlowRule;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flow.criteria.Criterion;
-import org.onosproject.net.flow.criteria.VlanIdCriterion;
-import org.onosproject.net.flow.instructions.L2ModificationInstruction;
 import org.onosproject.net.intent.FlowRuleIntent;
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentCompiler;
 import org.onosproject.net.intent.IntentExtensionService;
 import org.onosproject.net.intent.PathIntent;
-import org.onosproject.net.intent.constraint.EncapsulationConstraint;
-import org.onosproject.net.intent.impl.IntentCompilationException;
-import org.onosproject.net.newresource.Resource;
 import org.onosproject.net.newresource.ResourceService;
-import org.onosproject.net.newresource.Resources;
 import org.onosproject.net.resource.link.LinkResourceAllocations;
 import org.slf4j.Logger;
 
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
+import com.google.common.collect.ImmutableList;
 
-import static org.onosproject.net.LinkKey.linkKey;
 import static org.slf4j.LoggerFactory.getLogger;
 
 @Component(immediate = true)
-public class PathIntentCompiler implements IntentCompiler<PathIntent> {
+public class PathIntentCompiler
+        extends PathCompiler<FlowRule>
+        implements IntentCompiler<PathIntent>,
+                   PathCompiler.PathCompilerCreateFlow<FlowRule> {
 
     private final Logger log = getLogger(getClass());
 
@@ -94,47 +80,30 @@
     @Override
     public List<Intent> compile(PathIntent intent, List<Intent> installable,
                                 Set<LinkResourceAllocations> resources) {
-        // Note: right now recompile is not considered
-        // TODO: implement recompile behavior
 
-        List<Link> links = intent.path().links();
-
-        Optional<EncapsulationConstraint> encapConstraint = intent.constraints().stream()
-                .filter(constraint -> constraint instanceof EncapsulationConstraint)
-                .map(x -> (EncapsulationConstraint) x).findAny();
-        //if no encapsulation or is involved only a single switch use the default behaviour
-        if (!encapConstraint.isPresent() || links.size() == 1) {
-            List<FlowRule> rules = new LinkedList<>();
-            for (int i = 0; i < links.size() - 1; i++) {
-                ConnectPoint ingress = links.get(i).dst();
-                ConnectPoint egress = links.get(i + 1).src();
-                FlowRule rule = createFlowRule(intent.selector(), intent.treatment(),
-                                               ingress, egress, intent.priority(),
-                                               isLast(links, i));
-                rules.add(rule);
-            }
-
-            return ImmutableList.of(new FlowRuleIntent(appId, null, rules, intent.resources()));
-        }
-
-        List<FlowRule> rules = encapConstraint.map(EncapsulationConstraint::encapType)
-                .map(type -> {
-                    switch (type) {
-                        case VLAN:
-                            return manageVlanEncap(intent);
-                        // TODO: implement MPLS case here
-                        default:
-                            return Collections.<FlowRule>emptyList();
-                    }
-                })
-                .orElse(Collections.emptyList());
+        List<FlowRule> rules = new LinkedList<>();
+        List<DeviceId> devices = new LinkedList<>();
+        compile(this, intent, rules, devices);
 
         return ImmutableList.of(new FlowRuleIntent(appId, null, rules, intent.resources()));
     }
 
-    private FlowRule createFlowRule(TrafficSelector originalSelector, TrafficTreatment originalTreatment,
-                                    ConnectPoint ingress, ConnectPoint egress,
-                                    int priority, boolean applyTreatment) {
+    @Override
+    public Logger log() {
+        return log;
+    }
+
+    @Override
+    public ResourceService resourceService() {
+        return resourceService;
+    }
+
+    @Override
+    public void createFlow(TrafficSelector originalSelector, TrafficTreatment originalTreatment,
+                           ConnectPoint ingress, ConnectPoint egress,
+                           int priority, boolean applyTreatment,
+                           List<FlowRule> rules,
+                           List<DeviceId> devices) {
         TrafficSelector selector = DefaultTrafficSelector.builder(originalSelector)
                 .matchInPort(ingress.port())
                 .build();
@@ -147,164 +116,13 @@
         }
         TrafficTreatment treatment = treatmentBuilder.setOutput(egress.port()).build();
 
-        return DefaultFlowRule.builder()
+        rules.add(DefaultFlowRule.builder()
                 .forDevice(ingress.deviceId())
                 .withSelector(selector)
                 .withTreatment(treatment)
                 .withPriority(priority)
                 .fromApp(appId)
                 .makePermanent()
-                .build();
-    }
-
-    private List<FlowRule> manageVlanEncap(PathIntent intent) {
-        Map<LinkKey, VlanId> vlanIds = assignVlanId(intent);
-
-        Iterator<Link> links = intent.path().links().iterator();
-        Link srcLink = links.next();
-
-        Link link = links.next();
-        // List of flow rules to be installed
-        List<FlowRule> rules = new LinkedList<>();
-
-        // Ingress traffic
-        VlanId vlanId = vlanIds.get(linkKey(link));
-        if (vlanId == null) {
-            throw new IntentCompilationException("No available VLAN ID for " + link);
-        }
-        VlanId prevVlanId = vlanId;
-
-        Optional<VlanIdCriterion> vlanCriterion = intent.selector().criteria()
-                .stream().filter(criterion -> criterion.type() == Criterion.Type.VLAN_VID)
-                .map(criterion -> (VlanIdCriterion) criterion)
-                .findAny();
-
-        //Push VLAN if selector does not include VLAN
-        TrafficTreatment.Builder treatBuilder = DefaultTrafficTreatment.builder();
-        if (!vlanCriterion.isPresent()) {
-            treatBuilder.pushVlan();
-        }
-        //Tag the traffic with the new encapsulation VLAN
-        treatBuilder.setVlanId(vlanId);
-        rules.add(createFlowRule(intent.selector(), treatBuilder.build(),
-                                 srcLink.dst(), link.src(), intent.priority(), true));
-
-        ConnectPoint prev = link.dst();
-
-        while (links.hasNext()) {
-
-            link = links.next();
-
-            if (links.hasNext()) {
-                // Transit traffic
-                VlanId egressVlanId = vlanIds.get(linkKey(link));
-                if (egressVlanId == null) {
-                    throw new IntentCompilationException("No available VLAN ID for " + link);
-                }
-                prevVlanId = egressVlanId;
-
-                TrafficSelector transitSelector = DefaultTrafficSelector.builder()
-                        .matchInPort(prev.port())
-                        .matchVlanId(prevVlanId).build();
-
-                TrafficTreatment.Builder transitTreat = DefaultTrafficTreatment.builder();
-
-                // Set the new vlanId only if the previous one is different
-                if (!prevVlanId.equals(egressVlanId)) {
-                    transitTreat.setVlanId(egressVlanId);
-                }
-                rules.add(createFlowRule(transitSelector,
-                                         transitTreat.build(), prev, link.src(), intent.priority(), true));
-                prev = link.dst();
-            } else {
-                // Egress traffic
-                TrafficSelector egressSelector = DefaultTrafficSelector.builder()
-                        .matchInPort(prev.port())
-                        .matchVlanId(prevVlanId).build();
-                TrafficTreatment.Builder egressTreat = DefaultTrafficTreatment.builder(intent.treatment());
-
-                Optional<L2ModificationInstruction.ModVlanIdInstruction> modVlanIdInstruction = intent.treatment()
-                        .allInstructions().stream().filter(
-                                instruction -> instruction instanceof L2ModificationInstruction.ModVlanIdInstruction)
-                        .map(x -> (L2ModificationInstruction.ModVlanIdInstruction) x).findAny();
-
-                Optional<L2ModificationInstruction.PopVlanInstruction> popVlanInstruction = intent.treatment()
-                        .allInstructions().stream().filter(
-                                instruction -> instruction instanceof L2ModificationInstruction.PopVlanInstruction)
-                        .map(x -> (L2ModificationInstruction.PopVlanInstruction) x).findAny();
-
-                if (!modVlanIdInstruction.isPresent() && !popVlanInstruction.isPresent()) {
-                    if (vlanCriterion.isPresent()) {
-                        egressTreat.setVlanId(vlanCriterion.get().vlanId());
-                    } else {
-                        egressTreat.popVlan();
-                    }
-                }
-
-                rules.add(createFlowRule(egressSelector,
-                                         egressTreat.build(), prev, link.src(), intent.priority(), true));
-            }
-        }
-        return rules;
-
-    }
-
-    private Map<LinkKey, VlanId> assignVlanId(PathIntent intent) {
-        Set<LinkKey> linkRequest = Sets.newHashSetWithExpectedSize(intent.path()
-                                                                           .links().size() - 2);
-        for (int i = 1; i <= intent.path().links().size() - 2; i++) {
-            LinkKey link = linkKey(intent.path().links().get(i));
-            linkRequest.add(link);
-            // add the inverse link. I want that the VLANID is reserved both for
-            // the direct and inverse link
-            linkRequest.add(linkKey(link.dst(), link.src()));
-        }
-
-        Map<LinkKey, VlanId> vlanIds = findVlanIds(linkRequest);
-        if (vlanIds.isEmpty()) {
-            log.warn("No VLAN IDs available");
-            return Collections.emptyMap();
-        }
-
-        //same VLANID is used for both directions
-        Set<Resource> resources = vlanIds.entrySet().stream()
-                .flatMap(x -> Stream.of(
-                        Resources.discrete(x.getKey().src().deviceId(), x.getKey().src().port(), x.getValue())
-                                .resource(),
-                        Resources.discrete(x.getKey().dst().deviceId(), x.getKey().dst().port(), x.getValue())
-                                .resource()
-                ))
-                .collect(Collectors.toSet());
-        List<org.onosproject.net.newresource.ResourceAllocation> allocations =
-                resourceService.allocate(intent.id(), ImmutableList.copyOf(resources));
-        if (allocations.isEmpty()) {
-            Collections.emptyMap();
-        }
-
-        return vlanIds;
-    }
-
-    private Map<LinkKey, VlanId> findVlanIds(Set<LinkKey> links) {
-        Map<LinkKey, VlanId> vlanIds = new HashMap<>();
-        for (LinkKey link : links) {
-            Set<VlanId> forward = findVlanId(link.src());
-            Set<VlanId> backward = findVlanId(link.dst());
-            Set<VlanId> common = Sets.intersection(forward, backward);
-            if (common.isEmpty()) {
-                continue;
-            }
-            vlanIds.put(link, common.iterator().next());
-        }
-        return vlanIds;
-    }
-
-    private Set<VlanId> findVlanId(ConnectPoint cp) {
-        return resourceService.getAvailableResourceValues(
-                Resources.discrete(cp.deviceId(), cp.port()).id(),
-                VlanId.class);
-    }
-
-    private boolean isLast(List<Link> links, int i) {
-        return i == links.size() - 2;
+                .build());
     }
 }
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentFlowObjectiveCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentFlowObjectiveCompiler.java
new file mode 100644
index 0000000..25cae18
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentFlowObjectiveCompiler.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2016 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 java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+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.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+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.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.intent.FlowObjectiveIntent;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentCompiler;
+import org.onosproject.net.intent.IntentExtensionService;
+import org.onosproject.net.intent.PathIntent;
+import org.onosproject.net.newresource.ResourceService;
+import org.onosproject.net.resource.link.LinkResourceAllocations;
+import org.slf4j.Logger;
+
+import com.google.common.collect.ImmutableList;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+@Component(immediate = true)
+public class PathIntentFlowObjectiveCompiler
+        extends PathCompiler<Objective>
+        implements IntentCompiler<PathIntent>,
+                   PathCompiler.PathCompilerCreateFlow<Objective> {
+
+    private final Logger log = getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected IntentExtensionService intentManager;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ResourceService resourceService;
+
+    private ApplicationId appId;
+
+    @Activate
+    public void activate() {
+        appId = coreService.registerApplication("org.onosproject.net.intent");
+        //intentManager.registerCompiler(PathIntent.class, this);
+    }
+
+    @Deactivate
+    public void deactivate() {
+        //intentManager.unregisterCompiler(PathIntent.class);
+    }
+
+    @Override
+    public List<Intent> compile(PathIntent intent, List<Intent> installable,
+                                Set<LinkResourceAllocations> resources) {
+
+        List<Objective> objectives = new LinkedList<>();
+        List<DeviceId> devices = new LinkedList<>();
+        compile(this, intent, objectives, devices);
+
+        return ImmutableList.of(new FlowObjectiveIntent(appId, devices, objectives, intent.resources()));
+    }
+
+    @Override
+    public Logger log() {
+        return log;
+    }
+
+    @Override
+    public ResourceService resourceService() {
+        return resourceService;
+    }
+
+    @Override
+    public void createFlow(TrafficSelector originalSelector, TrafficTreatment originalTreatment,
+                                          ConnectPoint ingress, ConnectPoint egress,
+                                          int priority, boolean applyTreatment,
+                                          List<Objective> objectives,
+                                          List<DeviceId> devices) {
+        TrafficSelector selector = DefaultTrafficSelector.builder(originalSelector)
+                .matchInPort(ingress.port())
+                .build();
+
+        TrafficTreatment.Builder treatmentBuilder;
+        if (applyTreatment) {
+            treatmentBuilder = DefaultTrafficTreatment.builder(originalTreatment);
+        } else {
+            treatmentBuilder = DefaultTrafficTreatment.builder();
+        }
+        TrafficTreatment treatment = treatmentBuilder.setOutput(egress.port()).build();
+
+        objectives.add(DefaultForwardingObjective.builder()
+                  .withSelector(selector)
+                  .withTreatment(treatment)
+                  .withPriority(priority)
+                  .fromApp(appId)
+                  .makePermanent()
+                  .withFlag(ForwardingObjective.Flag.SPECIFIC)
+                  .add());
+        devices.add(ingress.deviceId());
+    }
+}