Merge pull request #26 from effy/RAMCloud

Flow path batch writing using addVertices(), addEdges() and setProperties() of RAMCloudGraph
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/ApplnObjectType.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/ApplnObjectType.java
new file mode 100644
index 0000000..8cb1316
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/ApplnObjectType.java
@@ -0,0 +1,16 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package net.onrc.onos.ofcontroller.flowmanager;
+
+/**
+ *
+ * @author nickkaranatsios
+ */
+public enum ApplnObjectType {
+    FLOWPATH,
+    FLOWENTRY,  
+    UNKNOWN
+}
+
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/DBOperationType.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/DBOperationType.java
new file mode 100644
index 0000000..e0de595
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/DBOperationType.java
@@ -0,0 +1,18 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package net.onrc.onos.ofcontroller.flowmanager;
+
+/**
+ *
+ * @author nickkaranatsios
+ */
+public enum DBOperationType {
+    ADD,
+    UPDATE,
+    QUERY,
+    REMOVE,
+    INVALID
+}
+
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 82d4a7e..2793dd4 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowDatabaseOperation.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowDatabaseOperation.java
@@ -1,5 +1,6 @@
 package net.onrc.onos.ofcontroller.flowmanager;
 
+import com.tinkerpop.blueprints.Direction;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.ArrayList;
@@ -28,6 +29,7 @@
     private final static Logger log = LoggerFactory.getLogger(FlowDatabaseOperation.class);
     private static final boolean measureONOSFlowTimeProp = Long.valueOf(System.getProperty("benchmark.measureONOSFlow", "0")) != 0;
     private static final boolean measureONOSFlowEntryTimeProp = Long.valueOf(System.getProperty("benchmark.measureONOSFlowEntry", "0")) != 0;
+    private static final boolean useFastAddFlow = true;
 
     /**
      * Add a flow.
@@ -36,7 +38,299 @@
      * @param flowPath the Flow Path to install.
      * @return true on success, otherwise false.
      */
