Adding experimental Flow Objective composition

Change-Id: I35e4414845ad5034145157ab83f933e40f75a1d9
diff --git a/cli/src/main/java/org/onosproject/cli/net/FlowObjectiveCompositionCommand.java b/cli/src/main/java/org/onosproject/cli/net/FlowObjectiveCompositionCommand.java
new file mode 100644
index 0000000..9bacc7a
--- /dev/null
+++ b/cli/src/main/java/org/onosproject/cli/net/FlowObjectiveCompositionCommand.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 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.cli.net;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+
+/**
+ * Manages FlowObjectiveComposition policy.
+ */
+@Command(scope = "onos", name = "policy",
+        description = "Manages FlowObjectiveComposition policy")
+public class FlowObjectiveCompositionCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "command",
+            description = "Command name (install)",
+            required = true, multiValued = false)
+    String command = null;
+
+    @Argument(index = 1, name = "names", description = "policy string",
+            required = true, multiValued = true)
+    String[] policies = null;
+
+    @Override
+    protected void execute() {
+        FlowObjectiveService service = get(FlowObjectiveService.class);
+        service.initPolicy(policies[0]);
+        print("Policy %s installed", policies[0]);
+    }
+}
diff --git a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index ca470e3..367013c 100644
--- a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -30,6 +30,10 @@
         </command>
 
         <command>
+            <action class="org.onosproject.cli.net.FlowObjectiveCompositionCommand"/>
+        </command>
+
+        <command>
             <action class="org.onosproject.cli.app.ApplicationsListCommand"/>
         </command>
 
diff --git a/core/api/src/main/java/org/onosproject/net/flowobjective/FlowObjectiveService.java b/core/api/src/main/java/org/onosproject/net/flowobjective/FlowObjectiveService.java
index cb9a248..d325415 100644
--- a/core/api/src/main/java/org/onosproject/net/flowobjective/FlowObjectiveService.java
+++ b/core/api/src/main/java/org/onosproject/net/flowobjective/FlowObjectiveService.java
@@ -56,4 +56,10 @@
      */
     int allocateNextId();
 
+    /**
+     * Installs the filtering rules onto the specified device.
+     *
+     * @param policy            policy expression
+     */
+    void initPolicy(String policy);
 }
diff --git a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/FlowObjectiveManager.java b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/FlowObjectiveManager.java
index 4247f03..7d0713b 100644
--- a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/FlowObjectiveManager.java
+++ b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/FlowObjectiveManager.java
@@ -218,6 +218,9 @@
         return flowObjectiveStore.allocateNextId();
     }
 
