Merge pull request #445 from jonohart/master

Various improvements and bug fixes
diff --git a/src/main/java/net/floodlightcontroller/util/MACAddress.java b/src/main/java/net/floodlightcontroller/util/MACAddress.java
index 88dbda2..b77d4cc 100644
--- a/src/main/java/net/floodlightcontroller/util/MACAddress.java
+++ b/src/main/java/net/floodlightcontroller/util/MACAddress.java
@@ -19,6 +19,18 @@
     public static final int MAC_ADDRESS_LENGTH = 6;
     private byte[] address = new byte[MAC_ADDRESS_LENGTH];
 
+    /**
+     * Default constructor.
+     */
+    public MACAddress() {
+	this.address = new byte[] { 0, 0, 0, 0, 0, 0};
+    }
+
+    /**
+     * Constructor for a given address stored in a byte array.
+     *
+     * @param address the address stored in a byte array.
+     */
     public MACAddress(byte[] address) {
         this.address = Arrays.copyOf(address, MAC_ADDRESS_LENGTH);
     }
diff --git a/src/main/java/net/onrc/onos/datagrid/HazelcastDatagrid.java b/src/main/java/net/onrc/onos/datagrid/HazelcastDatagrid.java
index 7de2869..41b4957 100644
--- a/src/main/java/net/onrc/onos/datagrid/HazelcastDatagrid.java
+++ b/src/main/java/net/onrc/onos/datagrid/HazelcastDatagrid.java
@@ -4,7 +4,13 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.Map;
+import java.util.Set;
+
+import com.esotericsoftware.kryo2.Kryo;
+import com.esotericsoftware.kryo2.io.Input;
+import com.esotericsoftware.kryo2.io.Output;
 
 import net.floodlightcontroller.core.IFloodlightProviderService;
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
@@ -12,13 +18,24 @@
 import net.floodlightcontroller.core.module.IFloodlightModule;
 import net.floodlightcontroller.core.module.IFloodlightService;
 
+import net.onrc.onos.ofcontroller.flowmanager.IFlowEventHandlerService;
+import net.onrc.onos.ofcontroller.topology.TopologyElement;
+import net.onrc.onos.ofcontroller.util.FlowEntry;
+import net.onrc.onos.ofcontroller.util.FlowEntryId;
+import net.onrc.onos.ofcontroller.util.FlowId;
+import net.onrc.onos.ofcontroller.util.FlowPath;
+import net.onrc.onos.ofcontroller.util.serializers.KryoFactory;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.hazelcast.config.Config;
 import com.hazelcast.config.FileSystemXmlConfig;
+import com.hazelcast.core.EntryEvent;
+import com.hazelcast.core.EntryListener;
 import com.hazelcast.core.Hazelcast;
 import com.hazelcast.core.HazelcastInstance;
+import com.hazelcast.core.IMap;
 import com.hazelcast.instance.GroupProperties;
 
 /**
@@ -27,13 +44,264 @@
  * appropriate in a multi-node cluster.
  */
 public class HazelcastDatagrid implements IFloodlightModule, IDatagridService {
+    private final static int MAX_BUFFER_SIZE = 64*1024;
+
     protected final static Logger log = LoggerFactory.getLogger(HazelcastDatagrid.class);
     protected IFloodlightProviderService floodlightProvider;
 
     protected static final String HazelcastConfigFile = "datagridConfig";
-    private HazelcastInstance hazelcast = null;
+    private HazelcastInstance hazelcastInstance = null;
     private Config hazelcastConfig = null;
 
+    private KryoFactory kryoFactory = new KryoFactory();
+    private IFlowEventHandlerService flowEventHandlerService = null;
+
+    // State related to the Flow map
+    protected static final String mapFlowName = "mapFlow";
+    private IMap<Long, byte[]> mapFlow = null;
+    private MapFlowListener mapFlowListener = null;
+    private String mapFlowListenerId = null;
+
+    // State related to the Flow Entry map
+    protected static final String mapFlowEntryName = "mapFlowEntry";
+    private IMap<Long, byte[]> mapFlowEntry = null;
+    private MapFlowEntryListener mapFlowEntryListener = null;
+    private String mapFlowEntryListenerId = null;
+
+    // State related to the Network Topology map
+    protected static final String mapTopologyName = "mapTopology";
+    private IMap<String, byte[]> mapTopology = null;
+    private MapTopologyListener mapTopologyListener = null;
+    private String mapTopologyListenerId = null;
+
+    /**
+     * Class for receiving notifications for Flow state.
+     *
+     * The datagrid map is:
+     *  - Key : Flow ID (Long)
+     *  - Value : Serialized Flow (byte[])
+     */
+    class MapFlowListener implements EntryListener<Long, byte[]> {
+	/**
+	 * Receive a notification that an entry is added.
+	 *
+	 * @param event the notification event for the entry.
+	 */
+	public void entryAdded(EntryEvent event) {
+	    Long keyLong = (Long)event.getKey();
+	    byte[] valueBytes = (byte[])event.getValue();
+
+	    //
+	    // Decode the value and deliver the notification
+	    //
+	    Kryo kryo = kryoFactory.newKryo();
+	    Input input = new Input(valueBytes);
+	    FlowPath flowPath = kryo.readObject(input, FlowPath.class);
+	    kryoFactory.deleteKryo(kryo);
+	    flowEventHandlerService.notificationRecvFlowAdded(flowPath);
+	}
+
+	/**
+	 * Receive a notification that an entry is removed.
+	 *
+	 * @param event the notification event for the entry.
+	 */
+	public void entryRemoved(EntryEvent event) {
+	    Long keyLong = (Long)event.getKey();
+	    byte[] valueBytes = (byte[])event.getValue();
+
+	    //
+	    // Decode the value and deliver the notification
+	    //
+	    Kryo kryo = kryoFactory.newKryo();
+	    Input input = new Input(valueBytes);
+	    FlowPath flowPath = kryo.readObject(input, FlowPath.class);
+	    kryoFactory.deleteKryo(kryo);
+	    flowEventHandlerService.notificationRecvFlowRemoved(flowPath);
+	}
+
+	/**
+	 * Receive a notification that an entry is updated.
+	 *
+	 * @param event the notification event for the entry.
+	 */
+	public void entryUpdated(EntryEvent event) {
+	    Long keyLong = (Long)event.getKey();
+	    byte[] valueBytes = (byte[])event.getValue();
+
+	    //
+	    // Decode the value and deliver the notification
+	    //
+	    Kryo kryo = kryoFactory.newKryo();
+	    Input input = new Input(valueBytes);
+	    FlowPath flowPath = kryo.readObject(input, FlowPath.class);
+	    kryoFactory.deleteKryo(kryo);
+	    flowEventHandlerService.notificationRecvFlowUpdated(flowPath);
+	}
+
+	/**
+	 * Receive a notification that an entry is evicted.
+	 *
+	 * @param event the notification event for the entry.
+	 */
+	public void entryEvicted(EntryEvent event) {
+	    // NOTE: We don't use eviction for this map
+	}
+    }
+
+    /**
+     * Class for receiving notifications for FlowEntry state.
+     *
+     * The datagrid map is:
+     *  - Key : FlowEntry ID (Long)
+     *  - Value : Serialized FlowEntry (byte[])
+     */
+    class MapFlowEntryListener implements EntryListener<Long, byte[]> {
+	/**
+	 * Receive a notification that an entry is added.
+	 *
+	 * @param event the notification event for the entry.
+	 */
+	public void entryAdded(EntryEvent event) {
+	    Long keyLong = (Long)event.getKey();
+	    byte[] valueBytes = (byte[])event.getValue();
+
+	    //
+	    // Decode the value and deliver the notification
+	    //
+	    Kryo kryo = kryoFactory.newKryo();
+	    Input input = new Input(valueBytes);
+	    FlowEntry flowEntry = kryo.readObject(input, FlowEntry.class);
+	    kryoFactory.deleteKryo(kryo);
+	    flowEventHandlerService.notificationRecvFlowEntryAdded(flowEntry);
+	}
+
+	/**
+	 * Receive a notification that an entry is removed.
+	 *
+	 * @param event the notification event for the entry.
+	 */
+	public void entryRemoved(EntryEvent event) {
+	    Long keyLong = (Long)event.getKey();
+	    byte[] valueBytes = (byte[])event.getValue();
+
+	    //
+	    // Decode the value and deliver the notification
+	    //
+	    Kryo kryo = kryoFactory.newKryo();
+	    Input input = new Input(valueBytes);
+	    FlowEntry flowEntry = kryo.readObject(input, FlowEntry.class);
+	    kryoFactory.deleteKryo(kryo);
+	    flowEventHandlerService.notificationRecvFlowEntryRemoved(flowEntry);
+	}
+
+	/**
+	 * Receive a notification that an entry is updated.
+	 *
+	 * @param event the notification event for the entry.
+	 */
+	public void entryUpdated(EntryEvent event) {
+	    Long keyLong = (Long)event.getKey();
+	    byte[] valueBytes = (byte[])event.getValue();
+
+	    //
+	    // Decode the value and deliver the notification
+	    //
+	    Kryo kryo = kryoFactory.newKryo();
+	    Input input = new Input(valueBytes);
+	    FlowEntry flowEntry = kryo.readObject(input, FlowEntry.class);
+	    kryoFactory.deleteKryo(kryo);
+	    flowEventHandlerService.notificationRecvFlowEntryUpdated(flowEntry);
+	}
+
+	/**
+	 * Receive a notification that an entry is evicted.
+	 *
+	 * @param event the notification event for the entry.
+	 */
+	public void entryEvicted(EntryEvent event) {
+	    // NOTE: We don't use eviction for this map
+	}
+    }
+
+    /**
+     * Class for receiving notifications for Network Topology state.
+     *
+     * The datagrid map is:
+     *  - Key: TopologyElement ID (String)
+     *  - Value: Serialized TopologyElement (byte[])
+     */
+    class MapTopologyListener implements EntryListener<String, byte[]> {
+	/**
+	 * Receive a notification that an entry is added.
+	 *
+	 * @param event the notification event for the entry.
+	 */
+	public void entryAdded(EntryEvent event) {
+	    String keyString = (String)event.getKey();
+	    byte[] valueBytes = (byte[])event.getValue();
+
+	    //
+	    // Decode the value and deliver the notification
+	    //
+	    Kryo kryo = kryoFactory.newKryo();
+	    Input input = new Input(valueBytes);
+	    TopologyElement topologyElement =
+		kryo.readObject(input, TopologyElement.class);
+	    kryoFactory.deleteKryo(kryo);
+	    flowEventHandlerService.notificationRecvTopologyElementAdded(topologyElement);
+	}
+
+	/**
+	 * Receive a notification that an entry is removed.
+	 *
+	 * @param event the notification event for the entry.
+	 */
+	public void entryRemoved(EntryEvent event) {
+	    String keyString = (String)event.getKey();
+	    byte[] valueBytes = (byte[])event.getValue();
+
+	    //
+	    // Decode the value and deliver the notification
+	    //
+	    Kryo kryo = kryoFactory.newKryo();
+	    Input input = new Input(valueBytes);
+	    TopologyElement topologyElement =
+		kryo.readObject(input, TopologyElement.class);
+	    kryoFactory.deleteKryo(kryo);
+	    flowEventHandlerService.notificationRecvTopologyElementRemoved(topologyElement);
+	}
+
+	/**
+	 * Receive a notification that an entry is updated.
+	 *
+	 * @param event the notification event for the entry.
+	 */
+	public void entryUpdated(EntryEvent event) {
+	    String keyString = (String)event.getKey();
+	    byte[] valueBytes = (byte[])event.getValue();
+
+	    //
+	    // Decode the value and deliver the notification
+	    //
+	    Kryo kryo = kryoFactory.newKryo();
+	    Input input = new Input(valueBytes);
+	    TopologyElement topologyElement =
+		kryo.readObject(input, TopologyElement.class);
+	    kryoFactory.deleteKryo(kryo);
+	    flowEventHandlerService.notificationRecvTopologyElementUpdated(topologyElement);
+	}
+
+	/**
+	 * Receive a notification that an entry is evicted.
+	 *
+	 * @param event the notification event for the entry.
+	 */
+	public void entryEvicted(EntryEvent event) {
+	    // NOTE: We don't use eviction for this map
+	}
+    }
+
     /**
      * Initialize the Hazelcast Datagrid operation.
      *
@@ -143,6 +411,350 @@
      */
     @Override
     public void startUp(FloodlightModuleContext context) {
-	hazelcast = Hazelcast.newHazelcastInstance(hazelcastConfig);
+	hazelcastInstance = Hazelcast.newHazelcastInstance(hazelcastConfig);
+    }
+
+    /**
+     * Register Flow Event Handler Service for receiving Flow-related
+     * notifications.
+     *
+     * NOTE: Only a single Flow Event Handler Service can be registered.
+     *
+     * @param flowEventHandlerService the Flow Event Handler Service to register.
+     */
+    @Override
+    public void registerFlowEventHandlerService(IFlowEventHandlerService flowEventHandlerService) {
+	this.flowEventHandlerService = flowEventHandlerService;
+
+	// Initialize the Flow-related map state
+	mapFlowListener = new MapFlowListener();
+	mapFlow = hazelcastInstance.getMap(mapFlowName);
+	mapFlowListenerId = mapFlow.addEntryListener(mapFlowListener, true);
+
+	// Initialize the FlowEntry-related map state
+	mapFlowEntryListener = new MapFlowEntryListener();
+	mapFlowEntry = hazelcastInstance.getMap(mapFlowEntryName);
+	mapFlowEntryListenerId = mapFlowEntry.addEntryListener(mapFlowEntryListener, true);
+
+	// Initialize the Topology-related map state
+	mapTopologyListener = new MapTopologyListener();
+	mapTopology = hazelcastInstance.getMap(mapTopologyName);
+	mapTopologyListenerId = mapTopology.addEntryListener(mapTopologyListener, true);
+    }
+
+    /**
+     * De-register Flow Event Handler Service for receiving Flow-related
+     * notifications.
+     *
+     * NOTE: Only a single Flow Event Handler Service can be registered.
+     *
+     * @param flowEventHandlerService the Flow Event Handler Service to
+     * de-register.
+     */
+    @Override
+    public void deregisterFlowEventHandlerService(IFlowEventHandlerService flowEventHandlerService) {
+	// Clear the Flow-related map state
+	mapFlow.removeEntryListener(mapFlowListenerId);
+	mapFlow = null;
+	mapFlowListener = null;
+
+	// Clear the FlowEntry-related map state
+	mapFlowEntry.removeEntryListener(mapFlowEntryListenerId);
+	mapFlowEntry = null;
+	mapFlowEntryListener = null;
+
+	// Clear the Topology-related map state
+	mapTopology.removeEntryListener(mapTopologyListenerId);
+	mapTopology = null;
+	mapTopologyListener = null;
+
+	this.flowEventHandlerService = null;
+    }
+
+    /**
+     * Get all Flows that are currently in the datagrid.
+     *
+     * @return all Flows that are currently in the datagrid.
+     */
+    @Override
+    public Collection<FlowPath> getAllFlows() {
+	Collection<FlowPath> allFlows = new LinkedList<FlowPath>();
+
+	//
+	// Get all current entries
+	//
+	Collection<byte[]> values = mapFlow.values();
+	Kryo kryo = kryoFactory.newKryo();
+	for (byte[] valueBytes : values) {
+	    //
+	    // Decode the value
+	    //
+	    Input input = new Input(valueBytes);
+	    FlowPath flowPath = kryo.readObject(input, FlowPath.class);
+	    allFlows.add(flowPath);
+	}
+	kryoFactory.deleteKryo(kryo);
+
+	return allFlows;
+    }
+
+    /**
+     * Send a notification that a Flow is added.
+     *
+     * @param flowPath the Flow that is added.
+     */
+    @Override
+    public void notificationSendFlowAdded(FlowPath flowPath) {
+	//
+	// Encode the value
+	//
+	byte[] buffer = new byte[MAX_BUFFER_SIZE];
+	Kryo kryo = kryoFactory.newKryo();
+	Output output = new Output(buffer, -1);
+	kryo.writeObject(output, flowPath);
+	byte[] valueBytes = output.toBytes();
+	kryoFactory.deleteKryo(kryo);
+
+	//
+	// Put the entry:
+	//  - Key : Flow ID (Long)
+	//  - Value : Serialized Flow (byte[])
+	//
+	mapFlow.putAsync(flowPath.flowId().value(), valueBytes);
+    }
+
+    /**
+     * Send a notification that a Flow is removed.
+     *
+     * @param flowId the Flow ID of the Flow that is removed.
+     */
+    @Override
+    public void notificationSendFlowRemoved(FlowId flowId) {
+	//
+	// Remove the entry:
+	//  - Key : Flow ID (Long)
+	//  - Value : Serialized Flow (byte[])
+	//
+	mapFlow.removeAsync(flowId.value());
+    }
+
+    /**
+     * Send a notification that a Flow is updated.
+     *
+     * @param flowPath the Flow that is updated.
+     */
+    @Override
+    public void notificationSendFlowUpdated(FlowPath flowPath) {
+	// NOTE: Adding an entry with an existing key automatically updates it
+	notificationSendFlowAdded(flowPath);
+    }
+
+    /**
+     * Send a notification that all Flows are removed.
+     */
+    @Override
+    public void notificationSendAllFlowsRemoved() {
+	//
+	// Remove all entries
+	// NOTE: We remove the entries one-by-one so the per-entry
+	// notifications will be delivered.
+	//
+	// mapFlow.clear();
+	Set<Long> keySet = mapFlow.keySet();
+	for (Long key : keySet) {
+	    mapFlow.removeAsync(key);
+	}
+    }
+
+    /**
+     * Get all Flow Entries that are currently in the datagrid.
+     *
+     * @return all Flow Entries that are currently in the datagrid.
+     */
+    @Override
+    public Collection<FlowEntry> getAllFlowEntries() {
+	Collection<FlowEntry> allFlowEntries = new LinkedList<FlowEntry>();
+
+	//
+	// Get all current entries
+	//
+	Collection<byte[]> values = mapFlowEntry.values();
+	Kryo kryo = kryoFactory.newKryo();
+	for (byte[] valueBytes : values) {
+	    //
+	    // Decode the value
+	    //
+	    Input input = new Input(valueBytes);
+	    FlowEntry flowEntry = kryo.readObject(input, FlowEntry.class);
+	    allFlowEntries.add(flowEntry);
+	}
+	kryoFactory.deleteKryo(kryo);
+
+	return allFlowEntries;
+    }
+
+    /**
+     * Send a notification that a FlowEntry is added.
+     *
+     * @param flowEntry the FlowEntry that is added.
+     */
+    @Override
+    public void notificationSendFlowEntryAdded(FlowEntry flowEntry) {
+	//
+	// Encode the value
+	//
+	byte[] buffer = new byte[MAX_BUFFER_SIZE];
+	Kryo kryo = kryoFactory.newKryo();
+	Output output = new Output(buffer, -1);
+	kryo.writeObject(output, flowEntry);
+	byte[] valueBytes = output.toBytes();
+	kryoFactory.deleteKryo(kryo);
+
+	//
+	// Put the entry:
+	//  - Key : FlowEntry ID (Long)
+	//  - Value : Serialized FlowEntry (byte[])
+	//
+	mapFlowEntry.putAsync(flowEntry.flowEntryId().value(), valueBytes);
+    }
+
+    /**
+     * Send a notification that a FlowEntry is removed.
+     *
+     * @param flowEntryId the FlowEntry ID of the FlowEntry that is removed.
+     */
+    @Override
+    public void notificationSendFlowEntryRemoved(FlowEntryId flowEntryId) {
+	//
+	// Remove the entry:
+	//  - Key : FlowEntry ID (Long)
+	//  - Value : Serialized FlowEntry (byte[])
+	//
+	mapFlowEntry.removeAsync(flowEntryId.value());
+    }
+
+    /**
+     * Send a notification that a FlowEntry is updated.
+     *
+     * @param flowEntry the FlowEntry that is updated.
+     */
+    @Override
+    public void notificationSendFlowEntryUpdated(FlowEntry flowEntry) {
+	// NOTE: Adding an entry with an existing key automatically updates it
+	notificationSendFlowEntryAdded(flowEntry);
+    }
+
+    /**
+     * Send a notification that all Flow Entries are removed.
+     */
+    @Override
+    public void notificationSendAllFlowEntriesRemoved() {
+	//
+	// Remove all entries
+	// NOTE: We remove the entries one-by-one so the per-entry
+	// notifications will be delivered.
+	//
+	// mapFlowEntry.clear();
+	Set<Long> keySet = mapFlowEntry.keySet();
+	for (Long key : keySet) {
+	    mapFlowEntry.removeAsync(key);
+	}
+    }
+
+    /**
+     * Get all Topology Elements that are currently in the datagrid.
+     *
+     * @return all Topology Elements that are currently in the datagrid.
+     */
+    @Override
+    public Collection<TopologyElement> getAllTopologyElements() {
+	Collection<TopologyElement> allTopologyElements =
+	    new LinkedList<TopologyElement>();
+
+	//
+	// Get all current entries
+	//
+	Collection<byte[]> values = mapTopology.values();
+	Kryo kryo = kryoFactory.newKryo();
+	for (byte[] valueBytes : values) {
+	    //
+	    // Decode the value
+	    //
+	    Input input = new Input(valueBytes);
+	    TopologyElement topologyElement =
+		kryo.readObject(input, TopologyElement.class);
+	    allTopologyElements.add(topologyElement);
+	}
+	kryoFactory.deleteKryo(kryo);
+
+	return allTopologyElements;
+    }
+
+    /**
+     * Send a notification that a Topology Element is added.
+     *
+     * @param topologyElement the Topology Element that is added.
+     */
+    @Override
+    public void notificationSendTopologyElementAdded(TopologyElement topologyElement) {
+	//
+	// Encode the value
+	//
+	byte[] buffer = new byte[MAX_BUFFER_SIZE];
+	Kryo kryo = kryoFactory.newKryo();
+	Output output = new Output(buffer, -1);
+	kryo.writeObject(output, topologyElement);
+	byte[] valueBytes = output.toBytes();
+	kryoFactory.deleteKryo(kryo);
+
+	//
+	// Put the entry:
+	//  - Key : TopologyElement ID (String)
+	//  - Value : Serialized TopologyElement (byte[])
+	//
+	mapTopology.putAsync(topologyElement.elementId(), valueBytes);
+    }
+
+    /**
+     * Send a notification that a Topology Element is removed.
+     *
+     * @param topologyElement the Topology Element that is removed.
+     */
+    @Override
+    public void notificationSendTopologyElementRemoved(TopologyElement topologyElement) {
+	//
+	// Remove the entry:
+	//  - Key : TopologyElement ID (String)
+	//  - Value : Serialized TopologyElement (byte[])
+	//
+	mapTopology.removeAsync(topologyElement.elementId());
+    }
+
+    /**
+     * Send a notification that a Topology Element is updated.
+     *
+     * @param topologyElement the Topology Element that is updated.
+     */
+    @Override
+    public void notificationSendTopologyElementUpdated(TopologyElement topologyElement) {
+	// NOTE: Adding an entry with an existing key automatically updates it
+	notificationSendTopologyElementAdded(topologyElement);
+    }
+
+    /**
+     * Send a notification that all Topology Elements are removed.
+     */
+    @Override
+    public void notificationSendAllTopologyElementsRemoved() {
+	//
+	// Remove all entries
+	// NOTE: We remove the entries one-by-one so the per-entry
+	// notifications will be delivered.
+	//
+	// mapTopology.clear();
+	Set<String> keySet = mapTopology.keySet();
+	for (String key : keySet) {
+	    mapTopology.removeAsync(key);
+	}
     }
 }
