Merge master branch into syncdev

Conflicts:
	src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java
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 e239ae9..ab4be1a 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java
@@ -19,7 +19,6 @@
 import net.floodlightcontroller.core.module.IFloodlightService;
 import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.util.OFMessageDamper;
-
 import net.onrc.onos.datagrid.IDatagridService;
 import net.onrc.onos.graph.GraphDBOperation;
 import net.onrc.onos.ofcontroller.core.INetMapStorage;
@@ -27,6 +26,7 @@
 import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IFlowPath;
 import net.onrc.onos.ofcontroller.floodlightlistener.INetworkGraphService;
 import net.onrc.onos.ofcontroller.flowmanager.web.FlowWebRoutable;
+import net.onrc.onos.ofcontroller.flowprogrammer.FlowPusher;
 import net.onrc.onos.ofcontroller.topology.ITopologyNetService;
 import net.onrc.onos.ofcontroller.topology.Topology;
 import net.onrc.onos.ofcontroller.topology.TopologyElement;
@@ -57,14 +57,18 @@
     protected FloodlightModuleContext context;
     protected FlowEventHandler flowEventHandler;
 
-    protected OFMessageDamper messageDamper;
-
-    //
-    // TODO: Values copied from elsewhere (class LearningSwitch).
-    // The local copy should go away!
-    //
-    protected static final int OFMESSAGE_DAMPER_CAPACITY = 50000; // TODO: find sweet spot
-    protected static final int OFMESSAGE_DAMPER_TIMEOUT = 250;	// ms
+    protected FlowPusher pusher;
+    private static final int NUM_PUSHER_THREAD = 1;
+    
+//	LEGACY
+//    protected OFMessageDamper messageDamper;
+//
+//    //
+//    // TODO: Values copied from elsewhere (class LearningSwitch).
+//    // The local copy should go away!
+//    //
+//    protected static final int OFMESSAGE_DAMPER_CAPACITY = 50000; // TODO: find sweet spot
+//    protected static final int OFMESSAGE_DAMPER_TIMEOUT = 250;	// ms
 
     // Flow Entry ID generation state
     private static Random randomGenerator = new Random();
@@ -458,10 +462,13 @@
 	datagridService = context.getServiceImpl(IDatagridService.class);
 	restApi = context.getServiceImpl(IRestApiService.class);
 
-	messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
-					    EnumSet.of(OFType.FLOW_MOD),
-					    OFMESSAGE_DAMPER_TIMEOUT);
+//	LEGACY
+//	messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
+//					    EnumSet.of(OFType.FLOW_MOD),
+//					    OFMESSAGE_DAMPER_TIMEOUT);
 
+	pusher = new FlowPusher(NUM_PUSHER_THREAD);
+	pusher.init(null, floodlightProvider.getOFMessageFactory(), null);
 	this.init("");
 
 	mapReaderScheduler = Executors.newScheduledThreadPool(1);
@@ -503,6 +510,8 @@
 	// Initialize the Flow Entry ID generator
 	nextFlowEntryIdPrefix = randomGenerator.nextInt();
 
+	pusher.start();
+	
 	//
 	// Create the Flow Event Handler thread and register it with the
 	// Datagrid Service