+    static boolean addFlowFast(DBOperation dbHandler, FlowPath flowPath) {
+	IFlowPath flowPathObj = null;
+	FlowPathProperty flowProp = new FlowPathProperty();
+        FlowEntity flowPathEntity = new FlowEntity();
+        boolean flowPathUpdate = false;
+	
+	flowPathObj = dbHandler.searchFlowPath(flowPath.flowId()); // toshi memo: getVertices("flow_id")
+	if (flowPathObj == null) {
+	    try {
+                flowPathEntity.operationBegin(DBOperationType.ADD.toString());
+		flowPathObj = dbHandler.newFlowPath(); // toshi memo: addVertex(), setType("flow")
+	    } catch (Exception e) {
+		flowPathObj = null;
+		StringWriter sw = new StringWriter();
+		e.printStackTrace(new PrintWriter(sw));
+		log.error(":addFlow FlowId:{} failed: {}", flowPath.flowId(), sw.toString());
+	    }
+            flowPathEntity.setProperty("user_state", "FP_USER_ADD");
+	    flowProp.setFlowPathUserState("FP_USER_ADD");
+	} else {
+            flowPathUpdate = true;
+	    // Remove the old Flow Entries (this is special for RAMCloud)
+	    for (IFlowEntry flowEntryObj : flowPathObj.getFlowEntries()) { // toshi memo: get.@Adjacency("flow", IN)
+		//flowObj.removeFlowEntry(flowEntryObj);   // toshi memo: remove.@Adjacency("flow", IN)
+                flowPathEntity.operationBegin(DBOperationType.REMOVE.toString());
+		dbHandler.removeFlowEntry(flowEntryObj); // toshi memo: removeVertex()
+                flowPathEntity.operationEnd(DBOperationType.REMOVE.toString());
+	    }
+            flowPathEntity.operationBegin(DBOperationType.UPDATE.toString());
+            flowPathEntity.setProperty("user_state", "FP_USER_ADD");
+	    flowProp.setFlowPathUserState("FP_USER_MODIFY");
+	}
+	if (flowPathObj == null) {
+	    log.error(":addFlow FlowId:{} failed: Flow object not created", flowPath.flowId());
+	    dbHandler.rollback();
+	    
+	    return false;
+	}
+
+        flowPathEntity.setProperty("flow_id", flowPath.flowId().toString());
+	// Set the Flow key
+	flowProp.setFlowId(flowPath.flowId().toString());
+
+	// Set the Flow attributes
+
+        flowPathEntity.setProperty("installer_id", flowPath.installerId().toString());
+	flowProp.setInstallerId(flowPath.installerId().toString());
+
+        flowPathEntity.setProperty("flow_path_type", flowPath.flowPathType().toString());
+	flowProp.setFlowPathType(flowPath.flowPathType().toString());
+
+        flowPathEntity.setProperty("user_state", flowPath.flowPathUserState().toString());
+	flowProp.setFlowPathUserState(flowPath.flowPathUserState().toString());
+
+
+        flowPathEntity.setProperty("flow_path_flags", flowPath.flowPathFlags().flags());
+	flowProp.setFlowPathFlags(flowPath.flowPathFlags().flags());
+
+        flowPathEntity.setProperty("idle_timeout", flowPath.idleTimeout());
+	flowProp.setIdleTimeout(flowPath.idleTimeout());
+
+        flowPathEntity.setProperty("hard_timeout", flowPath.hardTimeout());
+	flowProp.setHardTimeout(flowPath.hardTimeout());
+
+        flowPathEntity.setProperty("src_switch", flowPath.dataPath().srcPort().dpid().toString());
+	flowProp.setSrcSwitch(flowPath.dataPath().srcPort().dpid().toString());
+
+        flowPathEntity.setProperty("src_port", flowPath.dataPath().srcPort().port().value());
+	flowProp.setSrcPort(flowPath.dataPath().srcPort().port().value());
+
+        flowPathEntity.setProperty("dst_switch", flowPath.dataPath().dstPort().dpid().toString());
+	flowProp.setDstSwitch(flowPath.dataPath().dstPort().dpid().toString());
+
+        flowPathEntity.setProperty("dst_port", flowPath.dataPath().dstPort().port().value());
+	flowProp.setDstPort(flowPath.dataPath().dstPort().port().value());
+
+	if (flowPath.flowEntryMatch().matchSrcMac()) {
+            flowPathEntity.setProperty("matchSrcMac",flowPath.flowEntryMatch().srcMac().toString());
+	    flowProp.setMatchSrcMac(flowPath.flowEntryMatch().srcMac().toString());
+	}
+	if (flowPath.flowEntryMatch().matchDstMac()) {
+            flowPathEntity.setProperty("matchDstMac", flowPath.flowEntryMatch().dstMac().toString());
+	    flowProp.setMatchDstMac(flowPath.flowEntryMatch().dstMac().toString());
+	}
+	if (flowPath.flowEntryMatch().matchEthernetFrameType()) {
+            flowPathEntity.setProperty("matchEthernetFrameType", flowPath.flowEntryMatch().ethernetFrameType());
+	    flowProp.setMatchEthernetFrameType(flowPath.flowEntryMatch().ethernetFrameType());
+	}
+	if (flowPath.flowEntryMatch().matchVlanId()) {
+            flowPathEntity.setProperty("matchVlanId", flowPath.flowEntryMatch().vlanId());
+	    flowProp.setMatchVlanId(flowPath.flowEntryMatch().vlanId());
+	}
+	if (flowPath.flowEntryMatch().matchVlanPriority()) {
+            flowPathEntity.setProperty("matchVlanPriority", flowPath.flowEntryMatch().vlanPriority());
+	    flowProp.setMatchVlanPriority(flowPath.flowEntryMatch().vlanPriority());
+	}
+	if (flowPath.flowEntryMatch().matchSrcIPv4Net()) {
+            flowPathEntity.setProperty("matchSrcIPv4Net", flowPath.flowEntryMatch().srcIPv4Net().toString());
+	    flowProp.setMatchSrcIPv4Net(flowPath.flowEntryMatch().srcIPv4Net().toString());
+	}
+	if (flowPath.flowEntryMatch().matchDstIPv4Net()) {
+            flowPathEntity.setProperty("matchDstIPv4Net", flowPath.flowEntryMatch().dstIPv4Net().toString());
+	    flowProp.setMatchDstIPv4Net(flowPath.flowEntryMatch().dstIPv4Net().toString());
+	}
+	if (flowPath.flowEntryMatch().matchIpProto()) {
+            flowPathEntity.setProperty("matchIpProto", flowPath.flowEntryMatch().ipProto());
+	    flowProp.setMatchIpProto(flowPath.flowEntryMatch().ipProto());
+	}
+	if (flowPath.flowEntryMatch().matchIpToS()) {
+            flowPathEntity.setProperty("matchIpToS", flowPath.flowEntryMatch().ipToS());
+	    flowProp.setMatchIpToS(flowPath.flowEntryMatch().ipToS());
+	}
+	if (flowPath.flowEntryMatch().matchSrcTcpUdpPort()) {
+            flowPathEntity.setProperty("matchSrcTcpUdpPort", flowPath.flowEntryMatch().srcTcpUdpPort());
+	    flowProp.setMatchSrcTcpUdpPort(flowPath.flowEntryMatch().srcTcpUdpPort());
+	}
+	if (flowPath.flowEntryMatch().matchDstTcpUdpPort()) {
+            flowPathEntity.setProperty("matchDstTcpUdpPort", flowPath.flowEntryMatch().dstTcpUdpPort());
+	    flowProp.setMatchDstTcpUdpPort(flowPath.flowEntryMatch().dstTcpUdpPort());
+	}
+	if (! flowPath.flowEntryActions().actions().isEmpty()) {
+            flowPathEntity.setProperty("actions", flowPath.flowEntryActions().toString());
+	    flowProp.setActions(flowPath.flowEntryActions().toString());
+	}
+        flowPathEntity.setProperty("data_path_summary", flowPath.dataPath().dataPathSummary());
+	flowProp.setDataPathSummary(flowPath.dataPath().dataPathSummary());
+
+	flowProp.commitProperties(dbHandler, flowPathObj); // toshi memo: flowObj.setProperties()
+
+	//
+	// Flow Entries:
+	// flowPath.dataPath().flowEntries()
+	//
+	for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
+	    if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_DELETE)
+		continue;	// Skip: all Flow Entries were deleted earlier
+
+	    IFlowEntry iFlowEntry = null;
+	    FlowEntryProperty flowEntryProp = new FlowEntryProperty();
+            FlowEntity flowEntryEntity = new FlowEntity();
+            boolean updateFlowEntry = false;
+	    
+	    try {
+		iFlowEntry = dbHandler.searchFlowEntry(flowEntry.flowEntryId()); // toshi memo: getVertices()
+		if (iFlowEntry != null) {
+                    updateFlowEntry = true;
+                    flowEntryEntity.operationBegin(DBOperationType.UPDATE.toString());
+                    flowEntryEntity.setProperty("user_state", "FE_USER_MODIFY");
+		    flowEntryProp.setUserState("FE_USER_MODIFY");
+		} else {
+System.out.println("About to add a flow entry");
+                    flowEntryEntity.operationBegin(DBOperationType.ADD.toString());
+                    flowEntryEntity.setProperty("user_state", "FE_USER_ADD");
+		    flowEntryProp.setUserState("FE_USER_ADD");
+		    // NK: iFlowEntry = dbHandler.newFlowEntry(); // toshi memo: addVertex(). setType("flow_entry")
+		    //flowObj.addFlowEntry(iFlowEntry);      // toshi memo: add.@Adjacency("flow", IN)
+		    // NK: iFlowEntry.setFlow(flowPathObj);           // toshi memo: set.@Adjacency("flow")
+                    flowEntryEntity.addEdge(flowPathObj, Direction.OUT, "flow");
+		}
+	    } catch (Exception e) {
+		iFlowEntry = null;
+	    }
+            /* NK:
+	    if (iFlowEntry == null) {
+		log.error(":addFlow FlowEntryId:{} failed: FlowEntry object not created", flowEntry.flowEntryId());
+		dbHandler.rollback();
+		return false;
+	    }
+            */
+	    
+            flowEntryEntity.setProperty("flow_id", flowEntry.flowEntryId().toString());
+	    // Set the Flow Entry key
+	    // NK: flowEntryProp.setFlowEntryId(flowEntry.flowEntryId().toString());
+            flowEntryEntity.setProperty("flow_entry_id", flowEntry.flowEntryId().toString());
+
+            flowEntryEntity.setProperty("type", "flow_entry");
+	    // NK: flowEntryProp.setType("flow_entry");
+
+	    // Set the Flow Entry Edges
+	    ISwitchObject sw = dbHandler.searchSwitch(flowEntry.dpid().toString()); // toshi memo: getVertices()
+
+            flowEntryEntity.setProperty("idle_timeout", flowEntry.idleTimeout());
+	    // NK: flowEntryProp.setIdleTimeout(flowEntry.idleTimeout());
+
+            flowEntryEntity.setProperty("hard_timeout", flowEntry.hardTimeout());
+	    // NK: flowEntryProp.setHardTimeout(flowEntry.hardTimeout());
+
+            flowEntryEntity.setProperty("switch_dpid", flowEntry.dpid().toString());
+	    // NK:flowEntryProp.setSwitchDpid(flowEntry.dpid().toString());
+
+	    //NK: iFlowEntry.setSwitch(sw);  // toshi memo: set.@Adjacency("switch")
+            flowEntryEntity.addEdge(sw, Direction.OUT, "switch");
+	    if (flowEntry.flowEntryMatch().matchInPort()) {
+		IPortObject inport = dbHandler.searchPort(flowEntry.dpid().toString(), flowEntry.flowEntryMatch().inPort().value()); // toshi memo: getVertices()
+
+                flowEntryEntity.setProperty("matchInPort", flowEntry.flowEntryMatch().inPort().value());
+		// NK: flowEntryProp.setMatchInPort(flowEntry.flowEntryMatch().inPort().value());
+                flowEntryEntity.addEdge(inport, Direction.OUT, "inport");
+		// NK: iFlowEntry.setInPort(inport);  // toshi memo: set.@Adjacency("inport")
+	    }
+
+	    // Set the Flow Entry attributes
+	    if (flowEntry.flowEntryMatch().matchSrcMac()) {
+                flowEntryEntity.setProperty("matchSrcMac", flowEntry.flowEntryMatch().srcMac().toString());
+		// NK: flowEntryProp.setMatchSrcMac(flowEntry.flowEntryMatch().srcMac().toString());
+	    }
+	    if (flowEntry.flowEntryMatch().matchDstMac()) {
+                flowEntryEntity.setProperty("matchDstMac", flowEntry.flowEntryMatch().dstMac().toString());
+		// NK: flowEntryProp.setMatchDstMac(flowEntry.flowEntryMatch().dstMac().toString());
+	    }
+	    if (flowEntry.flowEntryMatch().matchEthernetFrameType()) {
+                flowEntryEntity.setProperty("matchEthernetFrameType", flowEntry.flowEntryMatch().ethernetFrameType());
+		// NK: flowEntryProp.setMatchEthernetFrameType(flowEntry.flowEntryMatch().ethernetFrameType());
+	    }
+	    if (flowEntry.flowEntryMatch().matchVlanId()) {
+                flowEntryEntity.setProperty("matchVlanId", flowEntry.flowEntryMatch().vlanId());
+		// NK: flowEntryProp.setMatchVlanId(flowEntry.flowEntryMatch().vlanId());
+	    }
+	    if (flowEntry.flowEntryMatch().matchVlanPriority()) {
+                flowEntryEntity.setProperty("matchVlanPriority", flowEntry.flowEntryMatch().vlanPriority());
+		// NK: flowEntryProp.setMatchVlanPriority(flowEntry.flowEntryMatch().vlanPriority());
+	    }
+	    if (flowEntry.flowEntryMatch().matchSrcIPv4Net()) {
+                flowEntryEntity.setProperty("matchSrcIPv4Net", flowEntry.flowEntryMatch().srcIPv4Net().toString());
+		// NK: flowEntryProp.setMatchSrcIPv4Net(flowEntry.flowEntryMatch().srcIPv4Net().toString());
+	    }
+	    if (flowEntry.flowEntryMatch().matchDstIPv4Net()) {
+                flowEntryEntity.setProperty("matchDstIPv4Net", flowEntry.flowEntryMatch().dstIPv4Net().toString());
+		// NK: flowEntryProp.setMatchDstIPv4Net(flowEntry.flowEntryMatch().dstIPv4Net().toString());
+	    }
+	    if (flowEntry.flowEntryMatch().matchIpProto()) {
+                flowEntryEntity.setProperty("matchIpProto", flowEntry.flowEntryMatch().ipProto());
+		// NK: flowEntryProp.setMatchIpProto(flowEntry.flowEntryMatch().ipProto());
+	    }
+	    if (flowEntry.flowEntryMatch().matchIpToS()) {
+                flowEntryEntity.setProperty("matchIpToS", flowEntry.flowEntryMatch().ipToS());
+		// NK: flowEntryProp.setMatchIpToS(flowEntry.flowEntryMatch().ipToS());
+	    }
+	    if (flowEntry.flowEntryMatch().matchSrcTcpUdpPort()) {
+                flowEntryEntity.setProperty("matchSrcTcpUdpPort", flowEntry.flowEntryMatch().srcTcpUdpPort());
+		// NK: flowEntryProp.setMatchSrcTcpUdpPort(flowEntry.flowEntryMatch().srcTcpUdpPort());
+	    }
+	    if (flowEntry.flowEntryMatch().matchDstTcpUdpPort()) {
+                flowEntryEntity.setProperty("matchDstTcpUdpPort", flowEntry.flowEntryMatch().dstTcpUdpPort());
+		// NK: flowEntryProp.setMatchDstTcpUdpPort(flowEntry.flowEntryMatch().dstTcpUdpPort());
+	    }
+
+	    for (FlowEntryAction fa : flowEntry.flowEntryActions().actions()) {
+		if (fa.actionOutput() != null) {
+		    IPortObject outport = dbHandler.searchPort(flowEntry.dpid().toString(), fa.actionOutput().port().value()); // toshi memo: getVertices()
+                    flowEntryEntity.setProperty("actionOutputPort", fa.actionOutput().port().value());
+		    // NK: flowEntryProp.setActionOutputPort(fa.actionOutput().port().value());
+                    flowEntryEntity.addEdge(outport, Direction.OUT, "outport");
+		    // NK: iFlowEntry.setOutPort(outport); // set.@Adjacency("outport")
+		}
+	    }
+	    if (! flowEntry.flowEntryActions().isEmpty()) {
+                flowEntryEntity.setProperty("actions", flowEntry.flowEntryActions().toString());
+		// NK: flowEntryProp.setActions(flowEntry.flowEntryActions().toString());
+	    }
+
+            flowEntryEntity.setProperty("switch_state", flowEntry.flowEntrySwitchState().toString());
+	    // NK: flowEntryProp.setSwitchState(flowEntry.flowEntrySwitchState().toString());
+	    // NK: flowEntryProp.commitProperties(dbHandler, iFlowEntry); // toshi memo: setProperties()
+            if (updateFlowEntry) {
+               flowEntryEntity.operationEnd(DBOperationType.UPDATE.toString());
+            } else {
+               flowEntryEntity.operationEnd(DBOperationType.ADD.toString());
+            }
+            flowPathEntity.append(flowEntryEntity);
+	}
+	
+        if (flowPathUpdate) {
+            flowPathEntity.operationEnd(DBOperationType.UPDATE.toString());
+        } else {
+            flowPathEntity.operationEnd(DBOperationType.ADD.toString());
+        }
+        flowPathEntity.persist(dbHandler);
+	// NK:dbHandler.commit();
+	return true;
+    }
+    
+    /**
+     * Add a flow.
+     *
+     * @param dbHandler the Graph Database handler to use.
+     * @param flowPath the Flow Path to install.
+     * @return true on success, otherwise false.
+     */
     static boolean addFlow(DBOperation dbHandler, FlowPath flowPath) {
+	if (useFastAddFlow)
+	    return addFlowFast(dbHandler, flowPath);
+
 	IFlowPath flowObj = null;
 	boolean found = false;
 	long startAddFlow = 0;
@@ -131,7 +425,7 @@
 		startSettingFlowPathProps = System.nanoTime();
 	}
 