diff --git a/src/main/java/net/onrc/onos/datagrid/IDatagridService.java b/src/main/java/net/onrc/onos/datagrid/IDatagridService.java
index 1c7f3ab..1bcf601 100644
--- a/src/main/java/net/onrc/onos/datagrid/IDatagridService.java
+++ b/src/main/java/net/onrc/onos/datagrid/IDatagridService.java
@@ -1,10 +1,137 @@
 package net.onrc.onos.datagrid;
 
+import java.util.Collection;
+
 import net.floodlightcontroller.core.module.IFloodlightService;
 
+import net.onrc.onos.ofcontroller.flowmanager.IFlowEventHandlerService;
+import net.onrc.onos.ofcontroller.topology.TopologyElement;
+import net.onrc.onos.ofcontroller.util.FlowEntry;
+import net.onrc.onos.ofcontroller.util.FlowEntryId;
+import net.onrc.onos.ofcontroller.util.FlowId;
+import net.onrc.onos.ofcontroller.util.FlowPath;
+
 /**
  * Interface for providing Datagrid Service to other modules.
  */
 public interface IDatagridService extends IFloodlightService {
-    // TODO
+    /**
+     * Register Flow Event Handler Service for receiving Flow-related
+     * notifications.
+     *
+     * NOTE: Only a single Flow Event Handler Service can be registered.
+     *
+     * @param flowEventHandlerService the Flow Event Handler Service to register.
+     */
+    void registerFlowEventHandlerService(IFlowEventHandlerService flowEventHandlerService);
+
+    /**
+     * De-register Flow Event Handler Service for receiving Flow-related
+     * notifications.
+     *
+     * NOTE: Only a single Flow Event Handler Service can be registered.
+     *
+     * @param flowEventHandlerService the Flow Event Handler Service to
+     * de-register.
+     */
+    void deregisterFlowEventHandlerService(IFlowEventHandlerService flowEventHandlerService);
+
+    /**
+     * Get all Flows that are currently in the datagrid.
+     *
+     * @return all Flows that are currently in the datagrid.
+     */
+    Collection<FlowPath> getAllFlows();
+
+    /**
+     * Send a notification that a Flow is added.
+     *
+     * @param flowPath the Flow that is added.
+     */
+    void notificationSendFlowAdded(FlowPath flowPath);
+
+    /**
+     * Send a notification that a Flow is removed.
+     *
+     * @param flowId the Flow ID of the Flow that is removed.
+     */
+    void notificationSendFlowRemoved(FlowId flowId);
+
+    /**
+     * Send a notification that a Flow is updated.
+     *
+     * @param flowPath the Flow that is updated.
+     */
+    void notificationSendFlowUpdated(FlowPath flowPath);
+
+    /**
+     * Send a notification that all Flows are removed.
+     */
+    void notificationSendAllFlowsRemoved();
+
+    /**
+     * Get all Flow Entries that are currently in the datagrid.
+     *
+     * @return all Flow Entries that are currently in the datagrid.
+     */
+    Collection<FlowEntry> getAllFlowEntries();
+
+    /**
+     * Send a notification that a FlowEntry is added.
+     *
+     * @param flowEntry the FlowEntry that is added.
+     */
+    void notificationSendFlowEntryAdded(FlowEntry flowEntry);
+
+    /**
+     * Send a notification that a FlowEntry is removed.
+     *
+     * @param flowEntryId the FlowEntry ID of the FlowEntry that is removed.
+     */
+    void notificationSendFlowEntryRemoved(FlowEntryId flowEntryId);
+
+    /**
+     * Send a notification that a FlowEntry is updated.
+     *
+     * @param flowEntry the FlowEntry that is updated.
+     */
+    void notificationSendFlowEntryUpdated(FlowEntry flowEntry);
+
+    /**
+     * Send a notification that all Flow Entries are removed.
+     */
+    void notificationSendAllFlowEntriesRemoved();
+
+    /**
+     * Get all Topology Elements that are currently in the datagrid.
+     *
+     * @return all Topology Elements that are currently in the datagrid.
+     */
+    Collection<TopologyElement> getAllTopologyElements();
+
+    /**
+     * Send a notification that a Topology Element is added.
+     *
+     * @param topologyElement the Topology Element that is added.
+     */
+    void notificationSendTopologyElementAdded(TopologyElement topologyElement);
+
+    /**
+     * Send a notification that a Topology Element is removed.
+     *
+     * @param topologyElement the Topology Element that is removed.
+     */
+    void notificationSendTopologyElementRemoved(TopologyElement topologyElement);
+
+    /**
+     * Send a notification that a Topology Element is updated.
+     *
+     * @param topologyElement the Topology Element that is updated.
+     */
+    void notificationSendTopologyElementUpdated(TopologyElement topologyElement);
+
+    /**
+     * Send a notification that all Topology Elements are removed.
+     */
+    void notificationSendAllTopologyElementsRemoved();
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjects.java b/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjects.java
index 7b38fef..6f13080 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjects.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjects.java
@@ -199,6 +199,20 @@
 		@Property("installer_id")
 		public void setInstallerId(String installerId);
 
+		@JsonProperty("flowPathType")
+		@Property("flow_path_type")
+		public String getFlowPathType();
+
+		@Property("flow_path_type")
+		public void setFlowPathType(String flowPathType);
+
+		@JsonProperty("flowPathUserState")
+		@Property("user_state")
+		public String getFlowPathUserState();
+
+		@Property("user_state")
+		public void setFlowPathUserState(String userState);
+
 		@JsonProperty("flowPathFlags")
 		@Property("flow_path_flags")
 		public Long getFlowPathFlags();
@@ -351,13 +365,6 @@
 		@JsonIgnore
 		@Property("state")
 		public String getState();
-
-		@JsonIgnore
-		@Property("user_state")
-		public String getUserState();
-
-		@Property("user_state")
-		public void setUserState(String userState);
 	}
 
 public interface IFlowEntry extends IBaseObject {
diff --git a/src/main/java/net/onrc/onos/ofcontroller/floodlightlistener/NetworkGraphPublisher.java b/src/main/java/net/onrc/onos/ofcontroller/floodlightlistener/NetworkGraphPublisher.java
index 9eefba3..14cffd8 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/floodlightlistener/NetworkGraphPublisher.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/floodlightlistener/NetworkGraphPublisher.java
@@ -25,6 +25,8 @@
 import net.floodlightcontroller.devicemanager.IDeviceService;
 import net.floodlightcontroller.routing.Link;
 import net.floodlightcontroller.threadpool.IThreadPoolService;
+
+import net.onrc.onos.datagrid.IDatagridService;
 import net.onrc.onos.graph.GraphDBConnection;
 import net.onrc.onos.graph.GraphDBOperation;
 import net.onrc.onos.graph.IDBConnection;
@@ -42,6 +44,7 @@
 import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscoveryListener;
 import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscoveryService;
 import net.onrc.onos.ofcontroller.linkdiscovery.LinkInfo;
+import net.onrc.onos.ofcontroller.topology.TopologyElement;
 import net.onrc.onos.registry.controller.IControllerRegistryService;
 import net.onrc.onos.registry.controller.IControllerRegistryService.ControlChangeCallback;
 import net.onrc.onos.registry.controller.RegistryException;
@@ -69,6 +72,8 @@
 	protected final int CLEANUP_TASK_INTERVAL = 60; // 1 min
 	protected SingletonTask cleanupTask;
 	protected ILinkDiscoveryService linkDiscovery;
+
+	protected IDatagridService datagridService;
 	
 	/**
      *  Cleanup and synch switch state from registry
@@ -98,6 +103,15 @@
 					    registryService.releaseControl(dpid);
 					    
 					    // TODO publish UPDATE_SWITCH event here
+					    //
+					    // NOTE: Here we explicitly send
+					    // notification to remove the
+					    // switch, because it is inactive
+					    //
+					    TopologyElement topologyElement =
+						new TopologyElement(dpid);
+					    datagridService.notificationSendTopologyElementRemoved(topologyElement);
+
 					}
 				} catch (Exception e) {
 	                log.error("Error in SwitchCleanup:controlChanged ", e);
@@ -136,7 +150,6 @@
 
 	@Override
 	public void linkDiscoveryUpdate(LDUpdate update) {
-		// TODO Auto-generated method stub
 		Link lt = new Link(update.getSrc(),update.getSrcPort(),update.getDst(),update.getDstPort());
 		//log.debug("{}:LinkDicoveryUpdate(): Updating Link {}",this.getClass(), lt);
 		
@@ -146,6 +159,12 @@
 				
 				if (linkStore.deleteLink(lt)) {
 				    // TODO publish DELETE_LINK event here
+				    TopologyElement topologyElement =
+					new TopologyElement(update.getSrc(),
+							    update.getSrcPort(),
+							    update.getDst(),
+							    update.getDstPort());
+				    datagridService.notificationSendTopologyElementRemoved(topologyElement);
 				}
 				break;
 			case LINK_UPDATED:
@@ -155,6 +174,16 @@
 				// TODO update "linfo" using portState derived using "update"
 				if (linkStore.update(lt, linfo, DM_OPERATION.UPDATE)) {
 				    // TODO publish UPDATE_LINK event here
+				    //
+				    // TODO NOTE: Here we assume that updated
+				    // link is UP.
+				    //
+				    TopologyElement topologyElement =
+					new TopologyElement(update.getSrc(),
+							    update.getSrcPort(),
+							    update.getDst(),
+							    update.getDstPort());
+				    datagridService.notificationSendTopologyElementUpdated(topologyElement);
 				}
 				break;
 			case LINK_ADDED:
@@ -162,6 +191,12 @@
 				
 				if (linkStore.addLink(lt)) {
 				    // TODO publish ADD_LINK event here
+				    TopologyElement topologyElement =
+					new TopologyElement(update.getSrc(),
+							    update.getSrcPort(),
+							    update.getDst(),
+							    update.getDstPort());
+				    datagridService.notificationSendTopologyElementAdded(topologyElement);
 				}
 				break;
 			default:
@@ -175,6 +210,13 @@
 		if (registryService.hasControl(sw.getId())) {
 			if (swStore.addSwitch(sw)) {
 			    // TODO publish ADD_SWITCH event here
+			    TopologyElement topologyElement =
+				new TopologyElement(sw.getId());
+			    // TODO: Add only ports that are UP?
+			    for (OFPhysicalPort port : sw.getPorts()) {
+				topologyElement.addSwitchPort(port.getPortNumber());
+			    }
+			    datagridService.notificationSendTopologyElementAdded(topologyElement);
 			}
 		}
 	}
@@ -184,13 +226,15 @@
 		if (registryService.hasControl(sw.getId())) {
 			if (swStore.deleteSwitch(sw.getStringId())) {
 			    // TODO publish DELETE_SWITCH event here
+			    TopologyElement topologyElement =
+				new TopologyElement(sw.getId());
+			    datagridService.notificationSendTopologyElementRemoved(topologyElement);
 			}
 		}
 	}
 
 	@Override
 	public void switchPortChanged(Long switchId) {
-		// TODO Auto-generated method stub
 		// NOTE: Event not needed here. This callback always coincide with add/remove callback.
 	}
 
@@ -199,6 +243,9 @@
 	public void switchPortAdded(Long switchId, OFPhysicalPort port) {
 		if (swStore.addPort(HexString.toHexString(switchId), port)) {
 		    // TODO publish ADD_PORT event here
+		    TopologyElement topologyElement =
+			new TopologyElement(switchId, port.getPortNumber());
+		    datagridService.notificationSendTopologyElementAdded(topologyElement);
 		}
 	}
 
@@ -206,6 +253,9 @@
 	public void switchPortRemoved(Long switchId, OFPhysicalPort port) {
 		if (swStore.deletePort(HexString.toHexString(switchId), port.getPortNumber())) {
 		    // TODO publish DELETE_PORT event here
+		    TopologyElement topologyElement =
+			new TopologyElement(switchId, port.getPortNumber());
+		    datagridService.notificationSendTopologyElementRemoved(topologyElement);
 		}
 	}
 
@@ -223,7 +273,6 @@
 	@Override
 	public void deviceRemoved(IDevice device) {
 		// TODO Auto-generated method stub
-
 	}
 
 	@Override
@@ -266,6 +315,7 @@
 	            new ArrayList<Class<? extends IFloodlightService>>();
 	        l.add(IFloodlightProviderService.class);
 	        l.add(IDeviceService.class);
+	        l.add(IDatagridService.class);
 	        l.add(IThreadPoolService.class);
 	        return l;
 	}
@@ -283,6 +333,7 @@
 		linkDiscovery = context.getServiceImpl(ILinkDiscoveryService.class);
 		threadPool = context.getServiceImpl(IThreadPoolService.class);
 		registryService = context.getServiceImpl(IControllerRegistryService.class);
+		datagridService = context.getServiceImpl(IDatagridService.class);
 		
 		devStore = new DeviceStorageImpl();
 		devStore.init(conf);
@@ -315,6 +366,11 @@
 				cleanupTask = new SingletonTask(ses, new SwitchCleanup());
 				cleanupTask.reschedule(CLEANUP_TASK_INTERVAL, TimeUnit.SECONDS);
 		}
+
+		//
+		// NOTE: No need to register with the Datagrid Service,
+		// because we don't need to receive any notifications from it.
+		//
 	}
 
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowDatabaseOperation.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowDatabaseOperation.java
index 3178b60..d06c62c 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowDatabaseOperation.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowDatabaseOperation.java
@@ -35,14 +35,11 @@
      * @param dbHandler the Graph Database handler to use.
      * @param flowPath the Flow Path to install.
      * @param flowId the return-by-reference Flow ID as assigned internally.
-     * @param dataPathSummaryStr the data path summary string if the added
-     * flow will be maintained internally, otherwise null.
      * @return true on success, otherwise false.
      */
     static boolean addFlow(FlowManager flowManager,
 			   GraphDBOperation dbHandler,
-			   FlowPath flowPath, FlowId flowId,
-			   String dataPathSummaryStr) {
+			   FlowPath flowPath, FlowId flowId) {
 	IFlowPath flowObj = null;
 	boolean found = false;
 	try {
@@ -80,6 +77,8 @@
 	//
 	// Set the Flow attributes:
 	// - flowPath.installerId()
+	// - flowPath.flowPathType()
+	// - flowPath.flowPathUserState()
 	// - flowPath.flowPathFlags()
 	// - flowPath.dataPath().srcPort()
 	// - flowPath.dataPath().dstPort()
@@ -97,6 +96,8 @@
 	// - flowPath.flowEntryActions()
 	//
 	flowObj.setInstallerId(flowPath.installerId().toString());
+	flowObj.setFlowPathType(flowPath.flowPathType().toString());
+	flowObj.setFlowPathUserState(flowPath.flowPathUserState().toString());
 	flowObj.setFlowPathFlags(flowPath.flowPathFlags().flags());
 	flowObj.setSrcSwitch(flowPath.dataPath().srcPort().dpid().toString());
 	flowObj.setSrcPort(flowPath.dataPath().srcPort().port().value());
@@ -138,17 +139,12 @@
 	if (! flowPath.flowEntryActions().actions().isEmpty()) {
 	    flowObj.setActions(flowPath.flowEntryActions().toString());
 	}
-
-	if (dataPathSummaryStr != null) {
-	    flowObj.setDataPathSummary(dataPathSummaryStr);
-	} else {
-	    flowObj.setDataPathSummary("");
-	}
+	flowObj.setDataPathSummary(flowPath.dataPath().dataPathSummary());
 
 	if (found)
-	    flowObj.setUserState("FE_USER_MODIFY");
+	    flowObj.setFlowPathUserState("FP_USER_MODIFY");
 	else
-	    flowObj.setUserState("FE_USER_ADD");
+	    flowObj.setFlowPathUserState("FP_USER_ADD");
 
 	// Flow edges:
 	//   HeadFE
@@ -313,7 +309,7 @@
 	    flowEntryObj.setUserState("FE_USER_MODIFY");
 	else
 	    flowEntryObj.setUserState("FE_USER_ADD");
-	flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
+	flowEntryObj.setSwitchState(flowEntry.flowEntrySwitchState().toString());
 	//
 	// TODO: Take care of the FlowEntryErrorState.
 	//
@@ -427,7 +423,7 @@
 	// Find and mark for deletion all Flow Entries,
 	// and the Flow itself.
 	//
-	flowObj.setUserState("FE_USER_DELETE");
+	flowObj.setFlowPathUserState("FP_USER_DELETE");
 	Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
 	boolean empty = true;	// TODO: an ugly hack
 	for (IFlowEntry flowEntryObj : flowEntries) {
@@ -751,6 +747,8 @@
 	//
 	String flowIdStr = flowObj.getFlowId();
 	String installerIdStr = flowObj.getInstallerId();
+	String flowPathType = flowObj.getFlowPathType();
+	String flowPathUserState = flowObj.getFlowPathUserState();
 	Long flowPathFlags = flowObj.getFlowPathFlags();
 	String srcSwitchStr = flowObj.getSrcSwitch();
 	Short srcPortShort = flowObj.getSrcPort();
@@ -759,6 +757,8 @@
 
 	if ((flowIdStr == null) ||
 	    (installerIdStr == null) ||
+	    (flowPathType == null) ||
+	    (flowPathUserState == null) ||
 	    (flowPathFlags == null) ||
 	    (srcSwitchStr == null) ||
 	    (srcPortShort == null) ||
@@ -771,6 +771,8 @@
 	FlowPath flowPath = new FlowPath();
 	flowPath.setFlowId(new FlowId(flowIdStr));
 	flowPath.setInstallerId(new CallerId(installerIdStr));
+	flowPath.setFlowPathType(FlowPathType.valueOf(flowPathType));
+	flowPath.setFlowPathUserState(FlowPathUserState.valueOf(flowPathUserState));
 	flowPath.setFlowPathFlags(new FlowPathFlags(flowPathFlags));
 	flowPath.dataPath().srcPort().setDpid(new Dpid(srcSwitchStr));
 	flowPath.dataPath().srcPort().setPort(new Port(srcPortShort));
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowEventHandler.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowEventHandler.java
new file mode 100644
index 0000000..29deb94
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowEventHandler.java
@@ -0,0 +1,661 @@
+package net.onrc.onos.ofcontroller.flowmanager;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import net.onrc.onos.datagrid.IDatagridService;
+import net.onrc.onos.ofcontroller.topology.ShortestPath;
+import net.onrc.onos.ofcontroller.topology.Topology;
+import net.onrc.onos.ofcontroller.topology.TopologyElement;
+import net.onrc.onos.ofcontroller.topology.TopologyManager;
+import net.onrc.onos.ofcontroller.util.DataPath;
+import net.onrc.onos.ofcontroller.util.EventEntry;
+import net.onrc.onos.ofcontroller.util.FlowEntry;
+import net.onrc.onos.ofcontroller.util.FlowEntryAction;
+import net.onrc.onos.ofcontroller.util.FlowEntryActions;
+import net.onrc.onos.ofcontroller.util.FlowEntryId;
+import net.onrc.onos.ofcontroller.util.FlowEntryMatch;
+import net.onrc.onos.ofcontroller.util.FlowEntrySwitchState;
+import net.onrc.onos.ofcontroller.util.FlowEntryUserState;
+import net.onrc.onos.ofcontroller.util.FlowId;
+import net.onrc.onos.ofcontroller.util.FlowPath;
+import net.onrc.onos.ofcontroller.util.FlowPathUserState;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Class for implementing the Path Computation and Path Maintenance.
+ */
+class FlowEventHandler extends Thread implements IFlowEventHandlerService {
+    /** The logger. */
+    private final static Logger log = LoggerFactory.getLogger(FlowEventHandler.class);
+
+    private FlowManager flowManager;		// The Flow Manager to use
+    private IDatagridService datagridService;	// The Datagrid Service to use
+    private Topology topology;			// The network topology
+    private Map<Long, FlowPath> allFlowPaths = new HashMap<Long, FlowPath>();
+    private List<FlowEntry> unmatchedFlowEntryUpdates =
+	new LinkedList<FlowEntry>();
+
+    // The queue with Flow Path and Topology Element updates
+    private BlockingQueue<EventEntry<?>> networkEvents =
+	new LinkedBlockingQueue<EventEntry<?>>();
+
+    // The pending Topology, FlowPath, and FlowEntry events
+    private List<EventEntry<TopologyElement>> topologyEvents =
+	new LinkedList<EventEntry<TopologyElement>>();
+    private List<EventEntry<FlowPath>> flowPathEvents =
+	new LinkedList<EventEntry<FlowPath>>();
+    private List<EventEntry<FlowEntry>> flowEntryEvents =
+	new LinkedList<EventEntry<FlowEntry>>();
+
+    /**
+     * Constructor for a given Flow Manager and Datagrid Service.
+     *
+     * @param flowManager the Flow Manager to use.
+     * @param datagridService the Datagrid Service to use.
+     */
+    FlowEventHandler(FlowManager flowManager,
+		     IDatagridService datagridService) {
+	this.flowManager = flowManager;
+	this.datagridService = datagridService;
+	this.topology = new Topology();
+    }
+
+    /**
+     * Run the thread.
+     */
+    @Override
+    public void run() {
+	//
+	// Obtain the initial Topology state
+	//
+	Collection<TopologyElement> topologyElements =
+	    datagridService.getAllTopologyElements();
+	for (TopologyElement topologyElement : topologyElements) {
+	    EventEntry<TopologyElement> eventEntry =
+		new EventEntry<TopologyElement>(EventEntry.Type.ENTRY_ADD, topologyElement);
+	    topologyEvents.add(eventEntry);
+	}
+	//
+	// Obtain the initial Flow Path state
+	//
+	Collection<FlowPath> flowPaths = datagridService.getAllFlows();
+	for (FlowPath flowPath : flowPaths) {
+	    EventEntry<FlowPath> eventEntry =
+		new EventEntry<FlowPath>(EventEntry.Type.ENTRY_ADD, flowPath);
+	    flowPathEvents.add(eventEntry);
+	}
+	//
+	// Obtain the initial FlowEntry state
+	//
+	Collection<FlowEntry> flowEntries = datagridService.getAllFlowEntries();
+	for (FlowEntry flowEntry : flowEntries) {
+	    EventEntry<FlowEntry> eventEntry =
+		new EventEntry<FlowEntry>(EventEntry.Type.ENTRY_ADD, flowEntry);
+	    flowEntryEvents.add(eventEntry);
+	}
+
+	// Process the events (if any)
+	processEvents();
+
+	//
+	// The main loop
+	//
+	Collection<EventEntry<?>> collection = new LinkedList<EventEntry<?>>();
+	try {
+	    while (true) {
+		EventEntry<?> eventEntry = networkEvents.take();
+		collection.add(eventEntry);
+		networkEvents.drainTo(collection);
+
+		//
+		// Demultiplex all events:
+		//  - EventEntry<TopologyElement>
+		//  - EventEntry<FlowPath>
+		//  - EventEntry<FlowEntry>
+		//
+		for (EventEntry<?> event : collection) {
+		    if (event.eventData() instanceof TopologyElement) {
+			EventEntry<TopologyElement> topologyEventEntry =
+			    (EventEntry<TopologyElement>)event;
+			topologyEvents.add(topologyEventEntry);
+		    } else if (event.eventData() instanceof FlowPath) {
+			EventEntry<FlowPath> flowPathEventEntry =
+			    (EventEntry<FlowPath>)event;
+			flowPathEvents.add(flowPathEventEntry);
+		    } else if (event.eventData() instanceof FlowEntry) {
+			EventEntry<FlowEntry> flowEntryEventEntry =
+			    (EventEntry<FlowEntry>)event;
+			flowEntryEvents.add(flowEntryEventEntry);
+		    }
+		}
+		collection.clear();
+
+		// Process the events (if any)
+		processEvents();
+	    }
+	} catch (Exception exception) {
+	    log.debug("Exception processing Network Events: ", exception);
+	}
+    }
+
+    /**
+     * Process the events (if any)
+     */
+    private void processEvents() {
+	List<FlowPath> newFlowPaths = new LinkedList<FlowPath>();
+	List<FlowPath> recomputeFlowPaths = new LinkedList<FlowPath>();
+	List<FlowPath> modifiedFlowPaths = new LinkedList<FlowPath>();
+
+	if (topologyEvents.isEmpty() && flowPathEvents.isEmpty() &&
+	    flowEntryEvents.isEmpty()) {
+	    return;		// Nothing to do
+	}
+
+	//
+	// Process the Flow Path events
+	//
+	for (EventEntry<FlowPath> eventEntry : flowPathEvents) {
+	    FlowPath flowPath = eventEntry.eventData();
+
+	    switch (eventEntry.eventType()) {
+	    case ENTRY_ADD: {
+		//
+		// Add a new Flow Path
+		//
+		if (allFlowPaths.get(flowPath.flowId().value()) != null) {
+		    //
+		    // TODO: What to do if the Flow Path already exists?
+		    // Remove and then re-add it, or merge the info?
+		    // For now, we don't have to do anything.
+		    //
+		    break;
+		}
+
+		switch (flowPath.flowPathType()) {
+		case FP_TYPE_SHORTEST_PATH:
+		    //
+		    // Reset the Data Path, in case it was set already, because
+		    // we are going to recompute it anyway.
+		    //
+		    flowPath.flowEntries().clear();
+		    recomputeFlowPaths.add(flowPath);
+		    break;
+		case FP_TYPE_EXPLICIT_PATH:
+		    //
+		    // Mark all Flow Entries for installation in the switches.
+		    //
+		    for (FlowEntry flowEntry : flowPath.flowEntries()) {
+			flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
+		    }
+		    modifiedFlowPaths.add(flowPath);
+		    break;
+		}
+		newFlowPaths.add(flowPath);
+
+		break;
+	    }
+
+	    case ENTRY_REMOVE: {
+		//
+		// Remove an existing Flow Path.
+		//
+		// Find the Flow Path, and mark the Flow and its Flow Entries
+		// for deletion.
+		//
+		FlowPath existingFlowPath =
+		    allFlowPaths.get(flowPath.flowId().value());
+		if (existingFlowPath == null)
+		    continue;		// Nothing to do
+
+		existingFlowPath.setFlowPathUserState(FlowPathUserState.FP_USER_DELETE);
+		for (FlowEntry flowEntry : existingFlowPath.flowEntries()) {
+		    flowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_DELETE);
+		    flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
+		}
+
+		allFlowPaths.remove(existingFlowPath.flowId());
+		modifiedFlowPaths.add(existingFlowPath);
+
+		break;
+	    }
+	    }
+	}
+
+	//
+	// Process the topology events
+	//
+	boolean isTopologyModified = false;
+	for (EventEntry<TopologyElement> eventEntry : topologyEvents) {
+	    TopologyElement topologyElement = eventEntry.eventData();
+	    switch (eventEntry.eventType()) {
+	    case ENTRY_ADD:
+		isTopologyModified = topology.addTopologyElement(topologyElement);
+		break;
+	    case ENTRY_REMOVE:
+		isTopologyModified = topology.removeTopologyElement(topologyElement);
+		break;
+	    }
+	}
+	if (isTopologyModified) {
+	    // TODO: For now, if the topology changes, we recompute all Flows
+	    recomputeFlowPaths.addAll(allFlowPaths.values());
+	}
+
+	// Add all new Flows
+	for (FlowPath flowPath : newFlowPaths) {
+	    allFlowPaths.put(flowPath.flowId().value(), flowPath);
+	}
+
+	// Recompute all affected Flow Paths and keep only the modified
+	for (FlowPath flowPath : recomputeFlowPaths) {
+	    if (recomputeFlowPath(flowPath))
+		modifiedFlowPaths.add(flowPath);
+	}
+
+	//
+	// Process previously unmatched Flow Entry updates
+	//
+	if ((! flowPathEvents.isEmpty()) && (! unmatchedFlowEntryUpdates.isEmpty())) {
+	    List<FlowEntry> remainingUpdates = new LinkedList<FlowEntry>();
+	    for (FlowEntry flowEntry : unmatchedFlowEntryUpdates) {
+		if (! updateFlowEntry(flowEntry))
+		    remainingUpdates.add(flowEntry);
+	    }
+	    unmatchedFlowEntryUpdates = remainingUpdates;
+	}
+
+	//
+	// Process the Flow Entry events
+	//
+	for (EventEntry<FlowEntry> eventEntry : flowEntryEvents) {
+	    FlowEntry flowEntry = eventEntry.eventData();
+	    switch (eventEntry.eventType()) {
+	    case ENTRY_ADD:
+		//
+		// Find the corresponding Flow Entry and update it.
+		// If not found, then keep it in a local cache for
+		// later matching.
+		//
+		if (! updateFlowEntry(flowEntry))
+		    unmatchedFlowEntryUpdates.add(flowEntry);
+		break;
+	    case ENTRY_REMOVE:
+		//
+		// NOTE: For now we remove the Flow Entries based on
+		// local decisions, so no need to remove them because of an
+		// external event.
+		//
+		break;
+	    }
+	}
+
+	//
+	// Push the Flow Entries that have been modified
+	//
+	flowManager.pushModifiedFlowEntries(modifiedFlowPaths);
+
+	// Cleanup
+	topologyEvents.clear();
+	flowPathEvents.clear();
+	flowEntryEvents.clear();
+    }
+
+    /**
+     * Update a Flow Entry because of an external event.
+     *
+     * @param flowEntry the FlowEntry with the new state.
+     * @return true if the Flow Entry was found and updated, otherwise false.
+     */
+    private boolean updateFlowEntry(FlowEntry flowEntry) {
+	if ((! flowEntry.isValidFlowId()) ||
+	    (! flowEntry.isValidFlowEntryId())) {
+	    //
+	    // Ignore events for Flow Entries with invalid Flow ID or
+	    // Flow Entry ID.
+	    // This shouldn't happen.
+	    //
+	    return true;
+	}
+
+	FlowPath flowPath = allFlowPaths.get(flowEntry.flowId().value());
+	if (flowPath == null)
+	    return false;
+
+	//
+	// Iterate over all Flow Entries and find a match based on the DPID
+	//
+	for (FlowEntry localFlowEntry : flowPath.flowEntries()) {
+	    if (localFlowEntry.dpid().value() != flowEntry.dpid().value())
+		continue;
+	    //
+	    // TODO: We might want to check the FlowEntryMatch and
+	    // FlowEntryActions to double-check it is the same Flow Entry
+	    //
+
+	    //
+	    // Local Flow Entry match found
+	    //
+	    if (localFlowEntry.isValidFlowEntryId()) {
+		if (localFlowEntry.flowEntryId().value() !=
+		    flowEntry.flowEntryId().value()) {
+		    //
+		    // Find a local Flow Entry, but the Flow Entry ID doesn't
+		    // match. Ignore the event.
+		    //
+		    return true;
+		}
+	    } else {
+		// Update the Flow Entry ID
+		FlowEntryId flowEntryId =
+		    new FlowEntryId(flowEntry.flowEntryId().value());
+		localFlowEntry.setFlowEntryId(flowEntryId);
+	    }
+
+	    //
+	    // Update the local Flow Entry.
+	    // For now we update only the Flow Entry Switch State
+	    //
+	    localFlowEntry.setFlowEntrySwitchState(flowEntry.flowEntrySwitchState());
+	    return true;
+	}
+
+	return false;		// Entry not found
+    }
+
+    /**
+     * Recompute a Flow Path.
+     *
+     * @param flowPath the Flow Path to recompute.
+     * @return true if the recomputed Flow Path has changed, otherwise false.
+     */
+    private boolean recomputeFlowPath(FlowPath flowPath) {
+	boolean hasChanged = false;
+
+	//
+	// Test whether the Flow Path needs to be recomputed
+	//
+	switch (flowPath.flowPathType()) {
+	case FP_TYPE_SHORTEST_PATH:
+	    break;
+	case FP_TYPE_EXPLICIT_PATH:
+	    return false;		// An explicit path never changes
+	}
+
+	DataPath oldDataPath = flowPath.dataPath();
+
+	// Compute the new path
+	DataPath newDataPath = TopologyManager.computeNetworkPath(topology,
+								  flowPath);
+	if (newDataPath == null) {
+	    // We need the DataPath to compare the paths
+	    newDataPath = new DataPath();
+	}
+	newDataPath.applyFlowPathFlags(flowPath.flowPathFlags());
+
+	//
+	// Test whether the new path is same
+	//
+	if (oldDataPath.flowEntries().size() !=
+	    newDataPath.flowEntries().size()) {
+	    hasChanged = true;
+	} else {
+	    Iterator<FlowEntry> oldIter = oldDataPath.flowEntries().iterator();
+	    Iterator<FlowEntry> newIter = newDataPath.flowEntries().iterator();
+	    while (oldIter.hasNext() && newIter.hasNext()) {
+		FlowEntry oldFlowEntry = oldIter.next();
+		FlowEntry newFlowEntry = newIter.next();
+		if (! TopologyManager.isSameFlowEntryDataPath(oldFlowEntry,
+							      newFlowEntry)) {
+		    hasChanged = true;
+		    break;
+		}
+	    }
+	}
+	if (! hasChanged)
+	    return hasChanged;
+
+	//
+	// Merge the changes in the path:
+	//  - If a Flow Entry for a switch is in the old data path, but not
+	//    in the new data path, then mark it for deletion.
+	//  - If a Flow Entry for a switch is in the new data path, but not
+	//    in the old data path, then mark it for addition.
+	//  - If a Flow Entry for a switch is in both the old and the new
+	//    data path, but it has changed, e.g., the incoming and/or outgoing
+	//    port(s), then mark the old Flow Entry for deletion, and mark
+	//    the new Flow Entry for addition.
+	//  - If a Flow Entry for a switch is in both the old and the new
+	//    data path, and it hasn't changed, then just keep it.
+	//
+	// NOTE: We use the Switch DPID of each entry to match the entries
+	//
+	Map<Long, FlowEntry> oldFlowEntriesMap = new HashMap<Long, FlowEntry>();
+	Map<Long, FlowEntry> newFlowEntriesMap = new HashMap<Long, FlowEntry>();
+	ArrayList<FlowEntry> finalFlowEntries = new ArrayList<FlowEntry>();
+	List<FlowEntry> deletedFlowEntries = new LinkedList<FlowEntry>();
+
+	// Prepare maps with the Flow Entries, so they are fast to lookup
+	for (FlowEntry flowEntry : oldDataPath.flowEntries())
+	    oldFlowEntriesMap.put(flowEntry.dpid().value(), flowEntry);
+	for (FlowEntry flowEntry : newDataPath.flowEntries())
+	    newFlowEntriesMap.put(flowEntry.dpid().value(), flowEntry);
+
+	//
+	// Find the old Flow Entries that should be deleted
+	//
+	for (FlowEntry oldFlowEntry : oldDataPath.flowEntries()) {
+	    FlowEntry newFlowEntry =
+		newFlowEntriesMap.get(oldFlowEntry.dpid().value());
+	    if (newFlowEntry == null) {
+		// The old Flow Entry should be deleted
+		oldFlowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_DELETE);
+		oldFlowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
+		deletedFlowEntries.add(oldFlowEntry);
+	    }
+	}
+
+	//
+	// Find the new Flow Entries that should be added or updated
+	//
+	int idx = 0;
+	for (FlowEntry newFlowEntry : newDataPath.flowEntries()) {
+	    FlowEntry oldFlowEntry =
+		oldFlowEntriesMap.get(newFlowEntry.dpid().value());
+
+	    if ((oldFlowEntry != null) &&
+		TopologyManager.isSameFlowEntryDataPath(oldFlowEntry,
+							newFlowEntry)) {
+		//
+		// Both Flow Entries are same
+		//
+		finalFlowEntries.add(oldFlowEntry);
+		idx++;
+		continue;
+	    }
+
+	    if (oldFlowEntry != null) {
+		//
+		// The old Flow Entry should be deleted
+		//
+		oldFlowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_DELETE);
+		oldFlowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
+		deletedFlowEntries.add(oldFlowEntry);
+	    }
+
+	    //
+	    // Add the new Flow Entry
+	    //
+	    //
+	    // NOTE: Assign only the Flow ID.
+	    // The Flow Entry ID is assigned later only for the Flow Entries
+	    // this instance is responsible for.
+	    //
+	    newFlowEntry.setFlowId(new FlowId(flowPath.flowId().value()));
+
+	    // Set the incoming port matching
+	    FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
+	    newFlowEntry.setFlowEntryMatch(flowEntryMatch);
+	    flowEntryMatch.enableInPort(newFlowEntry.inPort());
+
+	    //
+	    // Set the actions:
+	    // If the first Flow Entry, copy the Flow Path actions to it.
+	    //
+	    FlowEntryActions flowEntryActions = newFlowEntry.flowEntryActions();
+	    if ((idx == 0)  && (flowPath.flowEntryActions() != null)) {
+		FlowEntryActions flowActions =
+		    new FlowEntryActions(flowPath.flowEntryActions());
+		for (FlowEntryAction action : flowActions.actions())
+		    flowEntryActions.addAction(action);
+	    }
+	    idx++;
+
+	    //
+	    // Add the outgoing port output action
+	    //
+	    FlowEntryAction flowEntryAction = new FlowEntryAction();
+	    flowEntryAction.setActionOutput(newFlowEntry.outPort());
+	    flowEntryActions.addAction(flowEntryAction);
+
+	    //
+	    // Set the state of the new Flow Entry
+	    //
+	    newFlowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_ADD);
+	    newFlowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
+	    finalFlowEntries.add(newFlowEntry);
+	}
+
+	//
+	// Replace the old Flow Entries with the new Flow Entries.
+	// Note that the Flow Entries that will be deleted are added at
+	// the end.
+	//
+	for (FlowEntry flowEntry : deletedFlowEntries)
+	    finalFlowEntries.add(flowEntry);
+	flowPath.dataPath().setFlowEntries(finalFlowEntries);
+
+	return hasChanged;
+    }
+
+    /**
+     * Receive a notification that a Flow is added.
+     *
+     * @param flowPath the Flow that is added.
+     */
+    @Override
+    public void notificationRecvFlowAdded(FlowPath flowPath) {
+	EventEntry<FlowPath> eventEntry =
+	    new EventEntry<FlowPath>(EventEntry.Type.ENTRY_ADD, flowPath);
+	networkEvents.add(eventEntry);
+    }
+
+    /**
+     * Receive a notification that a Flow is removed.
+     *
+     * @param flowPath the Flow that is removed.
+     */
+    @Override
+    public void notificationRecvFlowRemoved(FlowPath flowPath) {
+	EventEntry<FlowPath> eventEntry =
+	    new EventEntry<FlowPath>(EventEntry.Type.ENTRY_REMOVE, flowPath);
+	networkEvents.add(eventEntry);
+    }
+
+    /**
+     * Receive a notification that a Flow is updated.
+     *
+     * @param flowPath the Flow that is updated.
+     */
+    @Override
+    public void notificationRecvFlowUpdated(FlowPath flowPath) {
+	// NOTE: The ADD and UPDATE events are processed in same way
+	EventEntry<FlowPath> eventEntry =
+	    new EventEntry<FlowPath>(EventEntry.Type.ENTRY_ADD, flowPath);
+	networkEvents.add(eventEntry);
+    }
+
+    /**
+     * Receive a notification that a FlowEntry is added.
+     *
+     * @param flowEntry the FlowEntry that is added.
+     */
+    @Override
+    public void notificationRecvFlowEntryAdded(FlowEntry flowEntry) {
+	EventEntry<FlowEntry> eventEntry =
+	    new EventEntry<FlowEntry>(EventEntry.Type.ENTRY_ADD, flowEntry);
+	networkEvents.add(eventEntry);
+    }
+
+    /**
+     * Receive a notification that a FlowEntry is removed.
+     *
+     * @param flowEntry the FlowEntry that is removed.
+     */
+    @Override
+    public void notificationRecvFlowEntryRemoved(FlowEntry flowEntry) {
+	EventEntry<FlowEntry> eventEntry =
+	    new EventEntry<FlowEntry>(EventEntry.Type.ENTRY_REMOVE, flowEntry);
+	networkEvents.add(eventEntry);
+    }
+
+    /**
+     * Receive a notification that a FlowEntry is updated.
+     *
+     * @param flowEntry the FlowEntry that is updated.
+     */
+    @Override
+    public void notificationRecvFlowEntryUpdated(FlowEntry flowEntry) {
+	// NOTE: The ADD and UPDATE events are processed in same way
+	EventEntry<FlowEntry> eventEntry =
+	    new EventEntry<FlowEntry>(EventEntry.Type.ENTRY_ADD, flowEntry);
+	networkEvents.add(eventEntry);
+    }
+
+    /**
+     * Receive a notification that a Topology Element is added.
+     *
+     * @param topologyElement the Topology Element that is added.
+     */
+    @Override
+    public void notificationRecvTopologyElementAdded(TopologyElement topologyElement) {
+	EventEntry<TopologyElement> eventEntry =
+	    new EventEntry<TopologyElement>(EventEntry.Type.ENTRY_ADD, topologyElement);
+	networkEvents.add(eventEntry);
+    }
+
+    /**
+     * Receive a notification that a Topology Element is removed.
+     *
+     * @param topologyElement the Topology Element that is removed.
+     */
+    @Override
+    public void notificationRecvTopologyElementRemoved(TopologyElement topologyElement) {
+	EventEntry<TopologyElement> eventEntry =
+	    new EventEntry<TopologyElement>(EventEntry.Type.ENTRY_REMOVE, topologyElement);
+	networkEvents.add(eventEntry);
+    }
+
+    /**
+     * Receive a notification that a Topology Element is updated.
+     *
+     * @param topologyElement the Topology Element that is updated.
+     */
+    @Override
+    public void notificationRecvTopologyElementUpdated(TopologyElement topologyElement) {
+	// NOTE: The ADD and UPDATE events are processed in same way
+	EventEntry<TopologyElement> eventEntry =
+	    new EventEntry<TopologyElement>(EventEntry.Type.ENTRY_ADD, topologyElement);
+	networkEvents.add(eventEntry);
+    }
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java
index cad5d9c..4465835 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java
@@ -29,6 +29,7 @@
 import net.onrc.onos.ofcontroller.flowmanager.web.FlowWebRoutable;
 import net.onrc.onos.ofcontroller.topology.ITopologyNetService;
 import net.onrc.onos.ofcontroller.topology.Topology;
+import net.onrc.onos.ofcontroller.topology.TopologyElement;
 import net.onrc.onos.ofcontroller.util.*;
 
 import org.openflow.protocol.OFType;
@@ -47,6 +48,7 @@
     protected volatile IDatagridService datagridService;
     protected IRestApiService restApi;
     protected FloodlightModuleContext context;
+    protected FlowEventHandler flowEventHandler;
 
     protected OFMessageDamper messageDamper;
 
@@ -260,20 +262,20 @@
 		    if (mySwitch == null)
 			continue;	// Ignore: not my responsibility
 
-		    // Test the Data Path Summary string
-		    String dataPathSummaryStr = flowPathObj.getDataPathSummary();
-		    if (dataPathSummaryStr == null)
+		    // Test whether we need to maintain this flow
+		    String flowPathTypeStr = flowPathObj.getFlowPathType();
+		    if (flowPathTypeStr == null)
 			continue;	// Could be invalid entry?
-		    if (dataPathSummaryStr.isEmpty())
+		    if (! flowPathTypeStr.equals("FP_TYPE_SHORTEST_PATH"))
 			continue;	// No need to maintain this flow
 
 		    //
 		    // Test whether we need to complete the Flow cleanup,
 		    // if the Flow has been deleted by the user.
 		    //
-		    String flowUserState = flowPathObj.getUserState();
-		    if ((flowUserState != null)
-			&& flowUserState.equals("FE_USER_DELETE")) {
+		    String flowPathUserStateStr = flowPathObj.getFlowPathUserState();
+		    if ((flowPathUserStateStr != null)
+			&& flowPathUserStateStr.equals("FP_USER_DELETE")) {
 			Iterable<IFlowEntry> flowEntries = flowPathObj.getFlowEntries();
 			final boolean empty = !flowEntries.iterator().hasNext();
 			if (empty)
@@ -281,11 +283,13 @@
 		    }
 
 		    // Fetch the fields needed to recompute the shortest path
+		    String dataPathSummaryStr = flowPathObj.getDataPathSummary();
 		    Short srcPortShort = flowPathObj.getSrcPort();
 		    String dstDpidStr = flowPathObj.getDstSwitch();
 		    Short dstPortShort = flowPathObj.getDstPort();
 		    Long flowPathFlagsLong = flowPathObj.getFlowPathFlags();
-		    if ((srcPortShort == null) ||
+		    if ((dataPathSummaryStr == null) ||
+			(srcPortShort == null) ||
 			(dstDpidStr == null) ||
 			(dstPortShort == null) ||
 			(flowPathFlagsLong == null)) {
@@ -297,6 +301,8 @@
 		    Port dstPort = new Port(dstPortShort);
 		    SwitchPort srcSwitchPort = new SwitchPort(srcDpid, srcPort);
 		    SwitchPort dstSwitchPort = new SwitchPort(dstDpid, dstPort);
+		    FlowPathType flowPathType = FlowPathType.valueOf(flowPathTypeStr);
+		    FlowPathUserState flowPathUserState = FlowPathUserState.valueOf(flowPathUserStateStr);
 		    FlowPathFlags flowPathFlags = new FlowPathFlags(flowPathFlagsLong);
 
 		    counterMyFlowPaths++;
@@ -379,6 +385,7 @@
      */
     @Override
     public void close() {
+	datagridService.deregisterFlowEventHandlerService(flowEventHandler);
     	dbHandler.close();
     }
 
@@ -487,6 +494,15 @@
 	// Initialize the Flow Entry ID generator
 	nextFlowEntryIdPrefix = randomGenerator.nextInt();
 
+	//
+	// Create the Flow Event Handler thread and register it with the
+	// Datagrid Service
+	//
+	flowEventHandler = new FlowEventHandler(this, datagridService);
+	datagridService.registerFlowEventHandlerService(flowEventHandler);
+
+	// Schedule the threads and periodic tasks
+	flowEventHandler.start();
 	mapReaderScheduler.scheduleAtFixedRate(
 			mapReader, 3, 3, TimeUnit.SECONDS);
 	shortestPathReconcileScheduler.scheduleAtFixedRate(
@@ -498,15 +514,28 @@
      *
      * @param flowPath the Flow Path to install.
      * @param flowId the return-by-reference Flow ID as assigned internally.
-     * @param dataPathSummaryStr the data path summary string if the added
-     * flow will be maintained internally, otherwise null.
      * @return true on success, otherwise false.
      */
     @Override
-    public boolean addFlow(FlowPath flowPath, FlowId flowId,
-			   String dataPathSummaryStr) {
-	return FlowDatabaseOperation.addFlow(this, dbHandler, flowPath, flowId,
-					     dataPathSummaryStr);
+    public boolean addFlow(FlowPath flowPath, FlowId flowId) {
+	//
+	// NOTE: We need to explicitly initialize some of the state,
+	// in case the application didn't do it.
+	//
+	for (FlowEntry flowEntry : flowPath.flowEntries()) {
+	    if (flowEntry.flowEntrySwitchState() ==
+		FlowEntrySwitchState.FE_SWITCH_UNKNOWN) {
+		flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
+	    }
+	    if (! flowEntry.isValidFlowId())
+		flowEntry.setFlowId(new FlowId(flowPath.flowId().value()));
+	}
+
+	if (FlowDatabaseOperation.addFlow(this, dbHandler, flowPath, flowId)) {
+	    datagridService.notificationSendFlowAdded(flowPath);
+	    return true;
+	}
+	return false;
     }
 
     /**
@@ -528,7 +557,11 @@
      */
     @Override
     public boolean deleteAllFlows() {
-	return FlowDatabaseOperation.deleteAllFlows(dbHandler);
+	if (FlowDatabaseOperation.deleteAllFlows(dbHandler)) {
+	    datagridService.notificationSendAllFlowsRemoved();
+	    return true;
+	}
+	return false;
     }
 
     /**
@@ -539,7 +572,11 @@
      */
     @Override
     public boolean deleteFlow(FlowId flowId) {
-	return FlowDatabaseOperation.deleteFlow(dbHandler, flowId);
+	if (FlowDatabaseOperation.deleteFlow(dbHandler, flowId)) {
+	    datagridService.notificationSendFlowRemoved(flowId);
+	    return true;
+	}
+	return false;
     }
 
     /**
@@ -549,7 +586,11 @@
      */
     @Override
     public boolean clearAllFlows() {
-	return FlowDatabaseOperation.clearAllFlows(dbHandler);
+	if (FlowDatabaseOperation.clearAllFlows(dbHandler)) {
+	    datagridService.notificationSendAllFlowsRemoved();
+	    return true;
+	}
+	return false;
     }
 
     /**
@@ -560,7 +601,11 @@
      */
     @Override
     public boolean clearFlow(FlowId flowId) {
-	return FlowDatabaseOperation.clearFlow(dbHandler, flowId);
+	if (FlowDatabaseOperation.clearFlow(dbHandler, flowId)) {
+	    datagridService.notificationSendFlowRemoved(flowId);
+	    return true;
+	}
+	return false;
     }
 
     /**
@@ -649,30 +694,11 @@
 	// Instead, let the Flow reconciliation thread take care of it.
 	//
 
-	// We need the DataPath to populate the Network MAP
-	DataPath dataPath = new DataPath();
-	dataPath.setSrcPort(flowPath.dataPath().srcPort());
-	dataPath.setDstPort(flowPath.dataPath().dstPort());
-
-	//
-	// Prepare the computed Flow Path
-	//
-	FlowPath computedFlowPath = new FlowPath();
-	computedFlowPath.setFlowId(new FlowId(flowPath.flowId().value()));
-	computedFlowPath.setInstallerId(new CallerId(flowPath.installerId().value()));
-	computedFlowPath.setFlowPathFlags(new FlowPathFlags(flowPath.flowPathFlags().flags()));
-	computedFlowPath.setDataPath(dataPath);
-	computedFlowPath.setFlowEntryMatch(new FlowEntryMatch(flowPath.flowEntryMatch()));
-	computedFlowPath.setFlowEntryActions(new FlowEntryActions(flowPath.flowEntryActions()));
-
 	FlowId flowId = new FlowId();
-	String dataPathSummaryStr = dataPath.dataPathSummary();
-	if (! addFlow(computedFlowPath, flowId, dataPathSummaryStr))
+	if (! addFlow(flowPath, flowId))
 	    return null;
 
-	// TODO: Mark the flow for maintenance purpose
-
-	return (computedFlowPath);
+	return (flowPath);
     }
 
     /**
@@ -682,7 +708,8 @@
      * @param newDataPath the new data path to use.
      * @return true on success, otherwise false.
      */
-    public boolean reconcileFlow(IFlowPath flowObj, DataPath newDataPath) {
+    private boolean reconcileFlow(IFlowPath flowObj, DataPath newDataPath) {
+	String flowIdStr = flowObj.getFlowId();
 
 	//
 	// Set the incoming port matching and the outgoing port output
@@ -690,6 +717,10 @@
 	//
 	int idx = 0;
 	for (FlowEntry flowEntry : newDataPath.flowEntries()) {
+	    flowEntry.setFlowId(new FlowId(flowIdStr));
+
+	    // Mark the Flow Entry as not updated in the switch
+	    flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
 	    // Set the incoming port matching
 	    FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
 	    flowEntry.setFlowEntryMatch(flowEntryMatch);
@@ -745,7 +776,7 @@
      *
      * @param flowObjSet the set of flows that need to be reconciliated.
      */
-    public void reconcileFlows(Iterable<IFlowPath> flowObjSet) {
+    private void reconcileFlows(Iterable<IFlowPath> flowObjSet) {
 	if (! flowObjSet.iterator().hasNext())
 	    return;
 	// TODO: Not implemented/used yet.
@@ -759,7 +790,7 @@
      * @param flowEntryObj the flow entry object to install.
      * @return true on success, otherwise false.
      */
-    public boolean installFlowEntry(IOFSwitch mySwitch, IFlowPath flowObj,
+    private boolean installFlowEntry(IOFSwitch mySwitch, IFlowPath flowObj,
 				    IFlowEntry flowEntryObj) {
 	return FlowSwitchOperation.installFlowEntry(
 		floodlightProvider.getOFMessageFactory(),
@@ -774,7 +805,7 @@
      * @param flowEntry the flow entry to install.
      * @return true on success, otherwise false.
      */
-    public boolean installFlowEntry(IOFSwitch mySwitch, FlowPath flowPath,
+    private boolean installFlowEntry(IOFSwitch mySwitch, FlowPath flowPath,
 				    FlowEntry flowEntry) {
 	return FlowSwitchOperation.installFlowEntry(
 		floodlightProvider.getOFMessageFactory(),
@@ -789,7 +820,7 @@
      * @param flowEntry the flow entry to remove.
      * @return true on success, otherwise false.
      */
-    public boolean removeFlowEntry(IOFSwitch mySwitch, FlowPath flowPath,
+    private boolean removeFlowEntry(IOFSwitch mySwitch, FlowPath flowPath,
 				   FlowEntry flowEntry) {
 	//
 	// The installFlowEntry() method implements both installation
@@ -797,4 +828,105 @@
 	//
 	return (installFlowEntry(mySwitch, flowPath, flowEntry));
     }
+
+    /**
+     * Push the modified Flow Entries of a collection of Flow Paths.
+     * Only the Flow Entries to switches controlled by this instance
+     * are pushed.
+     *
+     * NOTE: Currently, we write to both the Network MAP and the switches.
+     *
+     * @param modifiedFlowPaths the collection of Flow Paths with the modified
+     * Flow Entries.
+     */
+    public void pushModifiedFlowEntries(Collection<FlowPath> modifiedFlowPaths) {
+
+	// TODO: For now, the pushing of Flow Entries is disabled
+	if (true)
+	    return;
+
+	Map<Long, IOFSwitch> mySwitches = floodlightProvider.getSwitches();
+
+	for (FlowPath flowPath : modifiedFlowPaths) {
+	    IFlowPath flowObj = dbHandler.searchFlowPath(flowPath.flowId());
+	    if (flowObj == null) {
+		String logMsg = "Cannot find Network MAP entry for Flow Path " +
+		    flowPath.flowId();
+		log.error(logMsg);
+		continue;
+	    }
+
+	    for (FlowEntry flowEntry : flowPath.flowEntries()) {
+		if (flowEntry.flowEntrySwitchState() !=
+		    FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED) {
+		    continue;		// No need to update the entry
+		}
+
+		IOFSwitch mySwitch = mySwitches.get(flowEntry.dpid().value());
+		if (mySwitch == null)
+		    continue;		// Ignore the entry: not my switch
+
+		//
+		// Assign the FlowEntry ID if needed
+		//
+		if (! flowEntry.isValidFlowEntryId()) {
+		    long id = getNextFlowEntryId();
+		    flowEntry.setFlowEntryId(new FlowEntryId(id));
+		}
+
+		//
+		// Install the Flow Entry into the switch
+		//
+		if (! installFlowEntry(mySwitch, flowPath, flowEntry)) {
+		    String logMsg = "Cannot install Flow Entry " +
+			flowEntry.flowEntryId() +
+			" from Flow Path " + flowPath.flowId() +
+			" on switch " + flowEntry.dpid();
+		    log.error(logMsg);
+		    continue;
+		}
+
+		//
+		// NOTE: Here we assume that the switch has been successfully
+		// updated.
+		//
+		flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_UPDATED);
+		//
+		// Write the Flow Entry to the Datagrid
+		//
+		switch (flowEntry.flowEntryUserState()) {
+		case FE_USER_ADD:
+		    datagridService.notificationSendFlowEntryAdded(flowEntry);
+		    break;
+		case FE_USER_MODIFY:
+		    datagridService.notificationSendFlowEntryUpdated(flowEntry);
+		    break;
+		case FE_USER_DELETE:
+		    datagridService.notificationSendFlowEntryRemoved(flowEntry.flowEntryId());
+		    break;
+		}
+
+		//
+		// Write the Flow Entry to the Network Map
+		//
+		try {
+		    if (addFlowEntry(flowObj, flowEntry) == null) {
+			String logMsg = "Cannot write to Network MAP Flow Entry " +
+			    flowEntry.flowEntryId() +
+			    " from Flow Path " + flowPath.flowId() +
+			    " on switch " + flowEntry.dpid();
+			log.error(logMsg);
+			continue;
+		    }
+		} catch (Exception e) {
+		    String logMsg = "Exception writing Flow Entry to Network MAP";
+		    log.debug(logMsg);
+		    dbHandler.rollback();
+		    continue;
+		}
+	    }
+	}
+
+	dbHandler.commit();
+    }
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowSwitchOperation.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowSwitchOperation.java
index 3b64096..9741b04 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowSwitchOperation.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowSwitchOperation.java
@@ -202,6 +202,8 @@
 	String actionsStr = flowEntryObj.getActions();
 	if (actionsStr != null)
 	    flowEntryActions = new FlowEntryActions(actionsStr);
+	else
+	    flowEntryActions = new FlowEntryActions();
 	for (FlowEntryAction action : flowEntryActions.actions()) {
 	    ActionOutput actionOutput = action.actionOutput();
 	    ActionSetVlanId actionSetVlanId = action.actionSetVlanId();
@@ -655,6 +657,14 @@
 	//
 	// Write the message to the switch
 	//
+	log.debug("MEASUREMENT: Installing flow entry " +
+		  flowEntry.flowEntryUserState() +
+		  " into switch DPID: " +
+		  mySwitch.getStringId() +
+		  " flowEntryId: " + flowEntry.flowEntryId().toString() +
+		  " srcMac: " + matchSrcMac + " dstMac: " + matchDstMac +
+		  " inPort: " + matchInPort + " outPort: " + actionOutputPort
+		  );
 	try {
 	    messageDamper.write(mySwitch, fm, null);
 	    mySwitch.flush();
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IFlowEventHandlerService.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IFlowEventHandlerService.java
new file mode 100644
index 0000000..78562e1
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IFlowEventHandlerService.java
@@ -0,0 +1,73 @@
+package net.onrc.onos.ofcontroller.flowmanager;
+
+import net.onrc.onos.ofcontroller.topology.TopologyElement;
+import net.onrc.onos.ofcontroller.util.FlowEntry;
+import net.onrc.onos.ofcontroller.util.FlowPath;
+
+/**
+ * Interface for providing Flow Event Handler Service to other modules.
+ */
+public interface IFlowEventHandlerService {
+    /**
+     * Receive a notification that a Flow is added.
+     *
+     * @param flowPath the Flow that is added.
+     */
+    void notificationRecvFlowAdded(FlowPath flowPath);
+
+    /**
+     * Receive a notification that a Flow is removed.
+     *
+     * @param flowPath the Flow that is removed.
+     */
+    void notificationRecvFlowRemoved(FlowPath flowPath);
+
+    /**
+     * Receive a notification that a Flow is updated.
+     *
+     * @param flowPath the Flow that is updated.
+     */
+    void notificationRecvFlowUpdated(FlowPath flowPath);
+
+    /**
+     * Receive a notification that a FlowEntry is added.
+     *
+     * @param flowEntry the FlowEntry that is added.
+     */
+    void notificationRecvFlowEntryAdded(FlowEntry flowEntry);
+
+    /**
+     * Receive a notification that a FlowEntry is removed.
+     *
+     * @param flowEntry the FlowEntry that is removed.
+     */
+    void notificationRecvFlowEntryRemoved(FlowEntry flowEntry);
+
+    /**
+     * Receive a notification that a FlowEntry is updated.
+     *
+     * @param flowEntry the FlowEntry that is updated.
+     */
+    void notificationRecvFlowEntryUpdated(FlowEntry flowEntry);
+
+    /**
+     * Receive a notification that a Topology Element is added.
+     *
+     * @param topologyElement the Topology Element that is added.
+     */
+    void notificationRecvTopologyElementAdded(TopologyElement topologyElement);
+
+    /**
+     * Receive a notification that a Topology Element is removed.
+     *
+     * @param topologyElement the Topology Element that is removed.
+     */
+    void notificationRecvTopologyElementRemoved(TopologyElement topologyElement);
+
+    /**
+     * Receive a notification that a Topology Element is updated.
+     *
+     * @param topologyElement the Topology Element that is updated.
+     */
+    void notificationRecvTopologyElementUpdated(TopologyElement topologyElement);
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IFlowService.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IFlowService.java
index 73f86b6..1f8cd5b 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IFlowService.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IFlowService.java
@@ -21,12 +21,9 @@
      *
      * @param flowPath the Flow Path to install.
      * @param flowId the return-by-reference Flow ID as assigned internally.
-     * @param dataPathSummaryStr the data path summary string if the added
-     * flow will be maintained internally, otherwise null.
      * @return true on success, otherwise false.
      */
-    boolean addFlow(FlowPath flowPath, FlowId flowId,
-		    String dataPathSummaryStr);
+    boolean addFlow(FlowPath flowPath, FlowId flowId);
 
     /**
      * Delete all previously added flows.
@@ -114,5 +111,5 @@
      * conditions to install.
      * @return the added shortest-path flow on success, otherwise null.
      */
-    public FlowPath addAndMaintainShortestPathFlow(FlowPath flowPath);
+    FlowPath addAndMaintainShortestPathFlow(FlowPath flowPath);
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/AddFlowResource.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/AddFlowResource.java
index d891374..0926f91 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/AddFlowResource.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/AddFlowResource.java
@@ -64,7 +64,7 @@
 
 	// Process the request
 	if (flowPath != null) {
-	    if (flowService.addFlow(flowPath, result, null) != true) {
+	    if (flowService.addFlow(flowPath, result) != true) {
 		result = new FlowId();		// Error: Return empty Flow Id
 	    }
 	}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/topology/ShortestPath.java b/src/main/java/net/onrc/onos/ofcontroller/topology/ShortestPath.java
index 1133d3d..dabe916 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/topology/ShortestPath.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/topology/ShortestPath.java
@@ -123,7 +123,7 @@
 	Port outPort;
 	for (Node.Link link: resultPath) {
 	    // Setup the outgoing port, and add the Flow Entry
-	    outPort = new Port(link.myPort);
+	    outPort = new Port((short)link.myPort);
 
 	    FlowEntry flowEntry = new FlowEntry();
 	    flowEntry.setDpid(new Dpid(link.me.nodeId));
@@ -132,7 +132,7 @@
 	    result_data_path.flowEntries().add(flowEntry);
 
 	    // Setup the next incoming port
-	    inPort = new Port(link.neighborPort);
+	    inPort = new Port((short)link.neighborPort);
 	}
 	if (resultPath.size() > 0) {
 	    // Add the last Flow Entry
diff --git a/src/main/java/net/onrc/onos/ofcontroller/topology/Topology.java b/src/main/java/net/onrc/onos/ofcontroller/topology/Topology.java
index a2f2c21..612b72a 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/topology/Topology.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/topology/Topology.java
@@ -24,18 +24,18 @@
     class Link {
 	public Node me;			// The node this link originates from
 	public Node neighbor;		// The neighbor node on the other side
-	public short myPort;		// Local port number for the link
-	public short neighborPort;	// Neighbor port number for the link
+	public int myPort;		// Local port ID for the link
+	public int neighborPort;	// Neighbor port ID for the link
 
 	/**
 	 * Link constructor.
 	 *
 	 * @param me the node this link originates from.
 	 * @param the neighbor node on the other side of the link.
-	 * @param myPort local port number for the link.
-	 * @param neighborPort neighrobr port number for the link.
+	 * @param myPort local port ID for the link.
+	 * @param neighborPort neighbor port ID for the link.
 	 */
-	public Link(Node me, Node neighbor, short myPort, short neighborPort) {
+	public Link(Node me, Node neighbor, int myPort, int neighborPort) {
 	    this.me = me;
 	    this.neighbor = neighbor;
 	    this.myPort = myPort;
@@ -43,8 +43,10 @@
 	}
     };
 
-    public long nodeId;			// The node ID
-    public HashMap<Short, Link> links;	// The links originating from this node
+    public long nodeId;				// The node ID
+    public HashMap<Integer, Link> links;	// The links from this node
+    private HashMap<Integer, Link> reverseLinksMap; // The links to this node
+    private HashMap<Integer, Integer> portsMap;	// The ports for this node
 
     /**
      * Node constructor.
@@ -53,21 +55,124 @@
      */
     public Node(long nodeId) {
 	this.nodeId = nodeId;
-	links = new HashMap<Short, Link>();
+	links = new HashMap<Integer, Link>();
+	reverseLinksMap = new HashMap<Integer, Link>();
+	portsMap = new HashMap<Integer, Integer>();
     }
 
     /**
-     * Add a neighbor.
+     * Get all ports.
      *
-     * A new link to the neighbor will be created. 
-     *
-     * @param neighbor the neighbor to add.
-     * @param myPort the local port number for the link to the neighbor.
-     * @param neighborPort the neighbor port number for the link.
+     * @return all ports.
      */
-    public void addNeighbor(Node neighbor, short myPort, short neighborPort) {
-	Link link = new Link(this, neighbor, myPort, neighborPort);
-	links.put(myPort, link);
+    public Map<Integer, Integer> ports() {
+	return portsMap;
+    }
+
+    /**
+     * Get the port for a given Port ID.
+     *
+     * Note: For now the port itself is just the Port ID. In the future
+     * it might contain more information.
+     *
+     * @return the port if found, otherwise null.
+     */
+    public Integer getPort(int portId) {
+	return portsMap.get(nodeId);
+    }
+
+    /**
+     * Add a port for a given Port ID.
+     *
+     * Note: For now the port itself is just the Port ID. In the future
+     * it might contain more information.
+     *
+     * @param portId the Port ID of the port to add.
+     * @return the added Port.
+     */
+    Integer addPort(int portId) {
+	Integer port = new Integer(portId);
+	portsMap.put(portId, port);
+	return port;
+    }
+
+    /**
+     * Remove a port for a given Port ID.
+     *
+     * NOTE: The outgoing and incoming links using this port are removed as
+     * well.
+     */
+    void removePort(int portId) {
+	// Remove the outgoing link
+	Link link = getLink(portId);
+	if (link != null) {
+	    link.neighbor.removeReverseLink(link);
+	    removeLink(portId);
+	}
+
+	// Remove the incoming link
+	Link reverseLink = reverseLinksMap.get(portId);
+	if (reverseLink != null) {
+	    // NOTE: reverseLink.myPort is the neighbor's outgoing port
+	    reverseLink.neighbor.removeLink(reverseLink.myPort);
+	    removeReverseLink(reverseLink);
+	}
+
+	portsMap.remove(portId);
+    }
+    
+    /**
+     * Get a link on a port to a neighbor.
+     *
+     * @param myPortId the local port ID for the link to the neighbor.
+     * @return the link if found, otherwise null.
+     */
+    public Link getLink(int myPortId) {
+	return links.get(myPortId);
+    }
+
+    /**
+     * Add a link to a neighbor.
+     *
+     * @param myPortId the local port ID for the link to the neighbor.
+     * @param neighbor the neighbor for the link.
+     * @param neighborPortId the neighbor port ID for the link.
+     * @return the added Link.
+     */
+    public Link addLink(int myPortId, Node neighbor, int neighborPortId) {
+	Link link = new Link(this, neighbor, myPortId, neighborPortId);
+	links.put(myPortId, link);
+	neighbor.addReverseLink(link);
+	return link;
+    }
+
+    /**
+     * Add a reverse link from a neighbor.
+     *
+     * @param link the reverse link from a neighbor to add.
+     */
+    private void addReverseLink(Link link) {
+	// NOTE: link.neghborPort is my port
+	reverseLinksMap.put(link.neighborPort, link);
+    }
+
+    /**
+     * Remove a link to a neighbor.
+     *
+     * @param myPortId the local port ID for the link to the neighbor.
+     */
+    public void removeLink(int myPortId) {
+	links.remove(myPortId);
+    }
+
+    /**
+     * Remove a reverse link from a neighbor.
+     *
+     * @param link the reverse link from a neighbor to remove.
+     */
+    private void removeReverseLink(Link link) {
+	// NOTE: link.neghborPort is my port
+	reverseLinksMap.remove(link.neighborPort);
     }
 };
 
@@ -77,12 +182,147 @@
 public class Topology {
     private Map<Long, Node> nodesMap;	// The dpid->Node mapping
 
+    /**
+     * Default constructor.
+     */
     public Topology() {
 	nodesMap = new HashMap<Long, Node>();
     }
 
     /**
-     * Get a node for a give Node ID.
+     * Add a topology element to the topology.
+     *
+     * @param topologyElement the topology element to add.
+     * @return true if the topology was modified, otherwise false.
+     */
+    public boolean addTopologyElement(TopologyElement topologyElement) {
+	boolean isModified = false;
+
+	switch (topologyElement.getType()) {
+	case ELEMENT_SWITCH: {
+	    // Add the switch
+	    Node node = getNode(topologyElement.getSwitch());
+	    if (node == null) {
+		node = addNode(topologyElement.getSwitch());
+		isModified = true;
+	    }
+	    // Add the ports for the switch
+	    for (Integer portId : topologyElement.getSwitchPorts().values()) {
+		Integer port = node.getPort(portId);
+		if (port == null) {
+		    node.addPort(portId);
+		    isModified = true;
+		}
+	    }
+	    break;
+	}
+	case ELEMENT_PORT: {
+	    // Add the switch
+	    Node node = getNode(topologyElement.getSwitch());
+	    if (node == null) {
+		node = addNode(topologyElement.getSwitch());
+		isModified = true;
+	    }
+	    // Add the port for the switch
+	    Integer port = node.getPort(topologyElement.getSwitchPort());
+	    if (port == null) {
+		node.addPort(topologyElement.getSwitchPort());
+		isModified = true;
+	    }
+	    break;
+	}
+	case ELEMENT_LINK: {
+	    // Add the "from" switch
+	    Node fromNode = getNode(topologyElement.getFromSwitch());
+	    if (fromNode == null) {
+		fromNode = addNode(topologyElement.getFromSwitch());
+		isModified = true;
+	    }
+	    // Add the "to" switch
+	    Node toNode = getNode(topologyElement.getToSwitch());
+	    if (toNode == null) {
+		toNode = addNode(topologyElement.getToSwitch());
+		isModified = true;
+	    }
+	    // Add the "from" port
+	    Integer fromPort = fromNode.getPort(topologyElement.getFromPort());
+	    if (fromPort == null) {
+		fromNode.addPort(topologyElement.getFromPort());
+		isModified = true;
+	    }
+	    // Add the "to" port
+	    Integer toPort = fromNode.getPort(topologyElement.getToPort());
+	    if (toPort == null) {
+		toNode.addPort(topologyElement.getToPort());
+		isModified = true;
+	    }
+	    Node.Link link = fromNode.getLink(topologyElement.getFromPort());
+	    if (link == null) {
+		fromNode.addLink(topologyElement.getFromPort(),
+				 toNode,
+				 topologyElement.getToPort());
+		isModified = true;
+	    }
+
+	    break;
+	}
+	}
+
+	return isModified;
+    }
+
+    /**
+     * Remove a topology element from the topology.
+     *
+     * @param topologyElement the topology element to remove.
+     * @return true if the topology was modified, otherwise false.
+     */
+    public boolean removeTopologyElement(TopologyElement topologyElement) {
+	boolean isModified = false;
+
+	switch (topologyElement.getType()) {
+	case ELEMENT_SWITCH: {
+	    // Remove the switch
+	    Node node = getNode(topologyElement.getSwitch());
+	    if (node != null) {
+		removeNode(node);
+		isModified = true;
+	    }
+	    break;
+	}
+	case ELEMENT_PORT: {
+	    // Find the switch
+	    Node node = getNode(topologyElement.getSwitch());
+	    if (node == null)
+		break;
+	    // Remove the port for the switch
+	    Integer port = node.getPort(topologyElement.getSwitchPort());
+	    if (port != null) {
+		node.removePort(topologyElement.getSwitchPort());
+		isModified = true;
+	    }
+	    break;
+	}
+	case ELEMENT_LINK: {
+	    // Find the "from" switch
+	    Node fromNode = getNode(topologyElement.getFromSwitch());
+	    if (fromNode == null)
+		break;
+	    // Remove the link originating from the "from" port
+	    Node.Link link = fromNode.getLink(topologyElement.getFromPort());
+	    if (link != null) {
+		fromNode.removeLink(topologyElement.getFromPort());
+		isModified = true;
+	    }
+	    break;
+	}
+	}
+
+	return isModified;
+    }
+
+    /**
+     * Get a node for a given Node ID.
      *
      * @param nodeId the Node ID to use.
      * @return the corresponding Node if found, otherwise null.
@@ -92,6 +332,34 @@
     }
 
     /**
+     * Add a node for a given Node ID.
+     *
+     * @param nodeId the Node ID to use.
+     * @return the added Node.
+     */
+    Node addNode(long nodeId) {
+	Node node = new Node(nodeId);
+	nodesMap.put(nodeId, node);
+	return node;
+    }
+
+    /**
+     * Remove an existing node.
+     *
+     * @param node the Node to remove.
+     */
+    void removeNode(Node node) {
+	//
+	// Remove all ports one-by-one. This operation will also remove the
+	// incoming links originating from the neighbors.
+	//
+	for (Integer portId : node.ports().keySet())
+	    node.removePort(portId);
+
+	nodesMap.remove(node.nodeId);
+    }
+
+    /**
      * Read topology state from the database.
      *
      * @param dbHandler the Graph Database handler to use.
@@ -110,10 +378,8 @@
 	    String nodeDpid = nodeVertex.getProperty("dpid").toString();
 	    long nodeId = HexString.toLong(nodeDpid);
 	    Node me = nodesMap.get(nodeId);
-	    if (me == null) {
-		me = new Node(nodeId);
-		nodesMap.put(nodeId, me);
-	    }
+	    if (me == null)
+		me = addNode(nodeId);
 
 	    //
 	    // The local Port info
@@ -123,13 +389,12 @@
 		if (! myPortVertex.getProperty("state").toString().equals("ACTIVE"))
 		    continue;
 
-		short myPort = 0;
+		int myPort = 0;
 		Object obj = myPortVertex.getProperty("number");
 		if (obj instanceof Short) {
 		    myPort = (Short)obj;
 		} else if (obj instanceof Integer) {
-		    Integer int_nodeId = (Integer)obj;
-		    myPort = int_nodeId.shortValue();
+		    myPort = (Integer)obj;
 		}
 
 		//
@@ -140,13 +405,12 @@
 		    if (! neighborPortVertex.getProperty("state").toString().equals("ACTIVE"))
 			continue;
 
-		    short neighborPort = 0;
+		    int neighborPort = 0;
 		    obj = neighborPortVertex.getProperty("number");
 		    if (obj instanceof Short) {
 			neighborPort = (Short)obj;
 		    } else if (obj instanceof Integer) {
-			Integer int_nodeId = (Integer)obj;
-			neighborPort = int_nodeId.shortValue();
+			neighborPort = (Integer)obj;
 		    }
 		    //
 		    // The neighbor Switch info
@@ -160,11 +424,9 @@
 			String neighborDpid = neighborVertex.getProperty("dpid").toString();
 			long neighborId = HexString.toLong(neighborDpid);
 			Node neighbor = nodesMap.get(neighborId);
-			if (neighbor == null) {
-			    neighbor = new Node(neighborId);
-			    nodesMap.put(neighborId, neighbor);
-			}
-			me.addNeighbor(neighbor, myPort, neighborPort);
+			if (neighbor == null)
+			    neighbor = addNode(neighborId);
+			me.addLink(myPort, neighbor, neighborPort);
 		    }
 		}
 	    }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyElement.java b/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyElement.java
new file mode 100644
index 0000000..fe84654
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyElement.java
@@ -0,0 +1,198 @@
+package net.onrc.onos.ofcontroller.topology;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Class for storing information about a Topology Element: Switch, Port or
+ * Link.
+ */
+public class TopologyElement {
+    /**
+     * The Element Type.
+     */
+    public enum Type {
+	ELEMENT_UNKNOWN,	// Unknown element
+	ELEMENT_SWITCH,		// Network Switch
+	ELEMENT_PORT,		// Switch Port
+	ELEMENT_LINK		// Unidirectional Link between Switch Ports
+    }
+
+    private Type elementType;		// The element type
+    private long fromSwitchDpid = 0;	// The Switch DPID
+    private int fromSwitchPort = 0;	// The Switch Port
+    private long toSwitchDpid = 0;	// The Neighbor Switch DPID
+    private int toSwitchPort = 0;	// The Neighbor Switch Port
+
+    // All (known) ports for a Switch
+    private Map<Integer, Integer> switchPorts = new TreeMap<Integer, Integer>();
+
+    /**
+     * Default constructor.
+     */
+    public TopologyElement() {
+	elementType = Type.ELEMENT_UNKNOWN;
+    }
+
+    /**
+     * Constructor to create a Topology Element for a Switch.
+     *
+     * @param switchDpid the Switch DPID.
+     */
+    public TopologyElement(long switchDpid) {
+	this.elementType = Type.ELEMENT_SWITCH;
+	this.fromSwitchDpid = switchDpid;
+    }
+
+    /**
+     * Constructor to create a Topology Element for a Switch Port.
+     *
+     * @param switchDpid the Switch DPID.
+     * @param switchPort the Switch Port.
+     */
+    public TopologyElement(long switchDpid, int switchPort) {
+	this.elementType = Type.ELEMENT_PORT;
+	this.fromSwitchDpid = switchDpid;
+	this.fromSwitchPort = switchPort;
+    }
+
+    /**
+     * Constructor to create a Topology Element for an unidirectional Link
+     * between Switch Ports.
+     *
+     * @param fromSwitchDpid the Switch DPID the Link begins from.
+     * @param fromSwitchPort the Switch Port the Link begins from.
+     * @param toSwitchDpid the Switch DPID the Link ends to.
+     * @param toSwitchPort the Switch Port the Link ends to.
+     */
+    public TopologyElement(long fromSwitchDpid, int fromSwitchPort,
+			   long toSwitchDpid, int toSwitchPort) {
+	this.elementType = Type.ELEMENT_LINK;
+	this.fromSwitchDpid = fromSwitchDpid;
+	this.fromSwitchPort = fromSwitchPort;
+	this.toSwitchDpid = toSwitchDpid;
+	this.toSwitchPort = toSwitchPort;
+    }
+
+    /**
+     * Get the Element type.
+     *
+     * @return the Element type.
+     */
+    public TopologyElement.Type getType() {
+	return elementType;
+    }
+
+    /**
+     * Get the Switch DPID.
+     *
+     * NOTE: Applies for Type.ELEMENT_SWITCH and Type.ELEMENT_PORT
+     *
+     * @return the Switch DPID.
+     */
+    public long getSwitch() {
+	return fromSwitchDpid;
+    }
+
+    /**
+     * Get the Switch Ports.
+     *
+     * NOTE: Applies for Type.ELEMENT_SWITCH
+     *
+     * @return the collection of Switch Ports.
+     */
+    public Map<Integer, Integer> getSwitchPorts() {
+	return switchPorts;
+    }
+
+    /**
+     * Add a Switch Port.
+     *
+     * NOTE: Applies for Type.ELEMENT_SWITCH
+     *
+     * @param switchPort the Switch Port to add.
+     */
+    public void addSwitchPort(int switchPort) {
+	switchPorts.put(switchPort, switchPort);
+    }
+
+    /**
+     * Get the Switch Port.
+     *
+     * NOTE: Applies for Type.ELEMENT_PORT
+     *
+     * @return the Switch Port.
+     */
+    public int getSwitchPort() {
+	return fromSwitchPort;
+    }
+
+    /**
+     * Get the Switch DPID the Link begins from.
+     *
+     * NOTE: Applies for Type.ELEMENT_LINK
+     */
+    public long getFromSwitch() {
+	return fromSwitchDpid;
+    }
+
+    /**
+     * Get the Switch Port the Link begins from.
+     *
+     * NOTE: Applies for Type.ELEMENT_LINK
+     */
+    public int getFromPort() {
+	return fromSwitchPort;
+    }
+
+    /**
+     * Get the Switch DPID the Link ends to.
+     *
+     * NOTE: Applies for Type.ELEMENT_LINK
+     */
+    public long getToSwitch() {
+	return toSwitchDpid;
+    }
+
+    /**
+     * Get the Switch Port the Link ends to.
+     *
+     * NOTE: Applies for Type.ELEMENT_LINK
+     */
+    public int getToPort() {
+	return toSwitchPort;
+    }
+
+    /**
+     * Get the Topology Element ID.
+     *
+     * The Topology Element ID has the following format:
+     *   - Switch: "Switch=<HexLongDpid>"
+     *     Example: "Switch=101"
+     *   - Switch Port: "Port=<HexLongDpid>/<IntPortId>"
+     *     Example: "Port=102/1"
+     *   - Link: "Link=<FromHexLongDpid>/<FromIntPortId>/<ToHexLongDpid>/<ToIntPortId>"
+     *     Example: "Link=101/2/103/4"
+     *
+     * NOTE: The Topology Element ID has no syntax meaning. It is used only to
+     * uniquely identify a topology element.
+     *
+     * @return the Topology Element ID.
+     */
+    public String elementId() {
+	switch (elementType) {
+	case ELEMENT_SWITCH:
+	    return "Switch=" + Long.toHexString(fromSwitchDpid);
+	case ELEMENT_PORT:
+	    return "Port=" +
+		Long.toHexString(fromSwitchDpid) + "/" + fromSwitchPort;
+	case ELEMENT_LINK:
+	    return "Link=" +
+		Long.toHexString(fromSwitchDpid) + "/" + fromSwitchPort + "/" +
+		Long.toHexString(toSwitchDpid) + "/" + toSwitchPort;
+	}
+
+	assert(false);
+	return null;
+    }
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyManager.java b/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyManager.java
index ccb64f8..ffe806a 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyManager.java
@@ -15,6 +15,9 @@
 import net.onrc.onos.graph.GraphDBOperation;
 import net.onrc.onos.ofcontroller.floodlightlistener.INetworkGraphService;
 import net.onrc.onos.ofcontroller.util.DataPath;
+import net.onrc.onos.ofcontroller.util.FlowEntry;
+import net.onrc.onos.ofcontroller.util.FlowPath;
+import net.onrc.onos.ofcontroller.util.Port;
 import net.onrc.onos.ofcontroller.util.SwitchPort;
 
 import org.slf4j.Logger;
@@ -202,6 +205,80 @@
     }
 
     /**
+     * Compute the network path for a Flow.
+     *
+     * @param topology the topology handler to use.
+     * @param flowPath the Flow to compute the network path for.
+     * @return the data path with the computed path if found, otherwise null.
+     */
+    public static DataPath computeNetworkPath(Topology topology,
+					      FlowPath flowPath) {
+	//
+	// Compute the network path based on the desired Flow Path type
+	//
+	switch (flowPath.flowPathType()) {
+	case FP_TYPE_SHORTEST_PATH: {
+	    SwitchPort src = flowPath.dataPath().srcPort();
+	    SwitchPort dest = flowPath.dataPath().dstPort();
+	    return ShortestPath.getTopologyShortestPath(topology, src, dest);
+	}
+	case FP_TYPE_EXPLICIT_PATH:
+	    return flowPath.dataPath();
+	}
+
+	return null;
+    }
+
+    /**
+     * Test whether two Flow Entries represent same points in a data path.
+     *
+     * NOTE: Two Flow Entries represent same points in a data path if
+     * the Switch DPID, incoming port and outgoing port are same.
+     *
+     * NOTE: This method is specialized for shortest-path unicast paths,
+     * and probably should be moved somewhere else.
+     *
+     * @param oldFlowEntry the first Flow Entry to compare.
+     * @param newFlowEntry the second Flow Entry to compare.
+     * @return true if the two Flow Entries represent same points in a
+     * data path, otherwise false.
+     */
+    public static boolean isSameFlowEntryDataPath(FlowEntry oldFlowEntry,
+						  FlowEntry newFlowEntry) {
+	// Test the DPID
+	if (oldFlowEntry.dpid().value() != newFlowEntry.dpid().value())
+	    return false;
+
+	// Test the inPort
+	do {
+	    Port oldPort = oldFlowEntry.inPort();
+	    Port newPort = newFlowEntry.inPort();
+	    if ((oldPort != null) && (newPort != null) &&
+		(oldPort.value() == newPort.value())) {
+		break;
+	    }
+	    if ((oldPort == null) && (newPort == null))
+		break;
+	    return false;		// inPort is different
+	} while (false);
+
+	// Test the outPort
+	do {
+	    Port oldPort = oldFlowEntry.outPort();
+	    Port newPort = newFlowEntry.outPort();
+	    if ((oldPort != null) && (newPort != null) &&
+		(oldPort.value() == newPort.value())) {
+		break;
+	    }
+	    if ((oldPort == null) && (newPort == null))
+		break;
+	    return false;		// outPort is different
+	} while (false);
+
+	return true;
+    }
+
+    /**
      * Get the shortest path from a source to a destination by
      * using the pre-populated local topology state prepared
      * by method @ref newDatabaseTopology().
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/EventEntry.java b/src/main/java/net/onrc/onos/ofcontroller/util/EventEntry.java
new file mode 100644
index 0000000..5b296e0
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/EventEntry.java
@@ -0,0 +1,64 @@
+package net.onrc.onos.ofcontroller.util;
+
+/**
+ * Class for encapsulating events with event-related data entry.
+ */
+public class EventEntry<T> {
+    /**
+     * The event types.
+     */
+    public enum Type {
+	ENTRY_ADD,			// Add or update an entry
+	ENTRY_REMOVE			// Remove an entry
+    }
+
+    private Type	eventType;	// The event type
+    private T		eventData;	// The relevant event data entry
+
+    /**
+     * Constructor for a given event type and event-related data entry.
+     *
+     * @param eventType the event type.
+     * @param eventData the event data entry.
+     */
+    public EventEntry(EventEntry.Type eventType, T eventData) {
+	this.eventType = eventType;
+	this.eventData = eventData;
+    }
+
+    /**
+     * Test whether the event type is ENTRY_ADD.
+     *
+     * @return true if the event type is ENTRY_ADD, otherwise false.
+     */
+    public boolean isAdd() {
+	return (this.eventType == Type.ENTRY_ADD);
+    }
+
+    /**
+     * Test whether the event type is ENTRY_REMOVE.
+     *
+     * @return true if the event type is ENTRY_REMOVE, otherwise false.
+     */
+    public boolean isRemove() {
+	return (this.eventType == Type.ENTRY_REMOVE);
+    }
+
+    /**
+     * Get the event type.
+     *
+     * @return the event type.
+     */
+    public EventEntry.Type eventType() {
+	return this.eventType;
+    }
+
+    /**
+     * Get the event-related data entry.
+     *
+     * @return the event-related data entry.
+     */
+    public T eventData() {
+	return this.eventData;
+    }
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntry.java b/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntry.java
index 762d272..15a6233 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntry.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntry.java
@@ -116,10 +116,11 @@
 
     /**
      * Get the Flow ID.
+     *
      * @return the Flow ID.
      */
     @JsonIgnore
-    public FlowId getFlowId() { return flowId; }
+    public FlowId flowId() { return flowId; }
 
     /**
      * Set the Flow ID.
@@ -131,6 +132,17 @@
     }
 
     /**
+     * Test whether the Flow ID is valid.
+     *
+     * @return true if the Flow ID is valid, otherwise false.
+     */
+    public boolean isValidFlowId() {
+	if (this.flowId == null)
+	    return false;
+	return (this.flowId.value() != 0);
+    }
+
+    /**
      * Get the Flow Entry ID.
      *
      * @return the Flow Entry ID.
@@ -149,6 +161,17 @@
     }
 
     /**
+     * Test whether the Flow Entry ID is valid.
+     *
+     * @return true if the Flow Entry ID is valid, otherwise false.
+     */
+    public boolean isValidFlowEntryId() {
+	if (this.flowEntryId == null)
+	    return false;
+	return (this.flowEntryId.value() != 0);
+    }
+
+    /**
      * Get the Flow Entry Match.
      *
      * @return the Flow Entry Match.
@@ -331,6 +354,9 @@
 	} else {
 		ret.append("[");
 	}
+	if ( flowId != null ) {
+		ret.append(" flowId=" + this.flowId.toString());
+	}
 	if ( flowEntryMatch != null ) {
 		ret.append(" flowEntryMatch=" + this.flowEntryMatch.toString());
 	}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntrySwitchState.java b/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntrySwitchState.java
index a69fdac..44439f2 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntrySwitchState.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntrySwitchState.java
@@ -4,9 +4,9 @@
  * The Flow Entry state as set by the controller.
  */
 public enum FlowEntrySwitchState {
-	FE_SWITCH_UNKNOWN,		// Initialization value: state unknown
-	FE_SWITCH_NOT_UPDATED,		// Switch not updated with this entry
-	FE_SWITCH_UPDATE_IN_PROGRESS,	// Switch update in progress
-	FE_SWITCH_UPDATED,		// Switch updated with this entry
-	FE_SWITCH_UPDATE_FAILED	// Error updating the switch with this entry
+    FE_SWITCH_UNKNOWN,			// Initialization value: state unknown
+    FE_SWITCH_NOT_UPDATED,		// Switch not updated with this entry
+    FE_SWITCH_UPDATE_IN_PROGRESS,	// Switch update in progress
+    FE_SWITCH_UPDATED,			// Switch updated with this entry
+    FE_SWITCH_UPDATE_FAILED	// Error updating the switch with this entry
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntryUserState.java b/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntryUserState.java
index e3b64f0..5ed8865 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntryUserState.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntryUserState.java
@@ -4,8 +4,8 @@
  * The Flow Entry state as set by the user (via the ONOS API).
  */
 public enum FlowEntryUserState {
-	FE_USER_UNKNOWN,		// Initialization value: state unknown
-	FE_USER_ADD,			// Flow entry that is added
-	FE_USER_MODIFY,			// Flow entry that is modified
-	FE_USER_DELETE			// Flow entry that is deleted
+    FE_USER_UNKNOWN,			// Initialization value: state unknown
+    FE_USER_ADD,			// Flow entry that is added
+    FE_USER_MODIFY,			// Flow entry that is modified
+    FE_USER_DELETE			// Flow entry that is deleted
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/FlowPath.java b/src/main/java/net/onrc/onos/ofcontroller/util/FlowPath.java
index a56dbff..a720fc6 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/FlowPath.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/FlowPath.java
@@ -1,9 +1,10 @@
 package net.onrc.onos.ofcontroller.util;
 
+import java.util.ArrayList;
+
 import net.floodlightcontroller.util.MACAddress;
 import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IFlowEntry;
 import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IFlowPath;
-import net.onrc.onos.ofcontroller.util.FlowPathFlags;
 
 import org.codehaus.jackson.annotate.JsonProperty;
 
@@ -13,6 +14,8 @@
 public class FlowPath implements Comparable<FlowPath> {
     private FlowId flowId;		// The Flow ID
     private CallerId installerId;	// The Caller ID of the path installer
+    private FlowPathType flowPathType;	// The Flow Path type
+    private FlowPathUserState flowPathUserState; // The Flow Path User state
     private FlowPathFlags flowPathFlags; // The Flow Path flags
     private DataPath dataPath;		// The data path
     private FlowEntryMatch flowEntryMatch; // Common Flow Entry Match for all
@@ -24,6 +27,8 @@
      * Default constructor.
      */
     public FlowPath() {
+	flowPathType = FlowPathType.FP_TYPE_UNKNOWN;
+	flowPathUserState = FlowPathUserState.FP_USER_UNKNOWN;
 	flowPathFlags = new FlowPathFlags();
 	dataPath = new DataPath();
 	flowEntryActions = new FlowEntryActions();
@@ -36,6 +41,8 @@
     	dataPath = new DataPath();
     	this.setFlowId(new FlowId(flowObj.getFlowId()));
     	this.setInstallerId(new CallerId(flowObj.getInstallerId()));
+	this.setFlowPathType(FlowPathType.valueOf(flowObj.getFlowPathType()));
+	this.setFlowPathUserState(FlowPathUserState.valueOf(flowObj.getFlowPathUserState()));
 	this.setFlowPathFlags(new FlowPathFlags(flowObj.getFlowPathFlags()));
     	this.dataPath().srcPort().setDpid(new Dpid(flowObj.getSrcSwitch()));
     	this.dataPath().srcPort().setPort(new Port(flowObj.getSrcPort()));
@@ -221,6 +228,42 @@
     }
 
     /**
+     * Get the flow path type.
+     *
+     * @return the flow path type.
+     */
+    @JsonProperty("flowPathType")
+    public FlowPathType flowPathType() { return flowPathType; }
+
+    /**
+     * Set the flow path type.
+     *
+     * @param flowPathType the flow path type to set.
+     */
+    @JsonProperty("flowPathType")
+    public void setFlowPathType(FlowPathType flowPathType) {
+	this.flowPathType = flowPathType;
+    }
+
+    /**
+     * Get the flow path user state.
+     *
+     * @return the flow path user state.
+     */
+    @JsonProperty("flowPathUserState")
+    public FlowPathUserState flowPathUserState() { return flowPathUserState; }
+
+    /**
+     * Set the flow path user state.
+     *
+     * @param flowPathUserState the flow path user state to set.
+     */
+    @JsonProperty("flowPathUserState")
+    public void setFlowPathUserState(FlowPathUserState flowPathUserState) {
+	this.flowPathUserState = flowPathUserState;
+    }
+
+    /**
      * Get the flow path flags.
      *
      * @return the flow path flags.
@@ -257,6 +300,15 @@
     }
 
     /**
+     * Get the data path flow entries.
+     *
+     * @return the data path flow entries.
+     */
+    public ArrayList<FlowEntry> flowEntries() {
+	return this.dataPath.flowEntries();
+    }
+
+    /**
      * Get the flow path's match conditions common for all Flow Entries.
      *
      * @return the flow path's match conditions common for all Flow Entries.
@@ -300,8 +352,9 @@
      * Convert the flow path to a string.
      *
      * The string has the following form:
-     *  [flowId=XXX installerId=XXX flowPathFlags=XXX dataPath=XXX
-     *   flowEntryMatch=XXX flowEntryActions=XXX]
+     *  [flowId=XXX installerId=XXX flowPathType = XXX flowPathUserState = XXX
+     *   flowPathFlags=XXX dataPath=XXX flowEntryMatch=XXX
+     *   flowEntryActions=XXX]
      *
      * @return the flow path as a string.
      */
@@ -309,6 +362,8 @@
     public String toString() {
 	String ret = "[flowId=" + this.flowId.toString();
 	ret += " installerId=" + this.installerId.toString();
+	ret += " flowPathType=" + this.flowPathType;
+	ret += " flowPathUserState=" + this.flowPathUserState;
 	ret += " flowPathFlags=" + this.flowPathFlags.toString();
 	if (dataPath != null)
 	    ret += " dataPath=" + this.dataPath.toString();
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/FlowPathType.java b/src/main/java/net/onrc/onos/ofcontroller/util/FlowPathType.java
new file mode 100644
index 0000000..87f2d98
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/FlowPathType.java
@@ -0,0 +1,10 @@
+package net.onrc.onos.ofcontroller.util;
+
+/**
+ * The Flow Path types.
+ */
+public enum FlowPathType {
+    FP_TYPE_UNKNOWN,			// Initialization value: state unknown
+    FP_TYPE_SHORTEST_PATH,		// Shortest path flow
+    FP_TYPE_EXPLICIT_PATH		// Flow path with explicit flow entries
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/FlowPathUserState.java b/src/main/java/net/onrc/onos/ofcontroller/util/FlowPathUserState.java
new file mode 100644
index 0000000..96b6345
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/FlowPathUserState.java
@@ -0,0 +1,11 @@
+package net.onrc.onos.ofcontroller.util;
+
+/**
+ * The Flow Path state as set by the user (via the ONOS API).
+ */
+public enum FlowPathUserState {
+    FP_USER_UNKNOWN,			// Initialization value: state unknown
+    FP_USER_ADD,			// Flow path that is added
+    FP_USER_MODIFY,			// Flow path that is modified
+    FP_USER_DELETE			// Flow path that is deleted
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/Switch.java b/src/main/java/net/onrc/onos/ofcontroller/util/Switch.java
index c989732..f7df223 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/Switch.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/Switch.java
@@ -7,6 +7,9 @@
  * NOTE: Currently this class is (almost) not used.
  */
 public class Switch {
+    /**
+     * The Switch state.
+     */
     public enum SwitchState {
 	INACTIVE,
 	ACTIVE,
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/serializers/KryoFactory.java b/src/main/java/net/onrc/onos/ofcontroller/util/serializers/KryoFactory.java
new file mode 100644
index 0000000..eeb307f
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/serializers/KryoFactory.java
@@ -0,0 +1,135 @@
+package net.onrc.onos.ofcontroller.util.serializers;
+
+import java.util.ArrayList;
+import java.util.TreeMap;
+
+import com.esotericsoftware.kryo2.Kryo;
+
+import net.floodlightcontroller.util.MACAddress;
+
+import net.onrc.onos.ofcontroller.util.*;
+import net.onrc.onos.ofcontroller.topology.TopologyElement;
+
+/**
+ * Class factory for allocating Kryo instances for
+ * serialization/deserialization of classes.
+ */
+public class KryoFactory {
+    private ArrayList<Kryo> kryoList = new ArrayList<Kryo>();
+
+    /**
+     * Default constructor.
+     */
+    public KryoFactory() {
+	Kryo kryo;
+	// Preallocate
+	for (int i = 0; i < 100; i++) {
+	    kryo = newKryoImpl();
+	    kryoList.add(kryo);
+	}
+    }
+
+    /**
+     * Create and initialize a new Kryo object.
+     *
+     * @return the created Kryo object.
+     */
+    public Kryo newKryo() {
+	return newDeleteKryo(null);
+    }
+
+    /**
+     * Delete an existing Kryo object.
+     *
+     * @param deleteKryo the object to delete.
+     */
+    public void deleteKryo(Kryo deleteKryo) {
+	newDeleteKryo(deleteKryo);
+    }
+
+    /**
+     * Create or delete a Kryo object.
+     *
+     * @param deleteKryo if null, then allocate and return a new object,
+     * otherwise delete the provided object.
+     * @return a new Kryo object if needed, otherwise null.
+     */
+    synchronized private Kryo newDeleteKryo(Kryo deleteKryo) {
+	if (deleteKryo != null) {
+	    // Delete an entry by moving it back to the buffer
+	    kryoList.add(deleteKryo);
+	    return null;
+	} else {
+	    Kryo kryo = null;
+	    if (kryoList.isEmpty()) {
+		// Preallocate
+		for (int i = 0; i < 100; i++) {
+		    kryo = newKryoImpl();
+		    kryoList.add(kryo);
+		}
+	    }
+
+	    kryo = kryoList.remove(kryoList.size() - 1);
+	    return kryo;
+	}
+    }
+
+    /**
+     * Create and initialize a new Kryo object.
+     *
+     * @return the created Kryo object.
+     */
+    private Kryo newKryoImpl() {
+	Kryo kryo = new Kryo();
+	kryo.setRegistrationRequired(true);
+	// kryo.setReferences(false);
+	//
+	kryo.register(ArrayList.class);
+
+	// FlowPath and related classes
+	kryo.register(CallerId.class);
+	kryo.register(DataPath.class);
+	kryo.register(DataPathEndpoints.class);
+	kryo.register(Dpid.class);
+	kryo.register(FlowEntryAction.class);
+	kryo.register(FlowEntryAction.ActionEnqueue.class);
+	kryo.register(FlowEntryAction.ActionOutput.class);
+	kryo.register(FlowEntryAction.ActionSetEthernetAddr.class);
+	kryo.register(FlowEntryAction.ActionSetIpToS.class);
+	kryo.register(FlowEntryAction.ActionSetIPv4Addr.class);
+	kryo.register(FlowEntryAction.ActionSetTcpUdpPort.class);
+	kryo.register(FlowEntryAction.ActionSetVlanId.class);
+	kryo.register(FlowEntryAction.ActionSetVlanPriority.class);
+	kryo.register(FlowEntryAction.ActionStripVlan.class);
+	kryo.register(FlowEntryAction.ActionValues.class);
+	kryo.register(FlowEntryActions.class);
+	kryo.register(FlowEntryErrorState.class);
+	kryo.register(FlowEntryId.class);
+	kryo.register(FlowEntry.class);
+	kryo.register(FlowEntryMatch.class);
+	kryo.register(FlowEntryMatch.Field.class);
+	kryo.register(FlowEntrySwitchState.class);
+	kryo.register(FlowEntryUserState.class);
+	kryo.register(FlowId.class);
+	kryo.register(FlowPath.class);
+	kryo.register(FlowPathFlags.class);
+	kryo.register(FlowPathType.class);
+	kryo.register(FlowPathUserState.class);
+	kryo.register(IPv4.class);
+	kryo.register(IPv4Net.class);
+	kryo.register(IPv6.class);
+	kryo.register(IPv6Net.class);
+	kryo.register(byte[].class);
+	kryo.register(MACAddress.class);
+	kryo.register(Port.class);
+	kryo.register(Switch.class);
+	kryo.register(SwitchPort.class);
+
+	// Topology-related classes
+	kryo.register(TopologyElement.class);
+	kryo.register(TopologyElement.Type.class);
+	kryo.register(TreeMap.class);
+
+	return kryo;
+    }
+}
diff --git a/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIFlowPathTest.java b/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIFlowPathTest.java
index bb0dbbf..9a1e34a 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIFlowPathTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIFlowPathTest.java
@@ -104,6 +104,42 @@
 
 	/**
 	 * Desc:
+	 *  Test method for get and set FlowPathType method.
+	 * Condition:
+	 *  N/A
+	 * Expect:
+	 * 1. Should set the Flow Path Type.
+	 * 2. Should get the Flow Path Type.
+	 */
+	@Test
+	public void testSetGetFlowPathType() {
+		String flowId = "xx";
+		String flowPathType = "FP_TYPE_SHORTEST_PATH";
+		flowPath.setFlowId(flowId);
+		flowPath.setFlowPathType(flowPathType);
+		assertEquals(flowPath.getFlowPathType(), flowPathType);
+	}
+
+	/**
+	 * Desc:
+	 *  Test method for get and set FlowPathUserState method.
+	 * Condition:
+	 *  N/A
+	 * Expect:
+	 * 1. Should set the Flow Path User State.
+	 * 2. Should get the Flow Path User State.
+	 */
+	@Test
+	public void testSetGetFlowPathUserState() {
+		String flowId = "xx";
+		String flowPathUserState = "FP_USER_ADD";
+		flowPath.setFlowId(flowId);
+		flowPath.setFlowPathUserState(flowPathUserState);
+		assertEquals(flowPath.getFlowPathUserState(), flowPathUserState);
+	}
+
+	/**
+	 * Desc:
 	 *  Test method for get and set FlowPathFlags method.
 	 * Condition:
 	 *  N/A
@@ -457,24 +493,6 @@
 
 	/**
 	 * Desc:
-	 *  Test method for set and get UserState.
-	 * Condition:
-	 *  N/A
-	 * Expect:
-	 * 1. Should set UserState.
-	 * 2. Should get UserState.
-	 */
-	@Test
-	public void testSetGetUserState() {
-		String flowId = "xx";
-		String userStatus = "Good";
-		flowPath.setFlowId(flowId);
-		flowPath.setUserState(userStatus);
-		assertEquals(flowPath.getUserState(), userStatus);
-	}
-	
-	/**
-	 * Desc:
 	 *  Test method for get Switches.
 	 * Condition:
 	 *  N/A
diff --git a/src/test/java/net/onrc/onos/ofcontroller/core/internal/TestableGraphDBOperation.java b/src/test/java/net/onrc/onos/ofcontroller/core/internal/TestableGraphDBOperation.java
index 385b49d..dfe6ccf 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/core/internal/TestableGraphDBOperation.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/core/internal/TestableGraphDBOperation.java
@@ -415,8 +415,10 @@
 	
 	public static class TestFlowPath implements IFlowPath {
 		private String state,type,flowId,installerId,srcSw,dstSw;
+		private String flowPathType;
+		private String flowPathUserState;
 		private Long flowPathFlags;
-		private String dataPathSummary,userState;
+		private String dataPathSummary;
 		private Short srcPort,dstPort;
 		private String matchSrcMac,matchDstMac;
 		private Short matchEthernetFrameType;
@@ -431,8 +433,10 @@
 		private List<ISwitchObject> switches;
 
 		private String stateToUpdate,typeToUpdate,flowIdToUpdate,installerIdToUpdate,srcSwToUpdate,dstSwToUpdate;
+		private String flowPathTypeToUpdate;
+		private String flowPathUserStateToUpdate;
 		private Long flowPathFlagsToUpdate;
-		private String dataPathSummaryToUpdate,userStateToUpdate;
+		private String dataPathSummaryToUpdate;
 		private Short srcPortToUpdate,dstPortToUpdate;
 		private String matchSrcMacToUpdate,matchDstMacToUpdate;
 		private Short matchEthernetFrameTypeToUpdate;
@@ -469,11 +473,12 @@
 			if(typeToUpdate != null) { type = typeToUpdate; }
 			if(flowIdToUpdate != null) { flowId = flowIdToUpdate; }
 			if(installerIdToUpdate != null) { installerId = installerIdToUpdate; }
+			if(flowPathTypeToUpdate != null) { flowPathType = flowPathTypeToUpdate; }
+			if(flowPathUserStateToUpdate != null) { flowPathUserState = flowPathUserStateToUpdate; }
 			if(flowPathFlagsToUpdate != null) { flowPathFlags = flowPathFlagsToUpdate; }
 			if(srcSwToUpdate != null) { srcSw = srcSwToUpdate; }
 			if(dstSwToUpdate != null) { dstSw = dstSwToUpdate; }
 			if(dataPathSummaryToUpdate != null) { dataPathSummary = dataPathSummaryToUpdate; }
-			if(userStateToUpdate != null) { userState = userStateToUpdate; }
 			if(srcPortToUpdate != null) { srcPort = srcPortToUpdate; }
 			if(dstPortToUpdate != null) { dstPort = dstPortToUpdate; }
 			if(matchSrcMacToUpdate != null) { matchSrcMac = matchSrcMacToUpdate; }
@@ -499,8 +504,10 @@
 			flowsToRemove.clear();
 			
 			stateToUpdate = typeToUpdate = flowIdToUpdate = installerIdToUpdate = null;
+			flowPathTypeToUpdate = null;
+			flowPathUserStateToUpdate = null;
 			flowPathFlagsToUpdate = null;
-			srcSwToUpdate = dstSwToUpdate = dataPathSummaryToUpdate = userStateToUpdate = null;
+			srcSwToUpdate = dstSwToUpdate = dataPathSummaryToUpdate = null;
 			srcPortToUpdate = dstPortToUpdate = null;
 			matchSrcMacToUpdate = matchDstMacToUpdate = null;
 			matchEthernetFrameTypeToUpdate = null;
@@ -517,11 +524,12 @@
 		public void setTypeForTest(String type) { this.type = type; }
 		public void setFlowIdForTest(String flowId) { this.flowId = flowId; }
 		public void setInstallerIdForTest(String installerId) { this.installerId = installerId; }
+		public void setFlowPathTypeForTest(String flowPathType) { this.flowPathType = flowPathType; }
+		public void setFlowPathUserStateForTest(String flowPathUserState) { this.flowPathUserState = flowPathUserState; }
 		public void setFlowPathFlagsForTest(Long flowPathFlags) { this.flowPathFlags = flowPathFlags; }
 		public void setSrcSwForTest(String srcSw) { this.srcSw = srcSw; }
 		public void setDstSwForTest(String dstSw) { this.dstSw = dstSw; }
 		public void setDataPathSummaryForTest(String dataPathSummary) { this.dataPathSummary = dataPathSummary; }
-		public void setUserStateForTest(String userState) { this.userState = userState; }
 		public void setSrcPortForTest(Short srcPort) { this.srcPort = srcPort; }
 		public void setDstPortForTest(Short dstPort) { this.dstPort = dstPort; }
 		public void setMatchSrcMacForTest(String matchSrcMac) { this.matchSrcMac = matchSrcMac; }
@@ -570,6 +578,18 @@
 		public void setInstallerId(String installerId) { installerIdToUpdate = installerId; }
 
 		@Override
+		public String getFlowPathType() { return flowPathType; }
+
+		@Override
+		public void setFlowPathType(String flowPathType) { flowPathTypeToUpdate = flowPathType; }
+
+		@Override
+		public String getFlowPathUserState() { return flowPathUserState; }
+
+		@Override
+		public void setFlowPathUserState(String flowPathUserState) { flowPathUserStateToUpdate = flowPathUserState; }
+
+		@Override
 		public Long getFlowPathFlags() { return flowPathFlags; }
 
 		@Override
@@ -706,12 +726,6 @@
 
 		@Override
 		public Iterable<ISwitchObject> getSwitches() { return switches; }
-
-		@Override
-		public String getUserState() { return userState; }
-
-		@Override
-		public void setUserState(String userState) { userStateToUpdate = userState; }
 	}
 
 	public static class TestFlowEntry implements IFlowEntry {
diff --git a/src/test/java/net/onrc/onos/ofcontroller/flowmanager/FlowManagerTest.java b/src/test/java/net/onrc/onos/ofcontroller/flowmanager/FlowManagerTest.java
index 8a8779b..7fd0f67 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/flowmanager/FlowManagerTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/flowmanager/FlowManagerTest.java
@@ -5,6 +5,7 @@
 import static org.easymock.EasyMock.cmpEq;
 import static org.powermock.api.easymock.PowerMock.*;
 
+import java.lang.reflect.Method;
 import java.util.*;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
@@ -87,11 +88,14 @@
 	}
 	
 	private IFlowPath createIFlowPathMock(long flowId, String installerID,
-			long flowPathFlags,
-			long srcDpid, int srcPort, long dstDpid, int dstPort) {
+			String flowPathType, String flowPathUserState,
+			long flowPathFlags, long srcDpid, int srcPort,
+			long dstDpid, int dstPort) {
 		IFlowPath iFlowPath = createNiceMock(IFlowPath.class);
 		expect(iFlowPath.getFlowId()).andReturn(new FlowId(flowId).toString()).anyTimes();
 		expect(iFlowPath.getInstallerId()).andReturn(installerID).anyTimes();
+		expect(iFlowPath.getFlowPathType()).andReturn(flowPathType).anyTimes();
+		expect(iFlowPath.getFlowPathUserState()).andReturn(flowPathUserState).anyTimes();
 		expect(iFlowPath.getFlowPathFlags()).andReturn(new Long(flowPathFlags)).anyTimes();
 		expect(iFlowPath.getSrcSwitch()).andReturn(new Dpid(srcDpid).toString()).anyTimes();
 		expect(iFlowPath.getSrcPort()).andReturn(new Short((short)srcPort)).anyTimes();
@@ -101,13 +105,16 @@
 	}
 	
 	private FlowPath createTestFlowPath(long flowId, String installerId,
+			String flowPathType, String flowPathUserState,
 			final long flowPathFlags,
 			final long srcDpid, final int srcPort,
-			final long dstDpid, final int dstPort			
+			final long dstDpid, final int dstPort
 			) {
 		FlowPath flowPath = new FlowPath();
 		flowPath.setFlowId(new FlowId(flowId));
 		flowPath.setInstallerId(new CallerId(installerId));
+		flowPath.setFlowPathType(FlowPathType.valueOf(flowPathType));
+		flowPath.setFlowPathUserState(FlowPathUserState.valueOf(flowPathUserState));
 		flowPath.setFlowPathFlags(new FlowPathFlags(flowPathFlags));
 		flowPath.setDataPath(new DataPath() {{
 			setSrcPort(new SwitchPort(new Dpid(srcDpid), new Port((short)srcPort)));
@@ -118,9 +125,9 @@
 	}
 	
 	private ArrayList<FlowPath> createTestFlowPaths() {
-		FlowPath flowPath1 = createTestFlowPath(1, "foo caller id", 0, 1, 1, 2, 2); 
-		FlowPath flowPath2 = createTestFlowPath(2, "caller id", 0, 1, 1, 2, 2); 
-		FlowPath flowPath3 = createTestFlowPath(3, "caller id", 0, 1, 5, 2, 2); 
+		FlowPath flowPath1 = createTestFlowPath(1, "foo caller id", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 1, 1, 2, 2); 
+		FlowPath flowPath2 = createTestFlowPath(2, "caller id", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 1, 1, 2, 2); 
+		FlowPath flowPath3 = createTestFlowPath(3, "caller id", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 1, 5, 2, 2); 
 
 		ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
 		flowPaths.add(flowPath1);
@@ -156,7 +163,7 @@
 		replayAll();
 
 		fm.init(context);
-		Boolean result = fm.addFlow(flowPath, flowId, "");
+		Boolean result = fm.addFlow(flowPath, flowId);
 
 		// verify the test
 		verifyAll();
@@ -164,7 +171,7 @@
 	}
 
 	/**
-	 * Test method for {@link FlowManager#addFlow(FlowPath, FlowId, String)}.
+	 * Test method for {@link FlowManager#addFlow(FlowPath, FlowId)}.
 	 * @throws Exception 
 	 */
 	@Test
@@ -193,6 +200,8 @@
 		FlowPath flowPath = new FlowPath();
 		flowPath.setFlowId(new FlowId(0x100));
 		flowPath.setInstallerId(new CallerId("installer id"));
+		flowPath.setFlowPathType(FlowPathType.valueOf("FP_TYPE_SHORTEST_PATH"));
+		flowPath.setFlowPathUserState(FlowPathUserState.valueOf("FP_USER_ADD"));
 		flowPath.setFlowPathFlags(new FlowPathFlags(0));
 		flowPath.setDataPath(dataPath);
 		flowPath.setFlowEntryMatch(match);
@@ -204,13 +213,14 @@
 		createdFlowPath.setFlowId("0x100");
 		createdFlowPath.setType("flow");
 		createdFlowPath.setInstallerId("installer id");
+		createdFlowPath.setFlowPathType("FP_TYPE_SHORTEST_PATH");
+		createdFlowPath.setFlowPathUserState("FP_USER_ADD");
 		createdFlowPath.setFlowPathFlags(new Long((long)0));
 		createdFlowPath.setSrcSwitch("00:00:00:00:00:00:12:34");
 		createdFlowPath.setSrcPort(new Short((short)1));
 		createdFlowPath.setDstSwitch("00:00:00:00:00:00:56:78");
 		createdFlowPath.setDstPort(new Short((short)2));
 		createdFlowPath.setDataPathSummary("data path summary");
-		createdFlowPath.setUserState("FE_USER_ADD");
 		
 		expectPrivate(fm, addFlowEntry, createdFlowPath, flowEntry1)
 			.andReturn(createdFlowEntry1);
@@ -223,7 +233,7 @@
 		replayAll();
 		
 		fm.init(context);
-		Boolean result = fm.addFlow(flowPath, new FlowId(0x100), "data path summary");
+		Boolean result = fm.addFlow(flowPath, new FlowId(0x100));
 
 		// verify the test
 		verifyAll();
@@ -352,7 +362,7 @@
 	public final void testGetFlowSuccessNormally() throws Exception {
 		// instantiate required objects
 		FlowManager fm = new FlowManager();
-		IFlowPath iFlowPath = createIFlowPathMock(1, "caller id", 0, 1, 1, 2, 2); 
+		IFlowPath iFlowPath = createIFlowPathMock(1, "caller id", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 1, 1, 2, 2); 
 
 		// setup expectations
 		expectInitWithContext();
@@ -366,11 +376,15 @@
 		fm.init(context);
 		FlowPath flowPath = fm.getFlow(new FlowId(1));
 		String installerId = flowPath.installerId().toString();
+		String flowPathType = flowPath.flowPathType().toString();
+		String flowPathUserState = flowPath.flowPathUserState().toString();
 		long flowPathFlags = flowPath.flowPathFlags().flags();
 		
 		//verify the test
 		verifyAll();
 		assertEquals("caller id", installerId);
+		assertEquals("FP_TYPE_SHORTEST_PATH", flowPathType);
+		assertEquals("FP_USER_ADD", flowPathUserState);
 		assertEquals(0L, flowPathFlags);
 	}
 	
@@ -453,9 +467,9 @@
 		final String getAllFlowsWithoutFlowEntries = "getAllFlowsWithoutFlowEntries";
 		// create mock objects
 		FlowManager fm = createPartialMockAndInvokeDefaultConstructor(FlowManager.class, getAllFlowsWithoutFlowEntries);
-		IFlowPath flowPath1 = createIFlowPathMock(1, "", 0, 1, 2, 3, 4);
-		IFlowPath flowPath2 = createIFlowPathMock(5, "", 0, 2, 3, 4, 5);
-		IFlowPath flowPath3 = createIFlowPathMock(10, "", 0, 3, 4, 5, 6);
+		IFlowPath flowPath1 = createIFlowPathMock(1, "", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 1, 2, 3, 4);
+		IFlowPath flowPath2 = createIFlowPathMock(5, "", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 2, 3, 4, 5);
+		IFlowPath flowPath3 = createIFlowPathMock(10, "", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 3, 4, 5, 6);
 
 		// instantiate required objects
 		ArrayList<IFlowPath> flows = new ArrayList<IFlowPath>();
@@ -488,8 +502,8 @@
 	@Test
 	public final void testGetAllFlowsSuccessNormally() throws Exception {
 		// create mock objects
-		IFlowPath iFlowPath1 = createIFlowPathMock(1, "caller id", 0, 1, 1, 2, 2); 
-		IFlowPath iFlowPath2 = createIFlowPathMock(2, "caller id", 0, 2, 5, 3, 5);
+		IFlowPath iFlowPath1 = createIFlowPathMock(1, "caller id", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 1, 1, 2, 2); 
+		IFlowPath iFlowPath2 = createIFlowPathMock(2, "caller id", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 2, 5, 3, 5);
 		
 		// instantiate required objects
 		ArrayList<IFlowPath> flowPaths = new ArrayList<IFlowPath>();
@@ -540,6 +554,8 @@
 		FlowPath paramFlow = new FlowPath();
 		paramFlow.setFlowId(new FlowId(100));
 		paramFlow.setInstallerId(new CallerId("installer id"));
+		paramFlow.setFlowPathType(FlowPathType.valueOf("FP_TYPE_SHORTEST_PATH"));
+		paramFlow.setFlowPathUserState(FlowPathUserState.valueOf("FP_USER_ADD"));
 		paramFlow.setFlowPathFlags(new FlowPathFlags(0));
 		paramFlow.setDataPath(dataPath);
 		paramFlow.setFlowEntryMatch(match);
@@ -555,6 +571,8 @@
 						FlowPath flowPath = (FlowPath)EasyMock.getCurrentArguments()[0];
 						assertEquals(flowPath.flowId().value(), 100);
 						assertEquals(flowPath.installerId().toString(), "installer id");
+						assertEquals(flowPath.flowPathType().toString(), "PF_TYPE_SHORTEST_PATH");
+						assertEquals(flowPath.flowPathUserState().toString(), "PF_USER_STATE");
 						assertEquals(flowPath.flowPathFlags().flags(), 0);
 						assertEquals(flowPath.dataPath().srcPort().toString(),
 								new SwitchPort(new Dpid(1), new Port((short)3)).toString());
@@ -576,6 +594,8 @@
 		verifyAll();
 		assertEquals(paramFlow.flowId().value(), resultFlow.flowId().value());
 		assertEquals(paramFlow.installerId().toString(), resultFlow.installerId().toString());
+		assertEquals(paramFlow.flowPathType().toString(), resultFlow.flowPathType().toString());
+		assertEquals(paramFlow.flowPathUserState().toString(), resultFlow.flowPathUserState().toString());
 		assertEquals(paramFlow.flowPathFlags().flags(), resultFlow.flowPathFlags().flags());
 		assertEquals(paramFlow.dataPath().toString(), resultFlow.dataPath().toString());
 		assertEquals(paramFlow.flowEntryMatch().toString(), resultFlow.flowEntryMatch().toString());
@@ -774,7 +794,7 @@
 	@Test
 	public final void testClearFlowSuccessNormally() throws Exception {
 		// create mock objects
-		IFlowPath flowPath = createIFlowPathMock(123, "id", 0, 1, 2, 3, 4);
+		IFlowPath flowPath = createIFlowPathMock(123, "id", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 1, 2, 3, 4);
 		IFlowEntry flowEntry1 = createMock(IFlowEntry.class);
 		IFlowEntry flowEntry2 = createMock(IFlowEntry.class);
 		IFlowEntry flowEntry3 = createMock(IFlowEntry.class);
@@ -817,8 +837,8 @@
 	@Test
 	public final void testGetAllFlowsWithoutFlowEntriesSuccessNormally() throws Exception {
 		// create mock objects
-		IFlowPath iFlowPath1 = createIFlowPathMock(1, "caller id", 0, 1, 1, 2, 2); 
-		IFlowPath iFlowPath2 = createIFlowPathMock(2, "caller id", 0, 2, 5, 3, 5);
+		IFlowPath iFlowPath1 = createIFlowPathMock(1, "caller id", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 1, 1, 2, 2); 
+		IFlowPath iFlowPath2 = createIFlowPathMock(2, "caller id", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 2, 5, 3, 5);
 		
 		// instantiate required objects
 		ArrayList<IFlowPath> flowPaths = new ArrayList<IFlowPath>();
@@ -854,7 +874,7 @@
 		final String addFlowEntry = "addFlowEntry";
 		
 		// create mock objects
-		IFlowPath iFlowPath1 = createIFlowPathMock(1, "caller id", 0, 1, 1, 2, 2);
+		IFlowPath iFlowPath1 = createIFlowPathMock(1, "caller id", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 1, 1, 2, 2);
 		IFlowEntry iFlowEntry1 = createMock(IFlowEntry.class);
 		IFlowEntry iFlowEntry2 = createMock(IFlowEntry.class);
 		FlowManager fm = createPartialMockAndInvokeDefaultConstructor(FlowManager.class, addFlowEntry);
@@ -904,7 +924,15 @@
 		replayAll();
 		
 		fm.init(context);
-		Boolean result = fm.reconcileFlow(iFlowPath1, dataPath);
+		// Use reflection to test the private method
+		// Boolean result = fm.reconcileFlow(iFlowPath1, dataPath);
+		Class fmClass = FlowManager.class;
+		Method method = fmClass.getDeclaredMethod(
+			"reconcileFlow",
+			new Class[] { IFlowPath.class, DataPath.class });
+		method.setAccessible(true);
+		Boolean result = (Boolean)method.invoke(fm,
+			new Object[] { iFlowPath1, dataPath });
 		
 		// verify the test
 		verifyAll();
@@ -920,7 +948,7 @@
 	public final void testInstallFlowEntryWithIFlowPathSuccessNormally() throws Exception {
 		// create mock object
 		IOFSwitch iofSwitch = createNiceMock(IOFSwitch.class);
-		IFlowPath iFlowPath = createIFlowPathMock(1, "id", 0, 1, 2, 3, 4); 
+		IFlowPath iFlowPath = createIFlowPathMock(1, "id", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 1, 2, 3, 4); 
 		IFlowEntry iFlowEntry = createMock(IFlowEntry.class);
 		BasicFactory basicFactory = createMock(BasicFactory.class);
 		
@@ -958,7 +986,16 @@
 		replayAll();
 		
 		fm.init(context);
-		Boolean result = fm.installFlowEntry(iofSwitch, iFlowPath, iFlowEntry);
+		// Use reflection to test the private method
+		// Boolean result = fm.installFlowEntry(iofSwitch, iFlowPath, iFlowEntry);
+		Class fmClass = FlowManager.class;
+		Method method = fmClass.getDeclaredMethod(
+			"installFlowEntry",
+			new Class[] { IOFSwitch.class, IFlowPath.class, IFlowEntry.class });
+		method.setAccessible(true);
+		Boolean result = (Boolean)method.invoke(fm,
+			new Object[] { iofSwitch, iFlowPath, iFlowEntry });
+
 		
 		// verify the test
 		verifyAll();
diff --git a/src/test/java/net/onrc/onos/ofcontroller/util/FlowEntryTest.java b/src/test/java/net/onrc/onos/ofcontroller/util/FlowEntryTest.java
index 1d193c4..fc17178 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/util/FlowEntryTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/util/FlowEntryTest.java
@@ -144,8 +144,25 @@
 	}
 
 	@Test
-	public void testGetFlowId(){
-		assertEquals("flowId", flowId, entry.getFlowId() );
+	public void testFlowId(){
+		assertEquals("flowId", flowId, entry.flowId() );
+	}
+
+	@Test
+	public void testIsValidFlowId(){
+		FlowEntry e = new FlowEntry();
+
+		// Test a Flow Entry with empty Flow ID
+		assertEquals("isValidFlowId", false, e.isValidFlowId() );
+
+		// Test a Flow Entry with invalid Flow ID
+		e.setFlowId(new FlowId());
+		assertEquals("isValidFlowId", false, e.isValidFlowId() );
+
+		// Test a Flow Entry with valid Flow ID
+		e.setFlowId(new FlowId(0x1));
+		assertEquals("isValidFlowId", true, e.isValidFlowId() );
+		assertEquals("isValidFlowId", true, entry.isValidFlowId() );
 	}
 
 	@Test
@@ -154,6 +171,23 @@
 	}
 
 	@Test
+	public void testIsValidFlowEntryId(){
+		FlowEntry e = new FlowEntry();
+
+		// Test a Flow Entry with empty Flow Entry ID
+		assertEquals("isValidFlowEntryId", false, e.isValidFlowEntryId() );
+
+		// Test a Flow Entry with invalid Flow Entry ID
+		e.setFlowEntryId(new FlowEntryId());
+		assertEquals("isValidFlowEntryId", false, e.isValidFlowEntryId() );
+
+		// Test a Flow Entry with valid Flow Entry ID
+		e.setFlowEntryId(new FlowEntryId(0x1));
+		assertEquals("isValidFlowEntryId", true, e.isValidFlowEntryId() );
+		assertEquals("isValidFlowEntryId", true, entry.isValidFlowEntryId() );
+	}
+
+	@Test
 	public void testFlowEntryMatch(){
 		assertEquals("flowEntryMatch", match, entry.flowEntryMatch() );
 	}
@@ -203,8 +237,8 @@
 	@Test
 	public void testToString(){
 		FlowEntry def = new FlowEntry();
-		assertEquals( def.toString(), "[ flowEntryActions=[] flowEntryUserState=FE_USER_UNKNOWN flowEntrySwitchState=FE_SWITCH_UNKNOWN]" );
-		assertEquals( entry.toString(), "[flowEntryId=0x5678 flowEntryMatch=[inPort=1 srcMac=01:02:03:04:05:06 dstMac=06:05:04:03:02:01 ethernetFrameType=2 vlanId=3 vlanPriority=4 srcIPv4Net=127.0.0.1/32 dstIPv4Net=127.0.0.2/32 ipProto=5 ipToS=6 srcTcpUdpPort=7 dstTcpUdpPort=8] flowEntryActions=[[type=ACTION_OUTPUT action=[port=9 maxLen=0]];[type=ACTION_OUTPUT action=[port=-3 maxLen=0]];[type=ACTION_SET_VLAN_VID action=[vlanId=3]];[type=ACTION_SET_VLAN_PCP action=[vlanPriority=4]];[type=ACTION_STRIP_VLAN action=[stripVlan=true]];[type=ACTION_SET_DL_SRC action=[addr=01:02:03:04:05:06]];[type=ACTION_SET_DL_DST action=[addr=06:05:04:03:02:01]];[type=ACTION_SET_NW_SRC action=[addr=127.0.0.3]];[type=ACTION_SET_NW_DST action=[addr=127.0.0.4]];[type=ACTION_SET_NW_TOS action=[ipToS=6]];[type=ACTION_SET_TP_SRC action=[port=7]];[type=ACTION_SET_TP_DST action=[port=8]];[type=ACTION_ENQUEUE action=[port=10 queueId=11]];] dpid=00:00:00:00:00:00:ca:fe inPort=1 outPort=9 flowEntryUserState=FE_USER_ADD flowEntrySwitchState=FE_SWITCH_UPDATED flowEntryErrorState=[type=12 code=13]]" );
+		assertEquals("toString", def.toString(), "[ flowEntryActions=[] flowEntryUserState=FE_USER_UNKNOWN flowEntrySwitchState=FE_SWITCH_UNKNOWN]" );
+		assertEquals("toString", entry.toString(), "[flowEntryId=0x5678 flowId=0x1234 flowEntryMatch=[inPort=1 srcMac=01:02:03:04:05:06 dstMac=06:05:04:03:02:01 ethernetFrameType=2 vlanId=3 vlanPriority=4 srcIPv4Net=127.0.0.1/32 dstIPv4Net=127.0.0.2/32 ipProto=5 ipToS=6 srcTcpUdpPort=7 dstTcpUdpPort=8] flowEntryActions=[[type=ACTION_OUTPUT action=[port=9 maxLen=0]];[type=ACTION_OUTPUT action=[port=-3 maxLen=0]];[type=ACTION_SET_VLAN_VID action=[vlanId=3]];[type=ACTION_SET_VLAN_PCP action=[vlanPriority=4]];[type=ACTION_STRIP_VLAN action=[stripVlan=true]];[type=ACTION_SET_DL_SRC action=[addr=01:02:03:04:05:06]];[type=ACTION_SET_DL_DST action=[addr=06:05:04:03:02:01]];[type=ACTION_SET_NW_SRC action=[addr=127.0.0.3]];[type=ACTION_SET_NW_DST action=[addr=127.0.0.4]];[type=ACTION_SET_NW_TOS action=[ipToS=6]];[type=ACTION_SET_TP_SRC action=[port=7]];[type=ACTION_SET_TP_DST action=[port=8]];[type=ACTION_ENQUEUE action=[port=10 queueId=11]];] dpid=00:00:00:00:00:00:ca:fe inPort=1 outPort=9 flowEntryUserState=FE_USER_ADD flowEntrySwitchState=FE_SWITCH_UPDATED flowEntryErrorState=[type=12 code=13]]" );
 	}
 
 }
diff --git a/src/test/java/net/onrc/onos/ofcontroller/util/FlowPathTest.java b/src/test/java/net/onrc/onos/ofcontroller/util/FlowPathTest.java
index 89a12e5..bd42ac8 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/util/FlowPathTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/util/FlowPathTest.java
@@ -16,6 +16,8 @@
 		TestFlowPath iFlowPath = new TestFlowPath();
 		iFlowPath.setFlowIdForTest("0x1234");
 		iFlowPath.setInstallerIdForTest("installerId");
+		iFlowPath.setFlowPathTypeForTest("FP_TYPE_SHORTEST_PATH");
+		iFlowPath.setFlowPathUserStateForTest("FP_USER_ADD");
 		iFlowPath.setFlowPathFlagsForTest(0L);
 		iFlowPath.setSrcSwForTest("CA:FE");
 		iFlowPath.setSrcPortForTest((short)1);
@@ -38,6 +40,8 @@
 	@Test
 	public void testFlowPath(){
 		FlowPath flowPath = new FlowPath();
+		assertTrue ( flowPath.flowPathType() == FlowPathType.FP_TYPE_UNKNOWN);
+		assertTrue ( flowPath.flowPathUserState() == FlowPathUserState.FP_USER_UNKNOWN);
 		assertFalse( flowPath.flowPathFlags().isDiscardFirstHopEntry() );
 		assertFalse( flowPath.flowPathFlags().isKeepOnlyFirstHopEntry() );
 		assertTrue( flowPath.flowEntryActions().isEmpty() );
@@ -48,6 +52,8 @@
 		TestFlowPath iFlowPath = new TestFlowPath();
 		iFlowPath.setFlowIdForTest("0x1234");
 		iFlowPath.setInstallerIdForTest("installerId");
+		iFlowPath.setFlowPathTypeForTest("FP_TYPE_SHORTEST_PATH");
+		iFlowPath.setFlowPathUserStateForTest("FP_USER_ADD");
 		iFlowPath.setFlowPathFlagsForTest(0L);
 		iFlowPath.setSrcSwForTest("CA:FE");
 		iFlowPath.setSrcPortForTest((short)1);
@@ -91,6 +97,8 @@
 		FlowPath flowPath = new FlowPath(iFlowPath);
 		assertEquals(flowPath.flowId().value(), 0x1234);
 		assertEquals(flowPath.installerId().value(), "installerId");
+		assertEquals(flowPath.flowPathType(), FlowPathType.FP_TYPE_SHORTEST_PATH);
+		assertEquals(flowPath.flowPathUserState(), FlowPathUserState.FP_USER_ADD);
 		assertEquals(flowPath.flowPathFlags().flags(), 0);
 		assertEquals(flowPath.dataPath().srcPort().dpid().value(), 0xCAFE);
 		assertEquals(flowPath.dataPath().srcPort().port().value(), 1);
@@ -134,6 +142,21 @@
 		assertEquals("FE_SWITCH_UPDATE_IN_PROGRESS", flowPath.dataPath().flowEntries().get(0).flowEntrySwitchState().toString());
 	}
 
+	@Test
+	public void testSetFlowPathType(){
+		FlowPath flowPath = new FlowPath();
+		FlowPathType type = FlowPathType.FP_TYPE_SHORTEST_PATH;
+		flowPath.setFlowPathType( type );
+		assertTrue( flowPath.flowPathType() == FlowPathType.FP_TYPE_SHORTEST_PATH );
+	}
+
+	@Test
+	public void testSetFlowPathUserState(){
+		FlowPath flowPath = new FlowPath();
+		FlowPathUserState state = FlowPathUserState.FP_USER_ADD;
+		flowPath.setFlowPathUserState( state );
+		assertTrue( flowPath.flowPathUserState() == FlowPathUserState.FP_USER_ADD );
+	}
 
 	@Test
 	public void testFlowPathFlags(){
@@ -166,7 +189,7 @@
 	@Test
 	public void testToString(){
 
-		assertEquals("[flowId=0x1234 installerId=installerId flowPathFlags=[flags=] dataPath=[src=00:00:00:00:00:00:ca:fe/1 flowEntry=[flowEntryId=0x14 flowEntryMatch=[] flowEntryActions=[[type=ACTION_OUTPUT action=[port=23 maxLen=24]];[type=ACTION_OUTPUT action=[port=25 maxLen=26]];] dpid=00:00:00:00:00:00:be:ef flowEntryUserState=FE_USER_MODIFY flowEntrySwitchState=FE_SWITCH_UPDATE_IN_PROGRESS] dst=00:00:00:00:00:00:ba:be/2] flowEntryMatch=[] flowEntryActions=[[type=ACTION_OUTPUT action=[port=10 maxLen=11]];[type=ACTION_OUTPUT action=[port=12 maxLen=13]];]]", flowPath.toString());
+		assertEquals("[flowId=0x1234 installerId=installerId flowPathType=FP_TYPE_SHORTEST_PATH flowPathUserState=FP_USER_ADD flowPathFlags=[flags=] dataPath=[src=00:00:00:00:00:00:ca:fe/1 flowEntry=[flowEntryId=0x14 flowEntryMatch=[] flowEntryActions=[[type=ACTION_OUTPUT action=[port=23 maxLen=24]];[type=ACTION_OUTPUT action=[port=25 maxLen=26]];] dpid=00:00:00:00:00:00:be:ef flowEntryUserState=FE_USER_MODIFY flowEntrySwitchState=FE_SWITCH_UPDATE_IN_PROGRESS] dst=00:00:00:00:00:00:ba:be/2] flowEntryMatch=[] flowEntryActions=[[type=ACTION_OUTPUT action=[port=10 maxLen=11]];[type=ACTION_OUTPUT action=[port=12 maxLen=13]];]]", flowPath.toString());
 	}
 
 	@Test
diff --git a/web/add_flow.py b/web/add_flow.py
index 8100f22..6b9d5d0 100755
--- a/web/add_flow.py
+++ b/web/add_flow.py
@@ -352,6 +352,9 @@
   flow_path = {}
   flow_path['flowId'] = flow_id
   flow_path['installerId'] = installer_id
+  # NOTE: The 'flowPathType' might be rewritten later
+  flow_path['flowPathType'] = 'FP_TYPE_EXPLICIT_PATH'
+  flow_path['flowPathUserState'] = 'FP_USER_ADD'
   flow_path['flowPathFlags'] = flowPathFlags
 
   if (len(match) > 0):
@@ -440,6 +443,8 @@
     parsed_args[idx]['actionOutputEnabled'] = False
 
     flow_path = compute_flow_path(parsed_args[idx], data_path)
+    flow_path['flowPathType'] = 'FP_TYPE_SHORTEST_PATH'
+
     add_shortest_path_flow(flow_path)
 
     idx = idx + 1
diff --git a/web/get_flow.py b/web/get_flow.py
index 9ab55da..c45d853 100755
--- a/web/get_flow.py
+++ b/web/get_flow.py
@@ -161,6 +161,8 @@
 def print_flow_path(parsedResult):
   flowId = parsedResult['flowId']['value']
   installerId = parsedResult['installerId']['value']
+  flowPathType = parsedResult['flowPathType']
+  flowPathUserState = parsedResult['flowPathUserState']
   flowPathFlags = parsedResult['flowPathFlags']['flags']
   srcSwitch = parsedResult['dataPath']['srcPort']['dpid']['value']
   srcPort = parsedResult['dataPath']['srcPort']['port']['value']
@@ -179,7 +181,7 @@
       flowPathFlagsStr += ","
     flowPathFlagsStr += "KEEP_ONLY_FIRST_HOP_ENTRY"
 
-  print "FlowPath: (flowId = %s installerId = %s flowPathFlags = 0x%x(%s) src = %s/%s dst = %s/%s)" % (flowId, installerId, flowPathFlags, flowPathFlagsStr, srcSwitch, srcPort, dstSwitch, dstPort)
+  print "FlowPath: (flowId = %s installerId = %s flowPathType = %s flowPathUserState = %s flowPathFlags = 0x%x(%s) src = %s/%s dst = %s/%s)" % (flowId, installerId, flowPathType, flowPathUserState, flowPathFlags, flowPathFlagsStr, srcSwitch, srcPort, dstSwitch, dstPort)
 
   #
   # Print the common match conditions