@@ -834,9 +843,12 @@
      */
     private boolean installFlowEntry(IOFSwitch mySwitch, IFlowPath flowObj,
 				    IFlowEntry flowEntryObj) {
-	return FlowSwitchOperation.installFlowEntry(
-		floodlightProvider.getOFMessageFactory(),
-		messageDamper, mySwitch, flowObj, flowEntryObj);
+    	return pusher.add(mySwitch, flowObj, flowEntryObj);
+    	
+//    	LEGACY
+//	return FlowSwitchOperation.installFlowEntry(
+//		floodlightProvider.getOFMessageFactory(),
+//		messageDamper, mySwitch, flowObj, flowEntryObj);
     }
 
     /**
@@ -849,9 +861,12 @@
      */
     private boolean installFlowEntry(IOFSwitch mySwitch, FlowPath flowPath,
 				    FlowEntry flowEntry) {
-	return FlowSwitchOperation.installFlowEntry(
-		floodlightProvider.getOFMessageFactory(),
-		messageDamper, mySwitch, flowPath, flowEntry);
+    	return pusher.add(mySwitch, flowPath, flowEntry);
+
+//    	LEGACY
+//	return FlowSwitchOperation.installFlowEntry(
+//		floodlightProvider.getOFMessageFactory(),
+//		messageDamper, mySwitch, flowPath, flowEntry);
     }
 
     /**
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 9741b04..8bed120 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowSwitchOperation.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowSwitchOperation.java
@@ -36,11 +36,14 @@
     public static final short FLOWMOD_DEFAULT_IDLE_TIMEOUT = 0;	// infinity
     public static final short FLOWMOD_DEFAULT_HARD_TIMEOUT = 0;	// infinite
 
+    // TODO add Pusher instance member
+    // 
+    
     /**
      * Install a Flow Entry on a switch.
      *
      * @param messageFactory the OpenFlow message factory to use.
-     * @maram messageDamper the OpenFlow message damper to use.
+     * @param messageDamper the OpenFlow message damper to use.
      * @param mySwitch the switch to install the Flow Entry into.
      * @param flowObj the flow path object for the flow entry to install.
      * @param flowEntryObj the flow entry object to install.
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowprogrammer/FlowPusher.java b/src/main/java/net/onrc/onos/ofcontroller/flowprogrammer/FlowPusher.java
new file mode 100644
index 0000000..5655dfa
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowprogrammer/FlowPusher.java
@@ -0,0 +1,978 @@
+package net.onrc.onos.ofcontroller.flowprogrammer;
+
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.openflow.protocol.*;
+import org.openflow.protocol.action.*;
+import org.openflow.protocol.factory.BasicFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.floodlightcontroller.core.FloodlightContext;
+import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.util.MACAddress;
+import net.floodlightcontroller.util.OFMessageDamper;
+import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IFlowEntry;
+import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IFlowPath;
+import net.onrc.onos.ofcontroller.util.FlowEntryAction;
+import net.onrc.onos.ofcontroller.util.FlowEntryAction.*;
+import net.onrc.onos.ofcontroller.util.FlowEntry;
+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.FlowEntryUserState;
+import net.onrc.onos.ofcontroller.util.FlowPath;
+import net.onrc.onos.ofcontroller.util.IPv4Net;
+import net.onrc.onos.ofcontroller.util.Port;
+
+/**
+ * FlowPusher intermediates flow_mod sent from FlowManager/FlowSync to switches.
+ * FlowPusher controls the rate of sending flow_mods so that connection doesn't overflow.
+ * @author Naoki Shiota
+ *
+ */
+public class FlowPusher {
+    private final static Logger log = LoggerFactory.getLogger(FlowPusher.class);
+
+    // NOTE: Below are moved from FlowManager.
+    // TODO: Values copied from elsewhere (class LearningSwitch).
+    // The local copy should go away!
+    //
+    protected static final int OFMESSAGE_DAMPER_CAPACITY = 50000; // TODO: find sweet spot
+    protected static final int OFMESSAGE_DAMPER_TIMEOUT = 250;	// ms
+    
+    // Interval of sleep when queue is empty
+    protected static final long SLEEP_MILLI_SEC = 10;
+    protected static final int SLEEP_NANO_SEC = 0;
+    
+    // Number of messages sent to switch at once
+    protected static final int MAX_MESSAGE_SEND = 100;
+
+    public static final short PRIORITY_DEFAULT = 100;
+    public static final short FLOWMOD_DEFAULT_IDLE_TIMEOUT = 0;	// infinity
+    public static final short FLOWMOD_DEFAULT_HARD_TIMEOUT = 0;	// infinite
+
+	public enum QueueState {
+		READY,
+		SUSPENDED,
+	}
+	
+	private class SwitchQueue extends ArrayDeque<OFMessage> {
+		QueueState state;
+		
+		// Max rate of sending message (bytes/sec). 0 implies no limitation.
+		long max_rate = 0;	// 0 indicates no limitation
+		long last_sent_time = 0;
+		long last_sent_size = 0;
+		
+		/**
+		 * Check if sending rate is within the rate
+		 * @param current Current time
+		 * @return true if within the rate
+		 */
+		boolean isSendable(long current) {
+			if (max_rate == 0) {
+				// no limitation
+				return true;
+			}
+			
+			long rate = last_sent_size / (current - last_sent_time);
+			
+			if (rate < max_rate) {
+				return true;
+			} else {
+				return false;
+			}
+		}
+		
+		void logSentData(long current, long size) {
+			last_sent_time = current;
+			last_sent_size = size;
+		}
+		
+	}
+	
+	private OFMessageDamper messageDamper;
+
+	private FloodlightContext context = null;
+	private BasicFactory factory = null;
+	private Map<Long, FlowPusherProcess> threadMap = null;
+	
+	private int number_thread = 1;
+	
+	private class FlowPusherProcess implements Runnable {
+		private Map<IOFSwitch,SwitchQueue> queues
+		= new HashMap<IOFSwitch,SwitchQueue>();
+		
+		private boolean isStopped = false;
+		private boolean isMsgAdded = false;
+		
+		@Override
+		public void run() {
+			log.debug("Begin Flow Pusher Process");
+			
+			while (true) {
+				Set< Map.Entry<IOFSwitch,SwitchQueue> > entries;
+				synchronized (queues) {
+					entries = queues.entrySet();
+				}
+				
+				// Set taint flag to false at this moment.
+				isMsgAdded = false;
+				
+				for (Map.Entry<IOFSwitch,SwitchQueue> entry : entries) {
+					IOFSwitch sw = entry.getKey();
+					SwitchQueue queue = entry.getValue();
+
+					// Skip if queue is suspended
+					if (sw == null || queue == null ||
+							queue.state != QueueState.READY) {
+						continue;
+					}
+					
+					// check sending rate and determine it to be sent or not
+					long current_time = System.nanoTime();
+					long size = 0;
+					
+					synchronized (queue) {
+						if (queue.isSendable(current_time)) {
+							int i = 0;
+							while (! queue.isEmpty()) {
+								// Number of messages excess the limit
+								if (++i >= MAX_MESSAGE_SEND) {
+									// Messages remains in queue
+									isMsgAdded = true;
+									break;
+								}
+								
+								OFMessage msg = queue.poll();
+								
+								// if need to send, call IOFSwitch#write()
+								try {
+									messageDamper.write(sw, msg, context);
+									log.debug("Pusher sends message : {}", msg);
+									size += msg.getLength();
+								} catch (IOException e) {
+									e.printStackTrace();
+									log.error("Exception in sending message ({}) : {}", msg, e);
+								}
+							}
+							sw.flush();
+							queue.logSentData(current_time, size);
+						}
+					}
+				}
+				
+				// sleep while all queues are empty
+				while (! (isMsgAdded || isStopped)) {
+					try {
+						Thread.sleep(SLEEP_MILLI_SEC, SLEEP_NANO_SEC);
+					} catch (InterruptedException e) {
+						e.printStackTrace();
+						log.error("Thread.sleep failed");
+					}
+				}
+				
+				log.debug("Exit sleep loop.");
+				
+				if (isStopped) {
+					log.debug("Pusher Process finished.");
+					return;
+				}
+
+			}
+		}
+	}
+	
+	public FlowPusher() {
+		
+	}
+	
+	public FlowPusher(int number_thread) {
+		this.number_thread = number_thread;
+	}
+	
+	public void init(FloodlightContext context, BasicFactory factory, OFMessageDamper damper) {
+		this.context = context;
+		this.factory = factory;
+		
+		if (damper != null) {
+			messageDamper = damper;
+		} else {
+			// use default value
+			messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
+				    EnumSet.of(OFType.FLOW_MOD),
+				    OFMESSAGE_DAMPER_TIMEOUT);
+		}
+	}
+	
+	/**
+	 * Begin processing queue.
+	 */
+	public void start() {
+		if (factory == null) {
+			log.error("FlowPusher not yet initialized.");
+			return;
+		}
+		
+		threadMap = new HashMap<Long,FlowPusherProcess>();
+		for (long i = 0; i < number_thread; ++i) {
+			FlowPusherProcess runnable = new FlowPusherProcess();
+			threadMap.put(i, runnable);
+			
+			Thread thread = new Thread(runnable);
+			thread.start();
+		}
+	}
+	
+	/**
+	 * Suspend processing a queue related to given switch.
+	 * @param sw
+	 */
+	public boolean suspend(IOFSwitch sw) {
+		SwitchQueue queue = getQueue(sw);
+		
+		if (queue == null) {
+			return false;
+		}
+		
+		synchronized (queue) {
+			if (queue.state == QueueState.READY) {
+				queue.state = QueueState.SUSPENDED;
+				return true;
+			}
+			return false;
+		}
+	}
+
+	/**
+	 * Resume processing a queue related to given switch.
+	 */
+	public boolean resume(IOFSwitch sw) {
+		SwitchQueue queue = getQueue(sw);
+		
+		if (queue == null) {
+			return false;
+		}
+		
+		synchronized (queue) {
+			if (queue.state == QueueState.SUSPENDED) {
+				queue.state = QueueState.READY;
+				return true;
+			}
+			return false;
+		}
+	}
+	
+	public boolean isSuspended(IOFSwitch sw) {
+		SwitchQueue queue = getQueue(sw);
+		
+		if (queue == null) {
+			// TODO Is true suitable for this case?
+			return true;
+		}
+		
+		return (queue.state == QueueState.SUSPENDED);
+	}
+
+	/**
+	 * End processing queue and exit thread.
+	 */
+	public void stop() {
+		if (threadMap == null) {
+			return;
+		}
+		
+		for (FlowPusherProcess runnable : threadMap.values()) {
+			if (! runnable.isStopped) {
+				runnable.isStopped = true;
+			}
+		}
+	}
+	
+	public void setRate(IOFSwitch sw, long rate) {
+		SwitchQueue queue = getQueue(sw);
+		if (queue == null) {
+			return;
+		}
+		
+		if (rate > 0) {
+			queue.max_rate = rate;
+		}
+	}
+	
+	/**
+	 * Add OFMessage to the queue related to given switch.
+	 * @param sw
+	 * @param msg
+	 */
+	public boolean add(IOFSwitch sw, OFMessage msg) {
+		FlowPusherProcess proc = getProcess(sw);
+		SwitchQueue queue = proc.queues.get(sw);
+
+		if (queue == null) {
+			queue = new SwitchQueue();
+			queue.state = QueueState.READY;
+			synchronized (proc) {
+				proc.queues.put(sw, queue);
+			}
+		}
+		
+		synchronized (queue) {
+			queue.add(msg);
+			log.debug("Message is pushed : {}", msg);
+		}
+		
+		proc.isMsgAdded = true;
+		
+		return true;
+	}
+	
+	/**
+	 * Create OFMessage from given flow information and add it to the queue.
+	 * @param sw
+	 * @param flowObj
+	 * @param flowEntryObj
+	 * @return
+	 */
+	public boolean add(IOFSwitch sw, IFlowPath flowObj, IFlowEntry flowEntryObj) {
+		log.debug("sending : {}, {}", sw, flowObj);
+		String flowEntryIdStr = flowEntryObj.getFlowEntryId();
+		if (flowEntryIdStr == null)
+		    return false;
+		FlowEntryId flowEntryId = new FlowEntryId(flowEntryIdStr);
+		String userState = flowEntryObj.getUserState();
+		if (userState == null)
+		    return false;
+
+		//
+		// Create the Open Flow Flow Modification Entry to push
+		//
+		OFFlowMod fm = (OFFlowMod)factory.getMessage(OFType.FLOW_MOD);
+		long cookie = flowEntryId.value();
+
+		short flowModCommand = OFFlowMod.OFPFC_ADD;
+		if (userState.equals("FE_USER_ADD")) {
+		    flowModCommand = OFFlowMod.OFPFC_ADD;
+		} else if (userState.equals("FE_USER_MODIFY")) {
+		    flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
+		} else if (userState.equals("FE_USER_DELETE")) {
+		    flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
+		} else {
+		    // Unknown user state. Ignore the entry
+		    log.debug("Flow Entry ignored (FlowEntryId = {}): unknown user state {}",
+			      flowEntryId.toString(), userState);
+		    return false;
+		}
+
+		//
+		// Fetch the match conditions.
+		//
+		// NOTE: The Flow matching conditions common for all Flow Entries are
+		// used ONLY if a Flow Entry does NOT have the corresponding matching
+		// condition set.
+		//
+		OFMatch match = new OFMatch();
+		match.setWildcards(OFMatch.OFPFW_ALL);
+
+		// Match the Incoming Port
+		Short matchInPort = flowEntryObj.getMatchInPort();
+		if (matchInPort != null) {
+		    match.setInputPort(matchInPort);
+		    match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_IN_PORT);
+		}
+
+		// Match the Source MAC address
+		String matchSrcMac = flowEntryObj.getMatchSrcMac();
+		if (matchSrcMac == null)
+		    matchSrcMac = flowObj.getMatchSrcMac();
+		if (matchSrcMac != null) {
+		    match.setDataLayerSource(matchSrcMac);
+		    match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
+		}
+
+		// Match the Destination MAC address
+		String matchDstMac = flowEntryObj.getMatchDstMac();
+		if (matchDstMac == null)
+		    matchDstMac = flowObj.getMatchDstMac();
+		if (matchDstMac != null) {
+		    match.setDataLayerDestination(matchDstMac);
+		    match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
+		}
+
+		// Match the Ethernet Frame Type
+		Short matchEthernetFrameType = flowEntryObj.getMatchEthernetFrameType();
+		if (matchEthernetFrameType == null)
+		    matchEthernetFrameType = flowObj.getMatchEthernetFrameType();
+		if (matchEthernetFrameType != null) {
+		    match.setDataLayerType(matchEthernetFrameType);
+		    match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
+		}
+
+		// Match the VLAN ID
+		Short matchVlanId = flowEntryObj.getMatchVlanId();
+		if (matchVlanId == null)
+		    matchVlanId = flowObj.getMatchVlanId();
+		if (matchVlanId != null) {
+		    match.setDataLayerVirtualLan(matchVlanId);
+		    match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_VLAN);
+		}
+
+		// Match the VLAN priority
+		Byte matchVlanPriority = flowEntryObj.getMatchVlanPriority();
+		if (matchVlanPriority == null)
+		    matchVlanPriority = flowObj.getMatchVlanPriority();
+		if (matchVlanPriority != null) {
+		    match.setDataLayerVirtualLanPriorityCodePoint(matchVlanPriority);
+		    match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_VLAN_PCP);
+		}
+
+		// Match the Source IPv4 Network prefix
+		String matchSrcIPv4Net = flowEntryObj.getMatchSrcIPv4Net();
+		if (matchSrcIPv4Net == null)
+		    matchSrcIPv4Net = flowObj.getMatchSrcIPv4Net();
+		if (matchSrcIPv4Net != null) {
+		    match.setFromCIDR(matchSrcIPv4Net, OFMatch.STR_NW_SRC);
+		}
+
+		// Match the Destination IPv4 Network prefix
+		String matchDstIPv4Net = flowEntryObj.getMatchDstIPv4Net();
+		if (matchDstIPv4Net == null)
+		    matchDstIPv4Net = flowObj.getMatchDstIPv4Net();
+		if (matchDstIPv4Net != null) {
+		    match.setFromCIDR(matchDstIPv4Net, OFMatch.STR_NW_DST);
+		}
+
+		// Match the IP protocol
+		Byte matchIpProto = flowEntryObj.getMatchIpProto();
+		if (matchIpProto == null)
+		    matchIpProto = flowObj.getMatchIpProto();
+		if (matchIpProto != null) {
+		    match.setNetworkProtocol(matchIpProto);
+		    match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_NW_PROTO);
+		}
+
+		// Match the IP ToS (DSCP field, 6 bits)
+		Byte matchIpToS = flowEntryObj.getMatchIpToS();
+		if (matchIpToS == null)
+		    matchIpToS = flowObj.getMatchIpToS();
+		if (matchIpToS != null) {
+		    match.setNetworkTypeOfService(matchIpToS);
+		    match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_NW_TOS);
+		}
+
+		// Match the Source TCP/UDP port
+		Short matchSrcTcpUdpPort = flowEntryObj.getMatchSrcTcpUdpPort();
+		if (matchSrcTcpUdpPort == null)
+		    matchSrcTcpUdpPort = flowObj.getMatchSrcTcpUdpPort();
+		if (matchSrcTcpUdpPort != null) {
+		    match.setTransportSource(matchSrcTcpUdpPort);
+		    match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_TP_SRC);
+		}
+
+		// Match the Destination TCP/UDP port
+		Short matchDstTcpUdpPort = flowEntryObj.getMatchDstTcpUdpPort();
+		if (matchDstTcpUdpPort == null)
+		    matchDstTcpUdpPort = flowObj.getMatchDstTcpUdpPort();
+		if (matchDstTcpUdpPort != null) {
+		    match.setTransportDestination(matchDstTcpUdpPort);
+		    match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_TP_DST);
+		}
+
+		//
+		// Fetch the actions
+		//
+		Short actionOutputPort = null;
+		List<OFAction> openFlowActions = new ArrayList<OFAction>();
+		int actionsLen = 0;
+		FlowEntryActions flowEntryActions = null;
+		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();
+		    ActionSetVlanPriority actionSetVlanPriority = action.actionSetVlanPriority();
+		    ActionStripVlan actionStripVlan = action.actionStripVlan();
+		    ActionSetEthernetAddr actionSetEthernetSrcAddr = action.actionSetEthernetSrcAddr();
+		    ActionSetEthernetAddr actionSetEthernetDstAddr = action.actionSetEthernetDstAddr();
+		    ActionSetIPv4Addr actionSetIPv4SrcAddr = action.actionSetIPv4SrcAddr();
+		    ActionSetIPv4Addr actionSetIPv4DstAddr = action.actionSetIPv4DstAddr();
+		    ActionSetIpToS actionSetIpToS = action.actionSetIpToS();
+		    ActionSetTcpUdpPort actionSetTcpUdpSrcPort = action.actionSetTcpUdpSrcPort();
+		    ActionSetTcpUdpPort actionSetTcpUdpDstPort = action.actionSetTcpUdpDstPort();
+		    ActionEnqueue actionEnqueue = action.actionEnqueue();
+
+		    if (actionOutput != null) {
+				actionOutputPort = actionOutput.port().value();
+				// XXX: The max length is hard-coded for now
+				OFActionOutput ofa =
+				    new OFActionOutput(actionOutput.port().value(),
+						       (short)0xffff);
+				openFlowActions.add(ofa);
+				actionsLen += ofa.getLength();
+		    }
+
+		    if (actionSetVlanId != null) {
+				OFActionVirtualLanIdentifier ofa =
+				    new OFActionVirtualLanIdentifier(actionSetVlanId.vlanId());
+				openFlowActions.add(ofa);
+				actionsLen += ofa.getLength();
+		    }
+
+		    if (actionSetVlanPriority != null) {
+				OFActionVirtualLanPriorityCodePoint ofa =
+				    new OFActionVirtualLanPriorityCodePoint(actionSetVlanPriority.vlanPriority());
+				openFlowActions.add(ofa);
+				actionsLen += ofa.getLength();
+		    }
+
+		    if (actionStripVlan != null) {
+				if (actionStripVlan.stripVlan() == true) {
+				    OFActionStripVirtualLan ofa = new OFActionStripVirtualLan();
+				    openFlowActions.add(ofa);
+				    actionsLen += ofa.getLength();
+				}
+		    }
+
+		    if (actionSetEthernetSrcAddr != null) {
+				OFActionDataLayerSource ofa = 
+				    new OFActionDataLayerSource(actionSetEthernetSrcAddr.addr().toBytes());
+				openFlowActions.add(ofa);
+				actionsLen += ofa.getLength();
+		    }
+
+		    if (actionSetEthernetDstAddr != null) {
+				OFActionDataLayerDestination ofa =
+				    new OFActionDataLayerDestination(actionSetEthernetDstAddr.addr().toBytes());
+				openFlowActions.add(ofa);
+				actionsLen += ofa.getLength();
+		    }
+
+		    if (actionSetIPv4SrcAddr != null) {
+				OFActionNetworkLayerSource ofa =
+				    new OFActionNetworkLayerSource(actionSetIPv4SrcAddr.addr().value());
+				openFlowActions.add(ofa);
+				actionsLen += ofa.getLength();
+		    }
+
+		    if (actionSetIPv4DstAddr != null) {
+				OFActionNetworkLayerDestination ofa =
+				    new OFActionNetworkLayerDestination(actionSetIPv4DstAddr.addr().value());
+				openFlowActions.add(ofa);
+				actionsLen += ofa.getLength();
+		    }
+
+		    if (actionSetIpToS != null) {
+				OFActionNetworkTypeOfService ofa =
+				    new OFActionNetworkTypeOfService(actionSetIpToS.ipToS());
+				openFlowActions.add(ofa);
+				actionsLen += ofa.getLength();
+		    }
+
+		    if (actionSetTcpUdpSrcPort != null) {
+				OFActionTransportLayerSource ofa =
+				    new OFActionTransportLayerSource(actionSetTcpUdpSrcPort.port());
+				openFlowActions.add(ofa);
+				actionsLen += ofa.getLength();
+		    }
+
+		    if (actionSetTcpUdpDstPort != null) {
+				OFActionTransportLayerDestination ofa =
+				    new OFActionTransportLayerDestination(actionSetTcpUdpDstPort.port());
+				openFlowActions.add(ofa);
+				actionsLen += ofa.getLength();
+		    }
+
+		    if (actionEnqueue != null) {
+				OFActionEnqueue ofa =
+				    new OFActionEnqueue(actionEnqueue.port().value(),
+							actionEnqueue.queueId());
+				openFlowActions.add(ofa);
+				actionsLen += ofa.getLength();
+		    }
+		}
+
+		fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
+		    .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
+		    .setPriority(PRIORITY_DEFAULT)
+		    .setBufferId(OFPacketOut.BUFFER_ID_NONE)
+		    .setCookie(cookie)
+		    .setCommand(flowModCommand)
+		    .setMatch(match)
+		    .setActions(openFlowActions)
+		    .setLengthU(OFFlowMod.MINIMUM_LENGTH + actionsLen);
+		fm.setOutPort(OFPort.OFPP_NONE.getValue());
+		if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
+		    (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
+		    if (actionOutputPort != null)
+			fm.setOutPort(actionOutputPort);
+		}
+
+		//
+		// TODO: Set the following flag
+		// fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
+		// See method ForwardingBase::pushRoute()
+		//
+
+		//
+		// Write the message to the switch
+		//
+		log.debug("MEASUREMENT: Installing flow entry " + userState +
+			  " into switch DPID: " +
+			  sw.getStringId() +
+			  " flowEntryId: " + flowEntryId.toString() +
+			  " srcMac: " + matchSrcMac + " dstMac: " + matchDstMac +
+			  " inPort: " + matchInPort + " outPort: " + actionOutputPort
+			  );
+		add(sw,fm);
+	    //
+	    // TODO: We should use the OpenFlow Barrier mechanism
+	    // to check for errors, and update the SwitchState
+	    // for a flow entry after the Barrier message is
+	    // is received.
+	    //
+	    flowEntryObj.setSwitchState("FE_SWITCH_UPDATED");
+
+		return true;
+	}
+	
+	public boolean add(IOFSwitch sw, FlowPath flowPath, FlowEntry flowEntry) {
+		//
+		// Create the OpenFlow Flow Modification Entry to push
+		//
+		OFFlowMod fm = (OFFlowMod) factory.getMessage(OFType.FLOW_MOD);
+		long cookie = flowEntry.flowEntryId().value();
+
+		short flowModCommand = OFFlowMod.OFPFC_ADD;
+		if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_ADD) {
+			flowModCommand = OFFlowMod.OFPFC_ADD;
+		} else if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_MODIFY) {
+			flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
+		} else if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_DELETE) {
+			flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
+		} else {
+			// Unknown user state. Ignore the entry
+			log.debug(
+					"Flow Entry ignored (FlowEntryId = {}): unknown user state {}",
+					flowEntry.flowEntryId().toString(),
+					flowEntry.flowEntryUserState());
+			return false;
+		}
+
+		//
+		// Fetch the match conditions.
+		//
+		// NOTE: The Flow matching conditions common for all Flow Entries are
+		// used ONLY if a Flow Entry does NOT have the corresponding matching
+		// condition set.
+		//
+		OFMatch match = new OFMatch();
+		match.setWildcards(OFMatch.OFPFW_ALL);
+		FlowEntryMatch flowPathMatch = flowPath.flowEntryMatch();
+		FlowEntryMatch flowEntryMatch = flowEntry.flowEntryMatch();
+
+		// Match the Incoming Port
+		Port matchInPort = flowEntryMatch.inPort();
+		if (matchInPort != null) {
+			match.setInputPort(matchInPort.value());
+			match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_IN_PORT);
+		}
+
+		// Match the Source MAC address
+		MACAddress matchSrcMac = flowEntryMatch.srcMac();
+		if ((matchSrcMac == null) && (flowPathMatch != null)) {
+			matchSrcMac = flowPathMatch.srcMac();
+		}
+		if (matchSrcMac != null) {
+			match.setDataLayerSource(matchSrcMac.toString());
+			match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
+		}
+
+		// Match the Destination MAC address
+		MACAddress matchDstMac = flowEntryMatch.dstMac();
+		if ((matchDstMac == null) && (flowPathMatch != null)) {
+			matchDstMac = flowPathMatch.dstMac();
+		}
+		if (matchDstMac != null) {
+			match.setDataLayerDestination(matchDstMac.toString());
+			match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
+		}
+
+		// Match the Ethernet Frame Type
+		Short matchEthernetFrameType = flowEntryMatch.ethernetFrameType();
+		if ((matchEthernetFrameType == null) && (flowPathMatch != null)) {
+			matchEthernetFrameType = flowPathMatch.ethernetFrameType();
+		}
+		if (matchEthernetFrameType != null) {
+			match.setDataLayerType(matchEthernetFrameType);
+			match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
+		}
+
+		// Match the VLAN ID
+		Short matchVlanId = flowEntryMatch.vlanId();
+		if ((matchVlanId == null) && (flowPathMatch != null)) {
+			matchVlanId = flowPathMatch.vlanId();
+		}
+		if (matchVlanId != null) {
+			match.setDataLayerVirtualLan(matchVlanId);
+			match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_VLAN);
+		}
+
+		// Match the VLAN priority
+		Byte matchVlanPriority = flowEntryMatch.vlanPriority();
+		if ((matchVlanPriority == null) && (flowPathMatch != null)) {
+			matchVlanPriority = flowPathMatch.vlanPriority();
+		}
+		if (matchVlanPriority != null) {
+			match.setDataLayerVirtualLanPriorityCodePoint(matchVlanPriority);
+			match.setWildcards(match.getWildcards()
+					& ~OFMatch.OFPFW_DL_VLAN_PCP);
+		}
+
+		// Match the Source IPv4 Network prefix
+		IPv4Net matchSrcIPv4Net = flowEntryMatch.srcIPv4Net();
+		if ((matchSrcIPv4Net == null) && (flowPathMatch != null)) {
+			matchSrcIPv4Net = flowPathMatch.srcIPv4Net();
+		}
+		if (matchSrcIPv4Net != null) {
+			match.setFromCIDR(matchSrcIPv4Net.toString(), OFMatch.STR_NW_SRC);
+		}
+
+		// Natch the Destination IPv4 Network prefix
+		IPv4Net matchDstIPv4Net = flowEntryMatch.dstIPv4Net();
+		if ((matchDstIPv4Net == null) && (flowPathMatch != null)) {
+			matchDstIPv4Net = flowPathMatch.dstIPv4Net();
+		}
+		if (matchDstIPv4Net != null) {
+			match.setFromCIDR(matchDstIPv4Net.toString(), OFMatch.STR_NW_DST);
+		}
+
+		// Match the IP protocol
+		Byte matchIpProto = flowEntryMatch.ipProto();
+		if ((matchIpProto == null) && (flowPathMatch != null)) {
+			matchIpProto = flowPathMatch.ipProto();
+		}
+		if (matchIpProto != null) {
+			match.setNetworkProtocol(matchIpProto);
+			match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_NW_PROTO);
+		}
+
+		// Match the IP ToS (DSCP field, 6 bits)
+		Byte matchIpToS = flowEntryMatch.ipToS();
+		if ((matchIpToS == null) && (flowPathMatch != null)) {
+			matchIpToS = flowPathMatch.ipToS();
+		}
+		if (matchIpToS != null) {
+			match.setNetworkTypeOfService(matchIpToS);
+			match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_NW_TOS);
+		}
+
+		// Match the Source TCP/UDP port
+		Short matchSrcTcpUdpPort = flowEntryMatch.srcTcpUdpPort();
+		if ((matchSrcTcpUdpPort == null) && (flowPathMatch != null)) {
+			matchSrcTcpUdpPort = flowPathMatch.srcTcpUdpPort();
+		}
+		if (matchSrcTcpUdpPort != null) {
+			match.setTransportSource(matchSrcTcpUdpPort);
+			match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_TP_SRC);
+		}
+
+		// Match the Destination TCP/UDP port
+		Short matchDstTcpUdpPort = flowEntryMatch.dstTcpUdpPort();
+		if ((matchDstTcpUdpPort == null) && (flowPathMatch != null)) {
+			matchDstTcpUdpPort = flowPathMatch.dstTcpUdpPort();
+		}
+		if (matchDstTcpUdpPort != null) {
+			match.setTransportDestination(matchDstTcpUdpPort);
+			match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_TP_DST);
+		}
+
+		//
+		// Fetch the actions
+		//
+		Short actionOutputPort = null;
+		List<OFAction> openFlowActions = new ArrayList<OFAction>();
+		int actionsLen = 0;
+		FlowEntryActions flowEntryActions = flowEntry.flowEntryActions();
+		//
+		for (FlowEntryAction action : flowEntryActions.actions()) {
+			ActionOutput actionOutput = action.actionOutput();
+			ActionSetVlanId actionSetVlanId = action.actionSetVlanId();
+			ActionSetVlanPriority actionSetVlanPriority = action
+					.actionSetVlanPriority();
+			ActionStripVlan actionStripVlan = action.actionStripVlan();
+			ActionSetEthernetAddr actionSetEthernetSrcAddr = action
+					.actionSetEthernetSrcAddr();
+			ActionSetEthernetAddr actionSetEthernetDstAddr = action
+					.actionSetEthernetDstAddr();
+			ActionSetIPv4Addr actionSetIPv4SrcAddr = action
+					.actionSetIPv4SrcAddr();
+			ActionSetIPv4Addr actionSetIPv4DstAddr = action
+					.actionSetIPv4DstAddr();
+			ActionSetIpToS actionSetIpToS = action.actionSetIpToS();
+			ActionSetTcpUdpPort actionSetTcpUdpSrcPort = action
+					.actionSetTcpUdpSrcPort();
+			ActionSetTcpUdpPort actionSetTcpUdpDstPort = action
+					.actionSetTcpUdpDstPort();
+			ActionEnqueue actionEnqueue = action.actionEnqueue();
+
+			if (actionOutput != null) {
+				actionOutputPort = actionOutput.port().value();
+				// XXX: The max length is hard-coded for now
+				OFActionOutput ofa = new OFActionOutput(actionOutput.port()
+						.value(), (short) 0xffff);
+				openFlowActions.add(ofa);
+				actionsLen += ofa.getLength();
+			}
+
+			if (actionSetVlanId != null) {
+				OFActionVirtualLanIdentifier ofa = new OFActionVirtualLanIdentifier(
+						actionSetVlanId.vlanId());
+				openFlowActions.add(ofa);
+				actionsLen += ofa.getLength();
+			}
+
+			if (actionSetVlanPriority != null) {
+				OFActionVirtualLanPriorityCodePoint ofa = new OFActionVirtualLanPriorityCodePoint(
+						actionSetVlanPriority.vlanPriority());
+				openFlowActions.add(ofa);
+				actionsLen += ofa.getLength();
+			}
+
+			if (actionStripVlan != null) {
+				if (actionStripVlan.stripVlan() == true) {
+					OFActionStripVirtualLan ofa = new OFActionStripVirtualLan();
+					openFlowActions.add(ofa);
+					actionsLen += ofa.getLength();
+				}
+			}
+
+			if (actionSetEthernetSrcAddr != null) {
+				OFActionDataLayerSource ofa = new OFActionDataLayerSource(
+						actionSetEthernetSrcAddr.addr().toBytes());
+				openFlowActions.add(ofa);
+				actionsLen += ofa.getLength();
+			}
+
+			if (actionSetEthernetDstAddr != null) {
+				OFActionDataLayerDestination ofa = new OFActionDataLayerDestination(
+						actionSetEthernetDstAddr.addr().toBytes());
+				openFlowActions.add(ofa);
+				actionsLen += ofa.getLength();
+			}
+
+			if (actionSetIPv4SrcAddr != null) {
+				OFActionNetworkLayerSource ofa = new OFActionNetworkLayerSource(
+						actionSetIPv4SrcAddr.addr().value());
+				openFlowActions.add(ofa);
+				actionsLen += ofa.getLength();
+			}
+
+			if (actionSetIPv4DstAddr != null) {
+				OFActionNetworkLayerDestination ofa = new OFActionNetworkLayerDestination(
+						actionSetIPv4DstAddr.addr().value());
+				openFlowActions.add(ofa);
+				actionsLen += ofa.getLength();
+			}
+
+			if (actionSetIpToS != null) {
+				OFActionNetworkTypeOfService ofa = new OFActionNetworkTypeOfService(
+						actionSetIpToS.ipToS());
+				openFlowActions.add(ofa);
+				actionsLen += ofa.getLength();
+			}
+
+			if (actionSetTcpUdpSrcPort != null) {
+				OFActionTransportLayerSource ofa = new OFActionTransportLayerSource(
+						actionSetTcpUdpSrcPort.port());
+				openFlowActions.add(ofa);
+				actionsLen += ofa.getLength();
+			}
+
+			if (actionSetTcpUdpDstPort != null) {
+				OFActionTransportLayerDestination ofa = new OFActionTransportLayerDestination(
+						actionSetTcpUdpDstPort.port());
+				openFlowActions.add(ofa);
+				actionsLen += ofa.getLength();
+			}
+
+			if (actionEnqueue != null) {
+				OFActionEnqueue ofa = new OFActionEnqueue(actionEnqueue.port()
+						.value(), actionEnqueue.queueId());
+				openFlowActions.add(ofa);
+				actionsLen += ofa.getLength();
+			}
+		}
+
+		fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
+				.setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
+				.setPriority(PRIORITY_DEFAULT)
+				.setBufferId(OFPacketOut.BUFFER_ID_NONE).setCookie(cookie)
+				.setCommand(flowModCommand).setMatch(match)
+				.setActions(openFlowActions)
+				.setLengthU(OFFlowMod.MINIMUM_LENGTH + actionsLen);
+		fm.setOutPort(OFPort.OFPP_NONE.getValue());
+		if ((flowModCommand == OFFlowMod.OFPFC_DELETE)
+				|| (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
+			if (actionOutputPort != null)
+				fm.setOutPort(actionOutputPort);
+		}
+
+		//
+		// TODO: Set the following flag
+		// fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
+		// See method ForwardingBase::pushRoute()
+		//
+
+		//
+		// Write the message to the switch
+		//
+		log.debug("MEASUREMENT: Installing flow entry "
+				+ flowEntry.flowEntryUserState() + " into switch DPID: "
+				+ sw.getStringId() + " flowEntryId: "
+				+ flowEntry.flowEntryId().toString() + " srcMac: "
+				+ matchSrcMac + " dstMac: " + matchDstMac + " inPort: "
+				+ matchInPort + " outPort: " + actionOutputPort);
+		
+		//
+		// TODO: We should use the OpenFlow Barrier mechanism
+		// to check for errors, and update the SwitchState
+		// for a flow entry after the Barrier message is
+		// is received.
+		//
+		// TODO: The FlowEntry Object in Titan should be set
+		// to FE_SWITCH_UPDATED.
+		//
+		
+		return add(sw,fm);
+	}
+
+	private SwitchQueue getQueue(IOFSwitch sw) {
+		if (sw == null)  {
+			return null;
+		}
+		
+		return getProcess(sw).queues.get(sw);
+	}
+	
+	private long getHash(IOFSwitch sw) {
+		// TODO should consider equalization algorithm
+		return sw.getId() % number_thread;
+	}
+	
+	private FlowPusherProcess getProcess(IOFSwitch sw) {
+		long hash = getHash(sw);
+		
+		return threadMap.get(hash);
+	}
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowprogrammer/IFlowPusherService.java b/src/main/java/net/onrc/onos/ofcontroller/flowprogrammer/IFlowPusherService.java
new file mode 100644
index 0000000..146f19c
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowprogrammer/IFlowPusherService.java
@@ -0,0 +1,37 @@
+package net.onrc.onos.ofcontroller.flowprogrammer;
+
+import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.core.module.IFloodlightService;
+
+import org.openflow.protocol.OFMessage;
+
+public interface IFlowPusherService extends IFloodlightService {
+	/**
+	 * Add a message to the queue of a switch.
+	 * @param sw
+	 * @param msg
+	 * @return
+	 */
+	void addMessage(long dpid, OFMessage msg);
+	
+	/**
+	 * Suspend pushing message to a switch.
+	 * @param sw
+	 * @return true if success
+	 */
+	boolean suspend(long dpid);
+	
+	/**
+	 * Resume pushing message to a switch.
+	 * @param sw
+	 * @return true if success
+	 */
+	boolean resume(long dpid);
+	
+	/**
+	 * Get whether pushing of message is suspended or not.
+	 * @param sw
+	 * @return true if suspended
+	 */
+	boolean isSuspended(long dpid);
+}