-	FlowPathProperty flowProp = new FlowPathProperty(dbHandler, flowObj);
+	FlowPathProperty flowProp = new FlowPathProperty();
 
 	//
 	// Set the Flow key:
@@ -261,7 +555,7 @@
 	else
 	    flowProp.setFlowPathUserState("FP_USER_ADD");
 
-	flowProp.commitProperties();
+	flowProp.commitProperties(dbHandler, flowObj);
 
 	if ( measureONOSFlowTimeProp ) {
 	    ++numPropsSet;
@@ -427,7 +721,7 @@
 	    startSetProperties = System.nanoTime();
 	}
 
-	FlowEntryProperty flowProp = new FlowEntryProperty(dbHandler, flowEntryObj);
+	FlowEntryProperty flowProp = new FlowEntryProperty();
 
 	//
 	// Set the Flow Entry key:
@@ -622,7 +916,7 @@
 	if (measureONOSFlowEntryTimeProp) {
 	    numProperties += 2;
 	}
-	flowProp.commitProperties();
+	flowProp.commitProperties(dbHandler, flowEntryObj);
 	//
 	// TODO: Take care of the FlowEntryErrorState.
 	//
@@ -637,7 +931,7 @@
 	    if (measureONOSFlowEntryTimeProp) {
 		startAddEdgeBetweenFlowPath = System.nanoTime();
 	    }