+    @Override
+    public void initPolicy(String policy) {}
+
     private boolean queueObjective(DeviceId deviceId, ForwardingObjective fwd) {
         if (fwd.nextId() != null &&
                 flowObjectiveStore.getNextGroup(fwd.nextId()) == null) {
diff --git a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FilterTable.java b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FilterTable.java
new file mode 100644
index 0000000..b46ce8b
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FilterTable.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2015 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.flowobjective.impl.composition;
+
+import org.onosproject.net.flowobjective.FilteringObjective;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Provides a table to store Fitler.
+ */
+public class FilterTable {
+
+    protected Map<Integer, FilteringObjective> filterMap;
+
+    public FilterTable() {
+        this.filterMap = new HashMap<>();
+    }
+
+    public List<FilteringObjective> updateFilter(FilteringObjective filteringObjective) {
+        List<FilteringObjective> updates = new ArrayList<>();
+        switch (filteringObjective.op()) {
+            case ADD:
+                this.filterMap.put(filteringObjective.id(), filteringObjective);
+                updates.add(filteringObjective);
+                break;
+            case REMOVE:
+                this.filterMap.remove(filteringObjective.id());
+                updates.add(filteringObjective);
+                break;
+            default:
+                break;
+        }
+        return updates;
+    }
+
+    public List<FilteringObjective> updateFilter(List<FilteringObjective> filteringObjectives) {
+        List<FilteringObjective> updates = new ArrayList<>();
+        for (FilteringObjective filteringObjective : filteringObjectives) {
+            updates.addAll(this.updateFilter(filteringObjective));
+        }
+        return updates;
+    }
+
+}
diff --git a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionManager.java b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionManager.java
new file mode 100644
index 0000000..1d25c2e
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionManager.java
@@ -0,0 +1,439 @@
+/*
+ * Copyright 2015 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.flowobjective.impl.composition;
+
+import com.google.common.collect.Maps;
+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.apache.felix.scr.annotations.Service;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.util.ItemNotFoundException;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.core.Permission;
+import org.onosproject.mastership.MastershipEvent;
+import org.onosproject.mastership.MastershipListener;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.Pipeliner;
+import org.onosproject.net.behaviour.PipelinerContext;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.DefaultDriverProviderService;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.FlowObjectiveStore;
+import org.onosproject.net.flowobjective.FlowObjectiveStoreDelegate;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.NextObjective;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.flowobjective.ObjectiveError;
+import org.onosproject.net.flowobjective.ObjectiveEvent;
+import org.onosproject.net.group.GroupService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.Executors.newFixedThreadPool;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.security.AppGuard.checkPermission;
+
+
+/**
+ * Provides implementation of the flow objective programming service with composition feature.
+ *
+ * Note: This is an experimental, alternative implementation of the FlowObjectiveManager
+ * that supports composition. It can be enabled by setting the enable flag below to true,
+ * and you should also add "enabled = false" to the FlowObjectiveManager.
+ *
+ * The implementation relies a FlowObjectiveCompositionTree that is not yet distributed,
+ * so it will not have high availability and may break if device mastership changes.
+ * Therefore, it is safest to use this component in a single instance scenario.
+ * This comment will be removed when a distributed implementation is available.
+ */
+@Component(immediate = true, enabled = false)
+@Service
+public class FlowObjectiveCompositionManager implements FlowObjectiveService {
+
+    public enum PolicyOperator {
+        Parallel,
+        Sequential,
+        Override,
+        Application
+    }
+
+    public static final int INSTALL_RETRY_ATTEMPTS = 5;
+    public static final long INSTALL_RETRY_INTERVAL = 1000; // ms
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DriverService driverService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected MastershipService mastershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ClusterService clusterService;
+
+    // Note: The following dependencies are added on behalf of the pipeline
+    // driver behaviours to assure these services are available for their
+    // initialization.
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FlowRuleService flowRuleService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected GroupService groupService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FlowObjectiveStore flowObjectiveStore;
+
+    // Note: This must remain an optional dependency to allow re-install of default drivers.
+    // Note: For now disabled until we can move to OPTIONAL_UNARY dependency
+    // @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY, policy = ReferencePolicy.DYNAMIC)
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DefaultDriverProviderService defaultDriverService;
+
+    private final FlowObjectiveStoreDelegate delegate = new InternalStoreDelegate();
+
+    private final Map<DeviceId, DriverHandler> driverHandlers = Maps.newConcurrentMap();
+    private final Map<DeviceId, Pipeliner> pipeliners = Maps.newConcurrentMap();
+
+    private final PipelinerContext context = new InnerPipelineContext();
+    private final MastershipListener mastershipListener = new InnerMastershipListener();
+    private final DeviceListener deviceListener = new InnerDeviceListener();
+
+    protected ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
+
+    private Map<Integer, Set<PendingNext>> pendingForwards = Maps.newConcurrentMap();
+
+    private ExecutorService executorService;
+
+    private String policy;
+    private Map<DeviceId, FlowObjectiveCompositionTree> deviceCompositionTreeMap;
+
+    @Activate
+    protected void activate() {
+        executorService = newFixedThreadPool(4, groupedThreads("onos/objective-installer", "%d"));
+        flowObjectiveStore.setDelegate(delegate);
+        mastershipService.addListener(mastershipListener);
+        deviceService.addListener(deviceListener);
+        deviceService.getDevices().forEach(device -> setupPipelineHandler(device.id()));
+        deviceCompositionTreeMap = Maps.newConcurrentMap();
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        flowObjectiveStore.unsetDelegate(delegate);
+        mastershipService.removeListener(mastershipListener);
+        deviceService.removeListener(deviceListener);
+        executorService.shutdown();
+        pipeliners.clear();
+        driverHandlers.clear();
+        deviceCompositionTreeMap.clear();
+        log.info("Stopped");
+    }
+
+    /**
+     * Task that passes the flow objective down to the driver. The task will
+     * make a few attempts to find the appropriate driver, then eventually give
+     * up and report an error if no suitable driver could be found.
+     */
+    private class ObjectiveInstaller implements Runnable {
+        private final DeviceId deviceId;
+        private final Objective objective;
+
+        private final int numAttempts;
+
+        public ObjectiveInstaller(DeviceId deviceId, Objective objective) {
+            this(deviceId, objective, 1);
+        }
+
+        public ObjectiveInstaller(DeviceId deviceId, Objective objective, int attemps) {
+            this.deviceId = checkNotNull(deviceId);
+            this.objective = checkNotNull(objective);
+            this.numAttempts = checkNotNull(attemps);
+        }
+
+        @Override
+        public void run() {
+            try {
+                Pipeliner pipeliner = getDevicePipeliner(deviceId);
+
+                if (pipeliner != null) {
+                    if (objective instanceof NextObjective) {
+                        pipeliner.next((NextObjective) objective);
+                    } else if (objective instanceof ForwardingObjective) {
+                        pipeliner.forward((ForwardingObjective) objective);
+                    } else {
+                        pipeliner.filter((FilteringObjective) objective);
+                    }
+                } else if (numAttempts < INSTALL_RETRY_ATTEMPTS) {
+                    Thread.sleep(INSTALL_RETRY_INTERVAL);
+                    executorService.submit(new ObjectiveInstaller(deviceId, objective, numAttempts + 1));
+                } else {
+                    // Otherwise we've tried a few times and failed, report an
+                    // error back to the user.
+                    objective.context().ifPresent(
+                            c -> c.onError(objective, ObjectiveError.DEVICEMISSING));
+                }
+            } catch (Exception e) {
+                log.warn("Exception while installing flow objective", e);
+            }
+        }
+    }
+
+    @Override
+    public void filter(DeviceId deviceId, FilteringObjective filteringObjective) {
+        checkPermission(Permission.FLOWRULE_WRITE);
+
+        List<FilteringObjective> filteringObjectives
+                = this.deviceCompositionTreeMap.get(deviceId).updateFilter(filteringObjective);
+        for (FilteringObjective tmp : filteringObjectives) {
+            executorService.submit(new ObjectiveInstaller(deviceId, tmp));
+        }
+    }
+
+    @Override
+    public void forward(DeviceId deviceId, ForwardingObjective forwardingObjective) {
+        checkPermission(Permission.FLOWRULE_WRITE);
+
+        if (queueObjective(deviceId, forwardingObjective)) {
+            return;
+        }
+        List<ForwardingObjective> forwardingObjectives
+                = this.deviceCompositionTreeMap.get(deviceId).updateForward(forwardingObjective);
+        for (ForwardingObjective tmp : forwardingObjectives) {
+            executorService.submit(new ObjectiveInstaller(deviceId, tmp));
+        }
+    }
+
+    @Override
+    public void next(DeviceId deviceId, NextObjective nextObjective) {
+        checkPermission(Permission.FLOWRULE_WRITE);
+
+        List<NextObjective> nextObjectives = this.deviceCompositionTreeMap.get(deviceId).updateNext(nextObjective);
+        for (NextObjective tmp : nextObjectives) {
+            executorService.submit(new ObjectiveInstaller(deviceId, tmp));
+        }
+    }
+
+    @Override
+    public int allocateNextId() {
+        checkPermission(Permission.FLOWRULE_WRITE);
+
+        return flowObjectiveStore.allocateNextId();
+    }
+
+    private boolean queueObjective(DeviceId deviceId, ForwardingObjective fwd) {
+        if (fwd.nextId() != null &&
+                flowObjectiveStore.getNextGroup(fwd.nextId()) == null) {
+            log.trace("Queuing forwarding objective for nextId {}", fwd.nextId());
+            if (pendingForwards.putIfAbsent(fwd.nextId(),
+                    Sets.newHashSet(new PendingNext(deviceId, fwd))) != null) {
+                Set<PendingNext> pending = pendingForwards.get(fwd.nextId());
+                pending.add(new PendingNext(deviceId, fwd));
+            }
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void initPolicy(String policy) {
+        this.policy = policy;
+        deviceService.getDevices().forEach(device ->
+                this.deviceCompositionTreeMap.put(device.id(), FlowObjectiveCompositionUtil.parsePolicyString(policy)));
+        log.info("Initialize policy {}", policy);
+    }
+
+    // Retrieves the device pipeline behaviour from the cache.
+    private Pipeliner getDevicePipeliner(DeviceId deviceId) {
+        Pipeliner pipeliner = pipeliners.get(deviceId);
+        return pipeliner;
+    }
+
+    private void setupPipelineHandler(DeviceId deviceId) {
+        if (defaultDriverService == null) {
+            // We're not ready to go to work yet.
+            return;
+        }
+
+        // Attempt to lookup the handler in the cache
+        DriverHandler handler = driverHandlers.get(deviceId);
+        if (handler == null) {
+            try {
+                // Otherwise create it and if it has pipeline behaviour, cache it
+                handler = driverService.createHandler(deviceId);
+                if (!handler.driver().hasBehaviour(Pipeliner.class)) {
+                    log.warn("Pipeline behaviour not supported for device {}",
+                            deviceId);
+                    return;
+                }
+            } catch (ItemNotFoundException e) {
+                log.warn("No applicable driver for device {}", deviceId);
+                return;
+            }
+
+            driverHandlers.put(deviceId, handler);
+        }
+
+        // Always (re)initialize the pipeline behaviour
+        log.info("Driver {} bound to device {} ... initializing driver",
+                handler.driver().name(), deviceId);
+        Pipeliner pipeliner = handler.behaviour(Pipeliner.class);
+        pipeliner.init(deviceId, context);
+        pipeliners.putIfAbsent(deviceId, pipeliner);
+    }
+
+    // Triggers driver setup when the local node becomes a device master.
+    private class InnerMastershipListener implements MastershipListener {
+        @Override
+        public void event(MastershipEvent event) {
+            switch (event.type()) {
+                case MASTER_CHANGED:
+                    log.debug("mastership changed on device {}", event.subject());
+                    if (deviceService.isAvailable(event.subject())) {
+                        setupPipelineHandler(event.subject());
+                    }
+                    break;
+                case BACKUPS_CHANGED:
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    // Triggers driver setup when a device is (re)detected.
+    private class InnerDeviceListener implements DeviceListener {
+        @Override
+        public void event(DeviceEvent event) {
+            switch (event.type()) {
+                case DEVICE_ADDED:
+                case DEVICE_AVAILABILITY_CHANGED:
+                    log.debug("Device either added or availability changed {}",
+                            event.subject().id());
+                    if (deviceService.isAvailable(event.subject().id())) {
+                        log.debug("Device is now available {}", event.subject().id());
+                        setupPipelineHandler(event.subject().id());
+                    }
+                    break;
+                case DEVICE_UPDATED:
+                    break;
+                case DEVICE_REMOVED:
+                    break;
+                case DEVICE_SUSPENDED:
+                    break;
+                case PORT_ADDED:
+                    break;
+                case PORT_UPDATED:
+                    break;
+                case PORT_REMOVED:
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    // Processing context for initializing pipeline driver behaviours.
+    private class InnerPipelineContext implements PipelinerContext {
+        @Override
+        public ServiceDirectory directory() {
+            return serviceDirectory;
+        }
+
+        @Override
+        public FlowObjectiveStore store() {
+            return flowObjectiveStore;
+        }
+    }
+
+    private class InternalStoreDelegate implements FlowObjectiveStoreDelegate {
+        @Override
+        public void notify(ObjectiveEvent event) {
+            log.debug("Received notification of obj event {}", event);
+            Set<PendingNext> pending = pendingForwards.remove(event.subject());
+
+            if (pending == null) {
+                log.debug("Nothing pending for this obj event");
+                return;
+            }
+
+            log.debug("Processing pending forwarding objectives {}", pending.size());
+
+            pending.forEach(p -> getDevicePipeliner(p.deviceId())
+                    .forward(p.forwardingObjective()));
+
+        }
+    }
+
+    /**
+     * Data class used to hold a pending forwarding objective that could not
+     * be processed because the associated next object was not present.
+     */
+    private class PendingNext {
+        private final DeviceId deviceId;
+        private final ForwardingObjective fwd;
+
+        public PendingNext(DeviceId deviceId, ForwardingObjective fwd) {
+            this.deviceId = deviceId;
+            this.fwd = fwd;
+        }
+
+        public DeviceId deviceId() {
+            return deviceId;
+        }
+
+        public ForwardingObjective forwardingObjective() {
+            return fwd;
+        }
+    }
+
+    public static String forwardingObjectiveToString(ForwardingObjective forwardingObjective) {
+        String str = forwardingObjective.priority() + " ";
+        str += "selector( ";
+        for (Criterion criterion : forwardingObjective.selector().criteria()) {
+            str += criterion + " ";
+        }
+        str += ") treatment( ";
+        for (Instruction instruction : forwardingObjective.treatment().allInstructions()) {
+            str += instruction + " ";
+        }
+        str += ")";
+        return str;
+    }
+}
diff --git a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionTree.java b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionTree.java
new file mode 100644
index 0000000..152622b
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionTree.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2015 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.flowobjective.impl.composition;
+
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.NextObjective;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Provides a policy tree to store all flow tables for each device.
+ *
+ * Note: This class uses in-memory structures and is not yet distributed.
+ */
+public class FlowObjectiveCompositionTree {
+
+    public FlowObjectiveCompositionManager.PolicyOperator operator;
+    public FlowObjectiveCompositionTree leftChild;
+    public FlowObjectiveCompositionTree rightChild;
+    public short applicationId;
+    protected FilterTable filterTable;
+    protected ForwardTable forwardTable;
+    protected NextTable nextTable;
+
+    protected int priorityMultiplier;
+    protected int priorityAddend;
+
+    public FlowObjectiveCompositionTree(short applicationId) {
+        this.operator = FlowObjectiveCompositionManager.PolicyOperator.Application;
+        this.leftChild = null;
+        this.rightChild = null;
+        this.applicationId = applicationId;
+        this.filterTable = new FilterTable();
+        this.forwardTable = new ForwardTable();
+        this.nextTable = new NextTable();
+        this.priorityMultiplier = 10;
+        this.priorityAddend = 10;
+    }
+
+    public FlowObjectiveCompositionTree(Character ch) {
+        switch (ch) {
+            case '+':
+                this.operator = FlowObjectiveCompositionManager.PolicyOperator.Parallel;
+                break;
+            case '>':
+                this.operator = FlowObjectiveCompositionManager.PolicyOperator.Sequential;
+                break;
+            case '/':
+                this.operator = FlowObjectiveCompositionManager.PolicyOperator.Override;
+                break;
+            default:
+                this.operator = FlowObjectiveCompositionManager.PolicyOperator.Application;
+                break;
+        }
+        this.leftChild = null;
+        this.rightChild = null;
+        this.applicationId = (short) -1;
+        this.filterTable = new FilterTable();
+        this.forwardTable = new ForwardTable();
+        this.nextTable = new NextTable();
+        this.priorityMultiplier = 10;
+        this.priorityAddend = 10;
+    }
+
+    protected List<FilteringObjective> updateFilter(FilteringObjective filteringObjective) {
+        switch (this.operator) {
+            case Parallel:
+                return updateFilterParallel(filteringObjective);
+            case Sequential:
+                return updateFilterSequential(filteringObjective);
+            case Override:
+                return updateFilterOverride(filteringObjective);
+            case Application:
+                if (filteringObjective.appId().id() == this.applicationId) {
+                    return this.filterTable.updateFilter(filteringObjective);
+                } else {
+                    return new ArrayList<>();
+                }
+            default:
+                    return new ArrayList<>();
+        }
+    }
+
+    // Parallel composition: the filter set is the union of the children
+    protected List<FilteringObjective> updateFilterParallel(FilteringObjective filteringObjective) {
+        List<FilteringObjective> leftUpdates = this.leftChild.updateFilter(filteringObjective);
+        List<FilteringObjective> rightUpdates = this.rightChild.updateFilter(filteringObjective);
+
+        List<FilteringObjective> updates = new ArrayList<>();
+        updates.addAll(leftUpdates);
+        updates.addAll(rightUpdates);
+
+        return this.filterTable.updateFilter(updates);
+    }
+
+    // Sequential composition: the filter set is the filter set of the left child
+    protected List<FilteringObjective> updateFilterSequential(FilteringObjective filteringObjective) {
+        List<FilteringObjective> leftUpdates = this.leftChild.updateFilter(filteringObjective);
+        List<FilteringObjective> rightUpdates = this.rightChild.updateFilter(filteringObjective);
+        return this.filterTable.updateFilter(leftUpdates);
+    }
+
+    // Override composition: the filter set is the filter set of the left child
+    protected List<FilteringObjective> updateFilterOverride(FilteringObjective filteringObjective) {
+        List<FilteringObjective> leftUpdates = this.leftChild.updateFilter(filteringObjective);
+        List<FilteringObjective> rightUpdates = this.rightChild.updateFilter(filteringObjective);
+        return this.filterTable.updateFilter(leftUpdates);
+    }
+
+    public List<ForwardingObjective> updateForward(ForwardingObjective forwardingObjective) {
+        return this.updateForwardNode(forwardingObjective).toForwardingObjectiveList();
+    }
+
+    public ForwardUpdateTable updateForwardNode(ForwardingObjective forwardingObjective) {
+        switch (this.operator) {
+            case Parallel:
+            case Sequential:
+            case Override:
+                return updateForwardComposition(forwardingObjective);
+            case Application:
+                if (forwardingObjective.appId().id() == this.applicationId) {
+                    return this.forwardTable.updateForward(forwardingObjective);
+                } else {
+                    return (new ForwardUpdateTable());
+                }
+            default:
+                return (new ForwardUpdateTable());
+        }
+    }
+
+    protected ForwardUpdateTable updateForwardComposition(ForwardingObjective forwardingObjective) {
+        ForwardUpdateTable leftUpdates = this.leftChild.updateForwardNode(forwardingObjective);
+        ForwardUpdateTable rightUpdates = this.rightChild.updateForwardNode(forwardingObjective);
+
+        List<ForwardingObjective> addUpdates = new ArrayList<>();
+        List<ForwardingObjective> removeUpdates = new ArrayList<>();
+        // Handle ADD
+        if (this.operator == FlowObjectiveCompositionManager.PolicyOperator.Parallel
+                || this.operator == FlowObjectiveCompositionManager.PolicyOperator.Sequential) {
+            for (ForwardingObjective fo1 : leftUpdates.addObjectives) {
+                for (ForwardingObjective fo2 : this.rightChild.forwardTable.getForwardingObjectives()) {
+                    ForwardingObjective composedFo = null;
+                    if (this.operator == FlowObjectiveCompositionManager.PolicyOperator.Parallel) {
+                        composedFo = FlowObjectiveCompositionUtil.composeParallel(fo1, fo2);
+                    } else {
+                        composedFo = FlowObjectiveCompositionUtil.composeSequential(fo1, fo2, this.priorityMultiplier);
+                    }
+                    if (composedFo != null) {
+                        addUpdates.add(composedFo);
+                        this.leftChild.forwardTable.addGeneratedParentForwardingObjective(fo1, composedFo);
+                        this.rightChild.forwardTable.addGeneratedParentForwardingObjective(fo2, composedFo);
+                    }
+                }
+            }
+            Collection<ForwardingObjective> leftTableWithoutAdd = FlowObjectiveCompositionUtil
+                    .minusForwardingObjectives(this.leftChild.forwardTable.getForwardingObjectives(),
+                            leftUpdates.addObjectives);
+            for (ForwardingObjective fo1 : leftTableWithoutAdd) {
+                for (ForwardingObjective fo2 : rightUpdates.addObjectives) {
+                    ForwardingObjective composedFo = null;
+                    if (this.operator == FlowObjectiveCompositionManager.PolicyOperator.Parallel) {
+                        composedFo = FlowObjectiveCompositionUtil.composeParallel(fo1, fo2);
+                    } else {
+                        composedFo = FlowObjectiveCompositionUtil.composeSequential(fo1, fo2, this.priorityMultiplier);
+                    }
+                    if (composedFo != null) {
+                        addUpdates.add(composedFo);
+                        this.leftChild.forwardTable.addGeneratedParentForwardingObjective(fo1, composedFo);
+                        this.rightChild.forwardTable.addGeneratedParentForwardingObjective(fo2, composedFo);
+                    }
+                }
+            }
+        } else {
+            for (ForwardingObjective fo : leftUpdates.addObjectives) {
+                ForwardingObjective composedFo = FlowObjectiveCompositionUtil.composeOverride(fo, this.priorityAddend);
+                addUpdates.add(composedFo);
+                this.leftChild.forwardTable.addGeneratedParentForwardingObjective(fo, composedFo);
+            }
+            for (ForwardingObjective fo : rightUpdates.addObjectives) {
+                ForwardingObjective composedFo = FlowObjectiveCompositionUtil.composeOverride(fo, 0);
+                addUpdates.add(composedFo);
+                this.rightChild.forwardTable.addGeneratedParentForwardingObjective(fo, composedFo);
+            }
+        }
+
+        // Handle REMOVE
+        for (ForwardingObjective fo : leftUpdates.removeObjectives) {
+            List<ForwardingObjective> fos = this.leftChild.forwardTable
+                    .getGeneratedParentForwardingObjectiveForRemove(fo);
+            removeUpdates.addAll(fos);
+        }
+        this.leftChild.forwardTable.deleteGeneratedParentForwardingObjective(leftUpdates.removeObjectives);
+        for (ForwardingObjective fo : rightUpdates.removeObjectives) {
+            List<ForwardingObjective> fos = this.rightChild.forwardTable
+                    .getGeneratedParentForwardingObjectiveForRemove(fo);
+            removeUpdates.addAll(fos);
+        }
+        this.rightChild.forwardTable.deleteGeneratedParentForwardingObjective(rightUpdates.removeObjectives);
+
+        ForwardUpdateTable updates = new ForwardUpdateTable();
+        updates.addUpdateTable(this.forwardTable.updateForward(addUpdates));
+        updates.addUpdateTable(this.forwardTable.updateForward(removeUpdates));
+        return updates;
+    }
+
+    public List<NextObjective> updateNext(NextObjective nextObjective) {
+        switch (this.operator) {
+            case Parallel:
+            case Sequential:
+            case Override:
+                return updateNextComposition(nextObjective);
+            case Application:
+                if (nextObjective.appId().id() == this.applicationId) {
+                    return this.nextTable.updateNext(nextObjective);
+                } else {
+                    return new ArrayList<>();
+                }
+            default:
+                return new ArrayList<>();
+        }
+    }
+
+    // Next: the union of the children
+    protected List<NextObjective> updateNextComposition(NextObjective nextObjective) {
+        List<NextObjective> leftUpdates = this.leftChild.updateNext(nextObjective);
+        List<NextObjective> rightUpdates = this.rightChild.updateNext(nextObjective);
+
+        List<NextObjective> updates = new ArrayList<>();
+        updates.addAll(leftUpdates);
+        updates.addAll(rightUpdates);
+
+        return this.nextTable.updateNext(updates);
+    }
+
+    @Override
+    public String toString() {
+        String str = null;
+        switch (this.operator) {
+            case Parallel:
+                str = "(" + this.leftChild + "+" + this.rightChild + ")";
+                break;
+            case Sequential:
+                str = "(" + this.leftChild + ">" + this.rightChild + ")";
+                break;
+            case Override:
+                str = "(" + this.leftChild + "/" + this.rightChild + ")";
+                break;
+            default:
+                str = " " + applicationId + " ";
+                break;
+        }
+        return str;
+    }
+
+}
diff --git a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionUtil.java b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionUtil.java
new file mode 100644
index 0000000..0cc383b
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionUtil.java
@@ -0,0 +1,488 @@
+/*
+ * Copyright 2015 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.flowobjective.impl.composition;
+
+import org.onlab.packet.IpPrefix;
+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.LambdaCriterion;
+import org.onosproject.net.flow.criteria.OchSignalCriterion;
+import org.onosproject.net.flow.criteria.EthCriterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
+import org.onosproject.net.flow.criteria.VlanPcpCriterion;
+import org.onosproject.net.flow.criteria.MplsCriterion;
+import org.onosproject.net.flow.criteria.IPCriterion;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.L0ModificationInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+/**
+ * Provide util functions for FlowObjectiveComposition.
+ */
+public final class FlowObjectiveCompositionUtil {
+
+    private FlowObjectiveCompositionUtil() {}
+
+    // only work with VERSATILE
+    public static ForwardingObjective composeParallel(ForwardingObjective fo1, ForwardingObjective fo2) {
+
+        TrafficSelector trafficSelector = intersectTrafficSelector(fo1.selector(), fo2.selector());
+        if (trafficSelector == null) {
+            return null;
+        }
+
+        TrafficTreatment trafficTreatment = unionTrafficTreatment(fo1.treatment(), fo2.treatment());
+
+        return DefaultForwardingObjective.builder()
+                .fromApp(fo1.appId())
+                .makePermanent()
+                .withFlag(ForwardingObjective.Flag.VERSATILE)
+                .withPriority(fo1.priority() + fo2.priority())
+                .withSelector(trafficSelector)
+                .withTreatment(trafficTreatment)
+                .add();
+    }
+
+    public static ForwardingObjective composeSequential(ForwardingObjective fo1,
+                                                        ForwardingObjective fo2,
+                                                        int priorityMultiplier) {
+
+        TrafficSelector revertTrafficSelector = revertTreatmentSelector(fo1.treatment(), fo2.selector());
+        if (revertTrafficSelector == null) {
+            return null;
+        }
+
+        TrafficSelector trafficSelector = intersectTrafficSelector(fo1.selector(), revertTrafficSelector);
+        if (trafficSelector == null) {
+            return null;
+        }
+
+        TrafficTreatment trafficTreatment = unionTrafficTreatment(fo1.treatment(), fo2.treatment());
+
+        return DefaultForwardingObjective.builder()
+                .fromApp(fo1.appId())
+                .makePermanent()
+                .withFlag(ForwardingObjective.Flag.VERSATILE)
+                .withPriority(fo1.priority() * priorityMultiplier + fo2.priority())
+                .withSelector(trafficSelector)
+                .withTreatment(trafficTreatment)
+                .add();
+    }
+
+    public static ForwardingObjective composeOverride(ForwardingObjective fo, int priorityAddend) {
+        return DefaultForwardingObjective.builder()
+                .fromApp(fo.appId())
+                .makePermanent()
+                .withFlag(fo.flag())
+                .withPriority(fo.priority() + priorityAddend)
+                .withSelector(fo.selector())
+                .withTreatment(fo.treatment())
+                .add();
+    }
+
+    public static TrafficSelector intersectTrafficSelector(TrafficSelector ts1, TrafficSelector ts2) {
+
+        TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
+
+        Set<Criterion.Type> ts1IntersectTs2 = getTypeSet(ts1);
+        ts1IntersectTs2.retainAll(getTypeSet(ts2));
+        for (Criterion.Type type : ts1IntersectTs2) {
+            Criterion criterion = intersectCriterion(ts1.getCriterion(type), ts2.getCriterion(type));
+            if (criterion == null) {
+                return null;
+            } else {
+                selectorBuilder.add(criterion);
+            }
+        }
+
+        Set<Criterion.Type> ts1MinusTs2 = getTypeSet(ts1);
+        ts1MinusTs2.removeAll(getTypeSet(ts2));
+        for (Criterion.Type type : ts1MinusTs2) {
+            selectorBuilder.add(ts1.getCriterion(type));
+        }
+
+        Set<Criterion.Type> ts2MinusTs1 = getTypeSet(ts2);
+        ts2MinusTs1.removeAll(getTypeSet(ts1));
+        for (Criterion.Type type : ts2MinusTs1) {
+            selectorBuilder.add(ts2.getCriterion(type));
+        }
+
+        return selectorBuilder.build();
+    }
+
+    public static TrafficTreatment unionTrafficTreatment(TrafficTreatment tt1, TrafficTreatment tt2) {
+
+        TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
+
+        for (Instruction instruction : tt1.allInstructions()) {
+            treatmentBuilder.add(instruction);
+        }
+
+        for (Instruction instruction : tt2.allInstructions()) {
+            treatmentBuilder.add(instruction);
+        }
+
+        return treatmentBuilder.build();
+    }
+
+    public static TrafficSelector revertTreatmentSelector(TrafficTreatment trafficTreatment,
+                                                          TrafficSelector trafficSelector) {
+
+        TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
+
+        Map<Criterion.Type, Criterion> criterionMap = new HashMap<>();
+        for (Criterion criterion : trafficSelector.criteria()) {
+            criterionMap.put(criterion.type(), criterion);
+        }
+
+        for (Instruction instruction : trafficTreatment.allInstructions()) {
+            switch (instruction.type()) {
+                case DROP:
+                    return null;
+                case OUTPUT:
+                    break;
+                case GROUP:
+                    break;
+                case L0MODIFICATION: {
+                    L0ModificationInstruction l0 = (L0ModificationInstruction) instruction;
+                    switch (l0.subtype()) {
+                        case LAMBDA:
+                            if (criterionMap.containsKey(Criterion.Type.OCH_SIGID)) {
+                                if (((LambdaCriterion) criterionMap.get((Criterion.Type.OCH_SIGID))).lambda()
+                                        == ((L0ModificationInstruction.ModLambdaInstruction) l0).lambda()) {
+                                    criterionMap.remove(Criterion.Type.OCH_SIGID);
+                                } else {
+                                    return null;
+                                }
+                            } else {
+                                break;
+                            }
+                        case OCH:
+                            if (criterionMap.containsKey(Criterion.Type.OCH_SIGID)) {
+                                if (((OchSignalCriterion) criterionMap.get((Criterion.Type.OCH_SIGID))).lambda()
+                                        .equals(((L0ModificationInstruction.ModOchSignalInstruction) l0).lambda())) {
+                                    criterionMap.remove(Criterion.Type.OCH_SIGID);
+                                } else {
+                                    return null;
+                                }
+                            } else {
+                                break;
+                            }
+                        default:
+                            break;
+                    }
+                    break;
+                }
+                case L2MODIFICATION: {
+                    L2ModificationInstruction l2 = (L2ModificationInstruction) instruction;
+                    switch (l2.subtype()) {
+                        case ETH_SRC:
+                            if (criterionMap.containsKey(Criterion.Type.ETH_SRC)) {
+                                if (((EthCriterion) criterionMap.get((Criterion.Type.ETH_SRC))).mac()
+                                        .equals(((L2ModificationInstruction.ModEtherInstruction) l2).mac())) {
+                                    criterionMap.remove(Criterion.Type.ETH_SRC);
+                                } else {
+                                    return null;
+                                }
+                            } else {
+                                break;
+                            }
+                        case ETH_DST:
+                            if (criterionMap.containsKey(Criterion.Type.ETH_DST)) {
+                                if (((EthCriterion) criterionMap.get((Criterion.Type.ETH_DST))).mac()
+                                        .equals(((L2ModificationInstruction.ModEtherInstruction) l2).mac())) {
+                                    criterionMap.remove(Criterion.Type.ETH_DST);
+                                } else {
+                                    return null;
+                                }
+                            } else {
+                                break;
+                            }
+                        case VLAN_ID:
+                            if (criterionMap.containsKey(Criterion.Type.VLAN_VID)) {
+                                if (((VlanIdCriterion) criterionMap.get((Criterion.Type.VLAN_VID))).vlanId()
+                                        .equals(((L2ModificationInstruction.ModVlanIdInstruction) l2).vlanId())) {
+                                    criterionMap.remove(Criterion.Type.VLAN_VID);
+                                } else {
+                                    return null;
+                                }
+                            } else {
+                                break;
+                            }
+                        case VLAN_PCP:
+                            if (criterionMap.containsKey(Criterion.Type.VLAN_PCP)) {
+                                if (((VlanPcpCriterion) criterionMap.get((Criterion.Type.VLAN_PCP))).priority()
+                                        == ((L2ModificationInstruction.ModVlanPcpInstruction) l2).vlanPcp()) {
+                                    criterionMap.remove(Criterion.Type.VLAN_PCP);
+                                } else {
+                                    return null;
+                                }
+                            } else {
+                                break;
+                            }
+                        case MPLS_LABEL:
+                            if (criterionMap.containsKey(Criterion.Type.MPLS_LABEL)) {
+                                if (((MplsCriterion) criterionMap.get((Criterion.Type.MPLS_LABEL))).label()
+                                        .equals(((L2ModificationInstruction.ModMplsLabelInstruction) l2).label())) {
+                                    criterionMap.remove(Criterion.Type.ETH_DST);
+                                } else {
+                                    return null;
+                                }
+                            } else {
+                                break;
+                            }
+                        default:
+                            break;
+                    }
+                    break;
+                }
+                case TABLE:
+                    break;
+                case L3MODIFICATION: {
+                    L3ModificationInstruction l3 = (L3ModificationInstruction) instruction;
+                    switch (l3.subtype()) {
+                        case IPV4_SRC:
+                            if (criterionMap.containsKey(Criterion.Type.IPV4_SRC)) {
+                                if (((IPCriterion) criterionMap.get(Criterion.Type.IPV4_SRC)).ip()
+                                        .contains(((L3ModificationInstruction.ModIPInstruction) l3).ip())) {
+                                    criterionMap.remove(Criterion.Type.IPV4_SRC);
+                                } else {
+                                    return null;
+                                }
+                            } else {
+                                break;
+                            }
+                        case IPV4_DST:
+                            if (criterionMap.containsKey(Criterion.Type.IPV4_DST)) {
+                                if (((IPCriterion) criterionMap.get(Criterion.Type.IPV4_DST)).ip()
+                                        .contains(((L3ModificationInstruction.ModIPInstruction) l3).ip())) {
+                                    criterionMap.remove(Criterion.Type.IPV4_DST);
+                                } else {
+                                    return null;
+                                }
+                            } else {
+                                break;
+                            }
+                        case IPV6_SRC:
+                            if (criterionMap.containsKey(Criterion.Type.IPV6_SRC)) {
+                                if (((IPCriterion) criterionMap.get(Criterion.Type.IPV6_SRC)).ip()
+                                        .contains(((L3ModificationInstruction.ModIPInstruction) l3).ip())) {
+                                    criterionMap.remove(Criterion.Type.IPV6_SRC);
+                                } else {
+                                    return null;
+                                }
+                            } else {
+                                break;
+                            }
+                        case IPV6_DST:
+                            if (criterionMap.containsKey(Criterion.Type.IPV6_DST)) {
+                                if (((IPCriterion) criterionMap.get(Criterion.Type.IPV6_DST)).ip()
+                                        .contains(((L3ModificationInstruction.ModIPInstruction) l3).ip())) {
+                                    criterionMap.remove(Criterion.Type.IPV6_DST);
+                                } else {
+                                    return null;
+                                }
+                            } else {
+                                break;
+                            }
+                        case IPV6_FLABEL:
+                            if (criterionMap.containsKey(Criterion.Type.IPV6_FLABEL)) {
+                                if (((IPCriterion) criterionMap.get(Criterion.Type.IPV6_FLABEL)).ip()
+                                        .equals(((L3ModificationInstruction.ModIPv6FlowLabelInstruction) l3)
+                                                .flowLabel())) {
+                                    criterionMap.remove(Criterion.Type.IPV4_SRC);
+                                } else {
+                                    return null;
+                                }
+                            } else {
+                                break;
+                            }
+                        default:
+                            break;
+                    }
+                    break;
+                }
+                case METADATA:
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        for (Criterion criterion : criterionMap.values()) {
+            selectorBuilder.add(criterion);
+        }
+
+        return selectorBuilder.build();
+    }
+
+    public static Set<Criterion.Type> getTypeSet(TrafficSelector trafficSelector) {
+        Set<Criterion.Type> typeSet = new HashSet<>();
+        for (Criterion criterion : trafficSelector.criteria()) {
+            typeSet.add(criterion.type());
+        }
+        return typeSet;
+    }
+
+    public static Criterion intersectCriterion(Criterion c1, Criterion c2) {
+        switch (c1.type()) {
+            case IPV4_SRC: {
+                IpPrefix ipPrefix = intersectIpPrefix(((IPCriterion) c1).ip(), ((IPCriterion) c2).ip());
+                if (ipPrefix == null) {
+                    return null;
+                } else {
+                    return Criteria.matchIPSrc(ipPrefix);
+                }
+            }
+            case IPV4_DST: {
+                IpPrefix ipPrefix = intersectIpPrefix(((IPCriterion) c1).ip(), ((IPCriterion) c2).ip());
+                if (ipPrefix == null) {
+                    return null;
+                } else {
+                    return Criteria.matchIPDst(ipPrefix);
+                }
+            }
+            case IPV6_SRC: {
+                IpPrefix ipPrefix = intersectIpPrefix(((IPCriterion) c1).ip(), ((IPCriterion) c2).ip());
+                if (ipPrefix == null) {
+                    return null;
+                } else {
+                    return Criteria.matchIPv6Src(ipPrefix);
+                }
+            }
+            case IPV6_DST: {
+                IpPrefix ipPrefix = intersectIpPrefix(((IPCriterion) c1).ip(), ((IPCriterion) c2).ip());
+                if (ipPrefix == null) {
+                    return null;
+                } else {
+                    return Criteria.matchIPv6Dst(ipPrefix);
+                }
+            }
+            default:
+                if (!c1.equals(c2)) {
+                    return null;
+                } else {
+                    return c1;
+                }
+        }
+    }
+
+    public static IpPrefix intersectIpPrefix(IpPrefix ip1, IpPrefix ip2) {
+        if (ip1.contains(ip2)) {
+            return ip1;
+        } else if (ip2.contains(ip1)) {
+            return ip2;
+        } else {
+            return null;
+        }
+    }
+
+    public static FlowObjectiveCompositionTree parsePolicyString(String policy) {
+        List<FlowObjectiveCompositionTree> postfix = transformToPostfixForm(policy);
+        return buildPolicyTree(postfix);
+    }
+
+    private static List<FlowObjectiveCompositionTree> transformToPostfixForm(String policy) {
+        Stack<Character> stack = new Stack<>();
+        List<FlowObjectiveCompositionTree> postfix = new ArrayList<>();
+
+        for (int i = 0; i < policy.length(); i++) {
+            Character ch = policy.charAt(i);
+            if (Character.isDigit(ch)) {
+
+                int applicationId = ch - '0';
+                while (i + 1 < policy.length() && Character.isDigit(policy.charAt(i + 1))) {
+                    i++;
+                    applicationId = applicationId * 10 + policy.charAt(i) - '0';
+                }
+
+                postfix.add(new FlowObjectiveCompositionTree((short) applicationId));
+            } else if (ch == '(') {
+                stack.push(ch);
+            } else if (ch == ')') {
+                while (stack.peek() != '(') {
+                    postfix.add(new FlowObjectiveCompositionTree(stack.pop()));
+                }
+                stack.pop();
+            } else {
+                while (!stack.isEmpty() && compareOperatorPriority(stack.peek(), ch)) {
+                    postfix.add(new FlowObjectiveCompositionTree(stack.pop()));
+                }
+                stack.push(ch);
+            }
+        }
+        while (!stack.isEmpty()) {
+            postfix.add(new FlowObjectiveCompositionTree(stack.pop()));
+        }
+
+        return postfix;
+    }
+
+    private static boolean compareOperatorPriority(char peek, char cur) {
+        if (peek == '/' && (cur == '+' || cur == '>' || cur == '/')) {
+            return true;
+        } else if (peek == '>' && (cur == '+' || cur == '>')) {
+            return true;
+        } else if (peek == '+' && cur == '+') {
+            return true;
+        }
+        return false;
+    }
+
+    private static FlowObjectiveCompositionTree buildPolicyTree(List<FlowObjectiveCompositionTree> postfix) {
+        Stack<FlowObjectiveCompositionTree> stack = new Stack<>();
+        for (FlowObjectiveCompositionTree node : postfix) {
+            if (node.operator == FlowObjectiveCompositionManager.PolicyOperator.Application) {
+                stack.push(node);
+            } else {
+                node.rightChild = stack.pop();
+                node.leftChild = stack.pop();
+                stack.push(node);
+            }
+        }
+        return stack.pop();
+    }
+
+    public static Collection<ForwardingObjective> minusForwardingObjectives(Collection<ForwardingObjective> fo1,
+                                                                            Collection<ForwardingObjective> fo2) {
+        Map<Integer, ForwardingObjective> map = new HashMap<>();
+        for (ForwardingObjective fo : fo1) {
+            map.put(fo.id(), fo);
+        }
+        for (ForwardingObjective fo : fo2) {
+            map.remove(fo.id());
+        }
+        return map.values();
+    }
+
+
+}
diff --git a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/ForwardTable.java b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/ForwardTable.java
new file mode 100644
index 0000000..1384bbe
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/ForwardTable.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2015 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.flowobjective.impl.composition;
+
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Provides a table to store Forward.
+ */
+public class ForwardTable {
+
+    protected Map<Integer, ForwardingObjective> forwardMap;
+    protected Map<Integer, List<ForwardingObjective>> generatedParentForwardingObjectiveMap;
+
+    public ForwardTable() {
+        this.forwardMap = new HashMap<>();
+        this.generatedParentForwardingObjectiveMap = new HashMap<>();
+    }
+
+    public ForwardUpdateTable updateForward(ForwardingObjective forwardingObjective) {
+        ForwardUpdateTable updates = new ForwardUpdateTable();
+        switch (forwardingObjective.op()) {
+            case ADD:
+                this.forwardMap.put(forwardingObjectiveHash(forwardingObjective), forwardingObjective);
+                this.generatedParentForwardingObjectiveMap
+                        .put(forwardingObjectiveHash(forwardingObjective), new ArrayList<>());
+                updates.addObjectives.add(forwardingObjective);
+                break;
+            case REMOVE:
+                if (this.forwardMap.remove(forwardingObjectiveHash(forwardingObjective)) != null) {
+                    updates.removeObjectives.add(forwardingObjective);
+                }
+                break;
+            default:
+                break;
+        }
+        return updates;
+    }
+
+    public ForwardUpdateTable updateForward(List<ForwardingObjective> forwardingObjectives) {
+        ForwardUpdateTable updates = new ForwardUpdateTable();
+        for (ForwardingObjective forwardingObjective : forwardingObjectives) {
+            updates.addUpdateTable(this.updateForward(forwardingObjective));
+        }
+        return updates;
+    }
+
+    public void addGeneratedParentForwardingObjective(ForwardingObjective child, ForwardingObjective parent) {
+        this.generatedParentForwardingObjectiveMap.get(forwardingObjectiveHash(child)).add(parent);
+    }
+
+    public void deleteGeneratedParentForwardingObjective(List<ForwardingObjective> children) {
+        for (ForwardingObjective fo : children) {
+            this.generatedParentForwardingObjectiveMap.remove(forwardingObjectiveHash(fo));
+        }
+    }
+
+    private List<ForwardingObjective> getGeneratedParentForwardingObjective(ForwardingObjective child) {
+        return this.generatedParentForwardingObjectiveMap.get(forwardingObjectiveHash(child));
+    }
+
+    public List<ForwardingObjective> getGeneratedParentForwardingObjectiveForRemove(ForwardingObjective child) {
+        List<ForwardingObjective> fos = this.generatedParentForwardingObjectiveMap.get(forwardingObjectiveHash(child));
+        List<ForwardingObjective> removeFos = new ArrayList<>();
+        for (ForwardingObjective fo : fos) {
+            removeFos.add(DefaultForwardingObjective.builder()
+                    .fromApp(fo.appId())
+                    .makePermanent()
+                    .withFlag(fo.flag())
+                    .withPriority(fo.priority())
+                    .withSelector(fo.selector())
+                    .withTreatment(fo.treatment())
+                    .remove());
+        }
+        return removeFos;
+    }
+
+    public Collection<ForwardingObjective> getForwardingObjectives() {
+        return this.forwardMap.values();
+    }
+
+    public static int forwardingObjectiveHash(ForwardingObjective forwardingObjective) {
+        return Objects.hash(forwardingObjective.selector(), forwardingObjective.flag(),
+                forwardingObjective.permanent(), forwardingObjective.timeout(),
+                forwardingObjective.appId(), forwardingObjective.priority(),
+                forwardingObjective.nextId(), forwardingObjective.treatment());
+    }
+}
diff --git a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/ForwardUpdateTable.java b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/ForwardUpdateTable.java
new file mode 100644
index 0000000..9818cfd
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/ForwardUpdateTable.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2015 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.flowobjective.impl.composition;
+
+import org.onosproject.net.flowobjective.ForwardingObjective;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Provides an update table for Forward.
+ */
+public class ForwardUpdateTable {
+    public List<ForwardingObjective> addObjectives;
+    public List<ForwardingObjective> removeObjectives;
+
+    public ForwardUpdateTable() {
+        this.addObjectives = new ArrayList<>();
+        this.removeObjectives = new ArrayList<>();
+    }
+
+    public void addUpdateTable(ForwardUpdateTable updateTable) {
+        this.addObjectives.addAll(updateTable.addObjectives);
+        this.removeObjectives.addAll(updateTable.removeObjectives);
+    }
+
+    public List<ForwardingObjective> toForwardingObjectiveList() {
+        List<ForwardingObjective> forwardingObjectives = new ArrayList<>();
+        forwardingObjectives.addAll(this.addObjectives);
+        forwardingObjectives.addAll(this.removeObjectives);
+        return forwardingObjectives;
+    }
+}
diff --git a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/NextTable.java b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/NextTable.java
new file mode 100644
index 0000000..e2787ed
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/NextTable.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2015 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.flowobjective.impl.composition;
+
+import org.onosproject.net.flowobjective.NextObjective;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Provides a table to store Next.
+ */
+public class NextTable {
+
+    protected Map<Integer, NextObjective> nextMap;
+
+    public NextTable() {
+        this.nextMap = new HashMap<>();
+    }
+
+    public List<NextObjective> updateNext(NextObjective nextObjective) {
+        List<NextObjective> updates = new ArrayList<>();
+        switch (nextObjective.op()) {
+            case ADD:
+                this.nextMap.put(nextObjective.id(), nextObjective);
+                updates.add(nextObjective);
+                break;
+            case REMOVE:
+                this.nextMap.remove(nextObjective.id());
+                updates.add(nextObjective);
+                break;
+            default:
+                break;
+        }
+        return updates;
+    }
+
+    public List<NextObjective> updateNext(List<NextObjective> nextObjectives) {
+        List<NextObjective> updates = new ArrayList<>();
+        for (NextObjective nextObjective : nextObjectives) {
+            updates.addAll(this.updateNext(nextObjective));
+        }
+        return updates;
+    }
+
+}
diff --git a/core/net/src/test/java/org/onosproject/net/flowobjective/impl/FlowObjectiveCompositionTreeTest.java b/core/net/src/test/java/org/onosproject/net/flowobjective/impl/FlowObjectiveCompositionTreeTest.java
new file mode 100644
index 0000000..1e898d3
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/net/flowobjective/impl/FlowObjectiveCompositionTreeTest.java
@@ -0,0 +1,603 @@
+/*
+ * Copyright 2014-2015 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.flowobjective.impl;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Test FlowObjectiveCompositionTree.
+ */
+@Ignore
+public class FlowObjectiveCompositionTreeTest {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    @Before
+    public void setUp() {}
+
+    @After
+    public void tearDown() {}
+
+    /*@Test
+    public void testParallelComposition() {
+        FlowObjectiveCompositionTree policyTree = FlowObjectiveCompositionUtil.parsePolicyString("31+32");
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchIPSrc(IpPrefix.valueOf("1.0.0.0/24"))
+                    .build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(31, "a"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(1)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .add();
+
+            helper(policyTree, forwardingObjective);
+        }
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder().build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(31, "a"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(0)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .add();
+
+            helper(policyTree, forwardingObjective);
+        }
+
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchIPDst(IpPrefix.valueOf("2.0.0.1/32"))
+                    .build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setOutput(PortNumber.portNumber(1))
+                    .build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(32, "b"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(1)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .add();
+
+            helper(policyTree, forwardingObjective);
+        }
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchIPDst(IpPrefix.valueOf("2.0.0.2/32"))
+                    .build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setOutput(PortNumber.portNumber(2))
+                    .build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(32, "b"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(1)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .add();
+            helper(policyTree, forwardingObjective);
+        }
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder().build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(32, "b"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(0)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .add();
+
+            helper(policyTree, forwardingObjective);
+        }
+
+        System.out.println("---------- Parallel ----------");
+        for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) {
+            System.out.println(forwardingObjectiveToString(fo));
+        }
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchIPDst(IpPrefix.valueOf("2.0.0.3/32"))
+                    .build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setOutput(PortNumber.portNumber(3))
+                    .build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(32, "b"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(1)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .add();
+            helper(policyTree, forwardingObjective);
+        }
+
+        System.out.println("---------- Parallel ----------");
+        for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) {
+            System.out.println(forwardingObjectiveToString(fo));
+        }
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchIPDst(IpPrefix.valueOf("2.0.0.3/32"))
+                    .build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setOutput(PortNumber.portNumber(3))
+                    .build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(32, "b"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(1)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .remove();
+            helper(policyTree, forwardingObjective);
+        }
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchIPSrc(IpPrefix.valueOf("1.0.0.0/24"))
+                    .build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(31, "a"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(1)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .remove();
+
+            helper(policyTree, forwardingObjective);
+        }
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder().build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(31, "a"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(0)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .remove();
+
+            helper(policyTree, forwardingObjective);
+        }
+
+        System.out.println("---------- Parallel ----------");
+        for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) {
+            System.out.println(forwardingObjectiveToString(fo));
+        }
+    }
+
+    @Test
+    public void testSequentialComposition() {
+        FlowObjectiveCompositionTree policyTree = FlowObjectiveCompositionUtil.parsePolicyString("31>32");
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchIPSrc(IpPrefix.valueOf("0.0.0.0/2"))
+                    .matchIPDst(IpPrefix.valueOf("3.0.0.0/32"))
+                    .build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setIpDst(IpAddress.valueOf("2.0.0.1"))
+                    .build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(31, "a"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(3)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .add();
+
+            helper(policyTree, forwardingObjective);
+        }
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchIPDst(IpPrefix.valueOf("3.0.0.0/32"))
+                    .build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setIpDst(IpAddress.valueOf("2.0.0.2"))
+                    .build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(31, "a"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(1)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .add();
+
+            helper(policyTree, forwardingObjective);
+        }
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder().build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(31, "a"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(0)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .add();
+
+            helper(policyTree, forwardingObjective);
+        }
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchIPDst(IpPrefix.valueOf("2.0.0.1/32"))
+                    .build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setOutput(PortNumber.portNumber(1))
+                    .build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(32, "b"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(1)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .add();
+
+            helper(policyTree, forwardingObjective);
+        }
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchIPDst(IpPrefix.valueOf("2.0.0.2/32"))
+                    .build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setOutput(PortNumber.portNumber(2))
+                    .build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(32, "b"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(1)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .add();
+            helper(policyTree, forwardingObjective);
+        }
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder().build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(32, "b"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(0)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .add();
+
+            helper(policyTree, forwardingObjective);
+        }
+
+        System.out.println("---------- Sequential ----------");
+        for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) {
+            System.out.println(forwardingObjectiveToString(fo));
+        }
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchIPDst(IpPrefix.valueOf("2.0.0.3/32"))
+                    .build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setOutput(PortNumber.portNumber(3))
+                    .build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(32, "b"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(1)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .add();
+
+            helper(policyTree, forwardingObjective);
+        }
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchIPSrc(IpPrefix.valueOf("0.0.0.0/1"))
+                    .matchIPDst(IpPrefix.valueOf("3.0.0.0/32"))
+                    .build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setIpDst(IpAddress.valueOf("2.0.0.3"))
+                    .build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(31, "a"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(3)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .add();
+
+            helper(policyTree, forwardingObjective);
+        }
+
+        System.out.println("---------- Sequential ----------");
+        for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) {
+            System.out.println(forwardingObjectiveToString(fo));
+        }
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchIPDst(IpPrefix.valueOf("2.0.0.3/32"))
+                    .build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setOutput(PortNumber.portNumber(3))
+                    .build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(32, "b"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(1)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .remove();
+
+            helper(policyTree, forwardingObjective);
+        }
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchIPSrc(IpPrefix.valueOf("0.0.0.0/1"))
+                    .matchIPDst(IpPrefix.valueOf("3.0.0.0/32"))
+                    .build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setIpDst(IpAddress.valueOf("2.0.0.3"))
+                    .build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(31, "a"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(3)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .remove();
+
+            helper(policyTree, forwardingObjective);
+        }
+
+        System.out.println("---------- Sequential ----------");
+        for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) {
+            System.out.println(forwardingObjectiveToString(fo));
+        }
+    }
+
+    @Test
+    public void testOverrideComposition() {
+        FlowObjectiveCompositionTree policyTree = FlowObjectiveCompositionUtil.parsePolicyString("31/32");
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchIPSrc(IpPrefix.valueOf("1.0.0.0/32"))
+                    .matchIPDst(IpPrefix.valueOf("2.0.0.1/32"))
+                    .build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setOutput(PortNumber.portNumber(3))
+                    .build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(31, "a"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(1)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .add();
+
+            helper(policyTree, forwardingObjective);
+        }
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchIPDst(IpPrefix.valueOf("2.0.0.1/32"))
+                    .build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setOutput(PortNumber.portNumber(1))
+                    .build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(32, "b"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(1)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .add();
+
+            helper(policyTree, forwardingObjective);
+        }
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchIPDst(IpPrefix.valueOf("2.0.0.2/32"))
+                    .build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setOutput(PortNumber.portNumber(2))
+                    .build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(32, "b"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(1)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .add();
+            helper(policyTree, forwardingObjective);
+        }
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder().build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(32, "b"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(0)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .add();
+
+            helper(policyTree, forwardingObjective);
+        }
+
+        System.out.println("---------- Override ----------");
+        for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) {
+            System.out.println(forwardingObjectiveToString(fo));
+        }
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchIPDst(IpPrefix.valueOf("2.0.0.3/32"))
+                    .build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setOutput(PortNumber.portNumber(3))
+                    .build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(32, "b"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(1)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .add();
+            helper(policyTree, forwardingObjective);
+        }
+
+        System.out.println("---------- Override ----------");
+        for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) {
+            System.out.println(forwardingObjectiveToString(fo));
+        }
+
+        {
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchIPDst(IpPrefix.valueOf("2.0.0.3/32"))
+                    .build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setOutput(PortNumber.portNumber(3))
+                    .build();
+
+            ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+                    .fromApp(new DefaultApplicationId(32, "b"))
+                    .makePermanent()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(1)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .remove();
+            helper(policyTree, forwardingObjective);
+        }
+
+        System.out.println("---------- Override ----------");
+        for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) {
+            System.out.println(forwardingObjectiveToString(fo));
+        }
+    }
+
+    private void helper(FlowObjectiveCompositionTree policyTree, ForwardingObjective forwardingObjective) {
+        log.info("before composition");
+        log.info("\t{}", forwardingObjectiveToString(forwardingObjective));
+        List<ForwardingObjective> forwardingObjectives
+                = policyTree.updateForward(forwardingObjective);
+        log.info("after composition");
+        for (ForwardingObjective fo : forwardingObjectives) {
+            log.info("\t{}", forwardingObjectiveToString(fo));
+        }
+    }*/
+}