-	    flowObj.addFlowEntry(flowEntryObj);
+	    //flowObj.addFlowEntry(flowEntryObj);
 	    flowEntryObj.setFlow(flowObj);
 	    if (measureONOSFlowEntryTimeProp) {
 		endAddEdgeBetweenFlowPath = System.nanoTime();
@@ -727,7 +1021,6 @@
 		continue;
 	    deleteIFlowPath(dbHandler, flowPathObj);
 	}
-	dbHandler.commit();
 
 	return true;
     }
@@ -755,7 +1048,6 @@
 	}
 
 	deleteIFlowPath(dbHandler, flowObj);
-	dbHandler.commit();
 
 	return true;
     }
@@ -771,6 +1063,7 @@
 	}
 	// Remove the Flow itself
 	dbHandler.removeFlowPath(flowObj);
+	dbHandler.commit();
     }
 
     /**
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowEntity.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowEntity.java
new file mode 100644
index 0000000..991e2f1
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowEntity.java
@@ -0,0 +1,262 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package net.onrc.onos.ofcontroller.flowmanager;
+
+import com.tinkerpop.blueprints.Direction;
+import com.tinkerpop.blueprints.Vertex;
+import com.tinkerpop.blueprints.Edge;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import net.onrc.onos.graph.DBOperation;
+import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IBaseObject;
+import com.tinkerpop.blueprints.impls.ramcloud.RamCloudGraph;
+import com.tinkerpop.blueprints.impls.ramcloud.RamCloudVertex;
+import java.util.List;
+import java.util.Set;
+
+/**
+ *
+ * @author nickkaranatsios
+ */
+public class FlowEntity implements FlowEntityManager {
+    private String primaryKey;
+    private Class<?> hasMany;
+    private Collection<?> many = new ArrayList<>();
+    private Map<String, Object> properties;
+    private Map<String, Map<String, Object>> operations = new HashMap<>();
+    private ArrayList<Object> children = new ArrayList<>();
+    private ArrayList<Object> edges = new ArrayList<>();
+    private int opCount;
+    public Direction dir;
+
+    public FlowEntity() {
+        opCount = 0;
+    }
+    
+    private class EntityEdge {
+        private Object src;
+        private Object dst;
+        private Direction dir;
+        private String label;
+        private DBOperationType op;
+        
+        public EntityEdge(Object src, Object dst, DBOperationType op, Direction dir, String label) {
+            this.src = src;
+            this.dst = dst;
+            this.dir = dir;
+            this.label = label;
+            this.op = op;
+        }
+        
+        public EntityEdge(Object src, Object dst, String label) {
+            this.src = src;
+            this.dst = dst;
+            this.label = label;
+        }
+        
+        @Override
+        public String toString() {
+            return "EntityEdge: " + src + " " + dst + " " + label;
+        }
+    }
+
+    private class RamCloudEdgeEntity implements Edge {
+        private Vertex src;
+        private Vertex dst;
+        private Direction direction;
+        private String label;
+
+        public RamCloudEdgeEntity(Vertex src, Vertex dst, Direction direction, String label) {
+            this.src = src;
+            this.dst = dst;
+            this.direction = direction;
+            this.label = label;
+        }
+        
+        @Override
+        public Vertex getVertex(com.tinkerpop.blueprints.Direction dir) throws IllegalArgumentException {
+            if (dir == Direction.IN) {
+                System.out.println("returning in vertex " + this.dst.getId());
+                return dst;
+            } else if (dir == Direction.OUT) {
+                System.out.println("returning out vertex " + this.src.getId());
+                return src;
+            }
+            return null;
+        }
+
+        @Override
+        public String getLabel() {
+            return this.label;
+        }
+
+        @Override
+        public <T> T getProperty(String key) {
+            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+        }
+
+        @Override
+        public Set<String> getPropertyKeys() {
+            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+        }
+
+        @Override
+        public void setProperty(String key, Object value) {
+            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+        }
+
+        @Override
+        public <T> T removeProperty(String key) {
+            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+        }
+
+        @Override
+        public Object getId() {
+            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+        }
+        
+    }
+    
+    @Override
+    public void setPrimaryKey(String key) {
+        primaryKey = key;
+    }
+    
+    @Override
+    public String getPrimaryKey() {
+        return primaryKey;
+    }
+
+    @Override
+    public void hasMany(Class<?> cClass) {
+        hasMany = cClass;
+    }
+    
+    @Override
+    public void operationBegin(String opName) {
+        properties = new HashMap<>();
+        operations.put(getOpKey(opName), properties);
+        opCount++;
+    }
+    
+    @Override
+    public void operationEnd(String opName) {
+        String opKey = getOpKey(opName);
+        if (operations.containsKey(opKey)) {
+            System.out.println(operations);
+        }
+        
+    }
+    
+    
+    private String getOpKey(String opName) {
+        return opName + new Integer(opCount).toString();
+        
+    }
+
+    @Override
+    public void setProperty(String propertyName, Object value) {
+        properties.put(propertyName, value);
+    }
+
+    @Override
+    public FlowEntityManager append(Object entity) {
+        children.add(entity);
+        return this;
+    }
+    
+    @Override
+    public Object getProperty(String propertyName) {
+        if (properties.containsKey(propertyName)) {
+            return properties.get(propertyName);
+        }
+        return null;
+    }
+    
+    @Override
+    public void persist(DBOperation dbHandler) {
+        System.out.println("total operations: " );
+        System.out.println(operations);
+        // get a hold of all the flow entries for the current flowpath.
+        if (children.size() > 0) {
+            int noOfChildren = children.size();
+            if (noOfChildren > 0) {
+                // construct a list of null ids for creating vertices for all
+                // flow entries.
+                ArrayList<Object> ids = new ArrayList<>(noOfChildren);
+                // set properties
+                Map<RamCloudVertex, Map<String, Object>> propertiesToSet = new HashMap<>();
+                
+                RamCloudGraph graph = (RamCloudGraph)dbHandler.getDBConnection().getFramedGraph().getBaseGraph();
+                for (int i = 0; i < noOfChildren; i++) {
+                    ids.add(null);
+                    //addedVertices.add((RamCloudVertex) graph.addVertex(null));
+                }
+                List<RamCloudVertex> addedVertices = graph.addVertices(ids);
+                System.out.println("Added vertices " + addedVertices);
+                // call setVertices()
+                //Iterable<Vertex> vertices = dbHandler.setVertices(ids);
+                //Iterator vi = vertices.iterator();
+                // get source and destination edge match vertex v construct list
+                // of edges
+
+                ArrayList<Edge> edgesToSet = new ArrayList<>();
+                for (int i = 0; i < noOfChildren; i++) {
+                    FlowEntity childEntity = (FlowEntity)children.get(i);
+                    Vertex srcVertex = addedVertices.get(i);                    
+                    propertiesToSet.put((RamCloudVertex)srcVertex, childEntity.properties);
+                    //Vertex srcVertex = getVertexEdge(vi, i);
+
+                    if (srcVertex == null) continue;
+                    for (int j = 0; j < childEntity.edges.size(); j++) {
+                        EntityEdge edge = (EntityEdge) childEntity.edges.get(j);
+                        edgesToSet.add(new RamCloudEdgeEntity(srcVertex, ((IBaseObject) edge.dst).asVertex(), edge.dir, edge.label));
+                    }
+                }
+                graph.addEdges(edgesToSet);
+                graph.setProperties(propertiesToSet);
+            }
+        }
+        for (int i = 0; i < children.size(); i++) {
+            FlowEntityManager entity = (FlowEntityManager)children.get(i);
+            System.out.println(entity.getProperties());
+        }
+    }
+
+    private Vertex getVertexEdge(Iterator vi, int idx) {
+        int i = 0;
+        while (vi.hasNext()) {
+            Vertex v = (Vertex)vi.next();
+            if (i == idx) {
+                return v;
+            }
+            i++;
+        }
+        return null;
+    }
+    
+    @Override
+    public Map<String, Object> getProperties() {
+        return properties;
+    }
+    
+    @Override
+    public void addEdge(Object dst, Direction dir, String label) {
+        edges.add(new EntityEdge(this, dst, DBOperationType.ADD, dir, label));
+    }
+    
+    @Override
+    public void removeEdge(Object src, Object dst, Direction dir, String label) {
+        edges.add(new EntityEdge(src, dst, DBOperationType.REMOVE, dir, label));
+    }
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowEntityManager.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowEntityManager.java
new file mode 100644
index 0000000..38e694b
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowEntityManager.java
@@ -0,0 +1,29 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package net.onrc.onos.ofcontroller.flowmanager;
+
+import com.tinkerpop.blueprints.Direction;
+import java.util.Map;
+import net.onrc.onos.graph.DBOperation;
+
+/**
+ *
+ * @author nickkaranatsios
+ */
+public interface FlowEntityManager {
+    public void setPrimaryKey(String key);
+    public String getPrimaryKey();
+    public void hasMany(Class<?> cClass);
+    public void operationBegin(String opName);
+    public void operationEnd(String opName);
+    public void setProperty(String propertyName, Object value);
+    public Object getProperty(String propertyName);
+    public Map<String, Object> getProperties();
+    public FlowEntityManager append(Object entity);
+    public void addEdge(Object dst, Direction dir, String label);
+    public void removeEdge(Object src, Object dst, Direction dir, String label);
+    public void persist(DBOperation dbHandler);
+}
+
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowEntryProperty.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowEntryProperty.java
index 7b17aaa..b45f8ff 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowEntryProperty.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowEntryProperty.java
@@ -8,13 +8,6 @@
 
 public class FlowEntryProperty {
     private Map<String, Object> map = new HashMap<>();
-    private DBOperation dbhandler;
-    private IFlowEntry flowEntry;
-    
-    public FlowEntryProperty(DBOperation dbHandler, IFlowEntry flowEntry) {
-        this.dbhandler = dbHandler;
-        this.flowEntry = flowEntry;
-    }
     
     public void setFlowId(String value) {
         map.put("flow_id", value);
@@ -144,7 +137,7 @@
      *
      * @param dbhandler
      */
-    public void commitProperties() {
-        dbhandler.setVertexProperties(flowEntry.asVertex() ,map);
+    public void commitProperties(DBOperation dbhandler, IFlowEntry flowEntry) {
+        dbhandler.setVertexProperties(flowEntry.asVertex(), map);
     }
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowPathProperty.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowPathProperty.java
index c6a2b98..21db7bc 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowPathProperty.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowPathProperty.java
@@ -8,13 +8,6 @@
 
 public class FlowPathProperty {
 	private Map<String, Object> map = new HashMap<>();
-	private DBOperation dbhandler;
-	private IFlowPath flowPath;
-
-	public FlowPathProperty(DBOperation dbHandler, IFlowPath flowPath) {
-		this.dbhandler = dbHandler;
-		this.flowPath = flowPath;
-	}
 
 	public void setType(String typeStr) {
 		map.put("type", typeStr);
@@ -120,7 +113,7 @@
      *
      * @param dbhandler
      */
-    public void commitProperties() {
+    public void commitProperties(DBOperation dbhandler, IFlowPath flowPath) {
         dbhandler.setVertexProperties(flowPath.asVertex() ,map);
     }
 }