Added Intent Subscriber and Module

Module is used to drive plan computation and installation
Also, added support for ADD/REMOVE in plan computation

Change-Id: Ib88eae8b13a1f5ed1503c5ff7762980f8ed032ac
diff --git a/src/main/java/net/onrc/onos/intent/runtime/PlanCalcRuntime.java b/src/main/java/net/onrc/onos/intent/runtime/PlanCalcRuntime.java
index 18e962f..63892cb 100644
--- a/src/main/java/net/onrc/onos/intent/runtime/PlanCalcRuntime.java
+++ b/src/main/java/net/onrc/onos/intent/runtime/PlanCalcRuntime.java
@@ -12,9 +12,10 @@
 import net.floodlightcontroller.util.MACAddress;
 import net.onrc.onos.intent.FlowEntry;
 import net.onrc.onos.intent.Intent;
+import net.onrc.onos.intent.IntentOperation;
+import net.onrc.onos.intent.IntentOperation.Operator;
 import net.onrc.onos.intent.IntentOperationList;
 import net.onrc.onos.intent.PathIntent;
-import net.onrc.onos.intent.PathIntentMap;
 import net.onrc.onos.intent.ShortestPathIntent;
 import net.onrc.onos.ofcontroller.networkgraph.Link;
 import net.onrc.onos.ofcontroller.networkgraph.LinkEvent;
@@ -29,88 +30,103 @@
  */
 
 public class PlanCalcRuntime {
-	NetworkGraph graph;
-	protected PathIntentMap intents;
-	protected Set<Collection<FlowEntry>> flowEntries;
-	protected List<Set<FlowEntry>> plan;
 
-	public PlanCalcRuntime(NetworkGraph graph) {
-		this.graph = graph;
-		this.flowEntries = new HashSet<>();
-		this.plan = new ArrayList<>();
-		this.intents = new PathIntentMap();
+    NetworkGraph graph;
+
+    public PlanCalcRuntime(NetworkGraph graph) {
+	this.graph = graph;
+    }
+
+    public List<Set<FlowEntry>> computePlan(IntentOperationList intentOps) {
+	Set<Collection<FlowEntry>> flowEntries = computeFlowEntries(intentOps);
+	return buildPhases(flowEntries);
+    }
+
+    private Set<Collection<FlowEntry>> computeFlowEntries(IntentOperationList intentOps) {
+	Set<Collection<FlowEntry>> flowEntries = new HashSet<>();
+	for(IntentOperation i : intentOps) {
+	    PathIntent intent = (PathIntent) i.intent;
+	    Intent parent = intent.getParentIntent();
+	    Port srcPort, dstPort, lastDstPort = null;
+	    MACAddress srcMac, dstMac;
+	    if(parent instanceof ShortestPathIntent) {
+		ShortestPathIntent pathIntent = (ShortestPathIntent) parent;
+		Switch srcSwitch = graph.getSwitch(pathIntent.getSrcSwitchDpid());
+		srcPort = srcSwitch.getPort(pathIntent.getSrcPortNumber());
+		srcMac = MACAddress.valueOf(pathIntent.getSrcMac());
+		dstMac = MACAddress.valueOf(pathIntent.getDstMac());
+		Switch dstSwitch = graph.getSwitch(pathIntent.getDstSwitchDpid());
+		lastDstPort = dstSwitch.getPort(pathIntent.getDstPortNumber());
+	    }
+	    else {
+		// TODO: log this error
+		continue;
+	    }
+	    List<FlowEntry> entries = new ArrayList<>();
+	    for(LinkEvent linkEvent : intent.getPath()) {
+		Link link = graph.getLink(linkEvent.getSrc().getDpid(),
+			  linkEvent.getSrc().getNumber(),
+			  linkEvent.getDst().getDpid(),
+			  linkEvent.getDst().getNumber());
+		Switch sw = link.getSrcSwitch();
+		dstPort = link.getSrcPort();
+		FlowEntry fe = new FlowEntry(sw, srcPort, dstPort, srcMac, dstMac, i.operator);
+		entries.add(fe);
+		srcPort = link.getDstPort();
+	    }
+	    if(lastDstPort != null) {
+		Switch sw = lastDstPort.getSwitch();
+		dstPort = lastDstPort;
+		FlowEntry fe = new FlowEntry(sw, srcPort, dstPort, srcMac, dstMac, i.operator);
+		entries.add(fe);
+	    }
+	    // install flow entries in reverse order
+	    Collections.reverse(entries);
+	    flowEntries.add(entries);
 	}
+	return flowEntries;
+    }
 
-	public void addIntents(IntentOperationList intentOpList) {
-		intents.executeOperations(intentOpList);
-		computeFlowEntries();
-		constructPlan();
-	}
-
-	public List<Set<FlowEntry>> getPlan() {
-		return plan;
-	}
-
-	public void computeFlowEntries() {
-		for(Intent i : intents.getAllIntents()) {
-			PathIntent intent = (PathIntent)i;
-			Intent parent = intent.getParentIntent();
-			Port srcPort, dstPort, lastDstPort = null;
-			MACAddress srcMac, dstMac;
-			if(parent instanceof ShortestPathIntent) {
-				ShortestPathIntent pathIntent = (ShortestPathIntent) parent;
-				Switch srcSwitch = graph.getSwitch(pathIntent.getSrcSwitchDpid());
-				srcPort = srcSwitch.getPort(pathIntent.getSrcPortNumber());
-				srcMac = MACAddress.valueOf(pathIntent.getSrcMac());
-				dstMac = MACAddress.valueOf(pathIntent.getDstMac());
-				Switch dstSwitch = graph.getSwitch(pathIntent.getDstSwitchDpid());
-				lastDstPort = dstSwitch.getPort(pathIntent.getDstPortNumber());
-			}
-			else {
-				// TODO: log this error
-				continue;
-			}
-			List<FlowEntry> entries = new ArrayList<>();
-			for(LinkEvent linkEvent : intent.getPath()) {
-				Link link = graph.getLink(linkEvent.getSrc().getDpid(),
-							  linkEvent.getSrc().getNumber(),
-							  linkEvent.getDst().getDpid(),
-							  linkEvent.getDst().getNumber());
-				Switch sw = link.getSrcSwitch();
-				dstPort = link.getSrcPort();
-				FlowEntry fe = new FlowEntry(sw, srcPort, dstPort, srcMac, dstMac);
-				entries.add(fe);
-				srcPort = link.getDstPort();
-			}
-			if(lastDstPort != null) {
-				Switch sw = lastDstPort.getSwitch();
-				dstPort = lastDstPort;
-				FlowEntry fe = new FlowEntry(sw, srcPort, dstPort, srcMac, dstMac);
-				entries.add(fe);
-			}
-			// install flow entries in reverse order
-			Collections.reverse(entries);
-			flowEntries.add(entries);
+    private List<Set<FlowEntry>> buildPhases(Set<Collection<FlowEntry>> flowEntries) {
+	Map<FlowEntry, Integer> map = new HashMap<>();
+	List<Set<FlowEntry>> plan = new ArrayList<>();
+	for(Collection<FlowEntry> c : flowEntries) {
+	    for(FlowEntry e : c) {
+		Integer i = map.get(e);
+		if(i == null) {
+		    i = Integer.valueOf(0);
 		}
-	}
-
-	public void constructPlan() {
-		Map<FlowEntry, Integer> map = new HashMap<>();
-		for(Collection<FlowEntry> c : flowEntries) {
-			for(FlowEntry e: c) {
-				Integer i = map.get(e);
-				if(i == null) {
-					map.put(e, 1);
-				}
-				else {
-					i += 1;
-				}
-
-			}
+		switch(e.getOperator()) {
+		case ADD:
+		    i += 1;
+		    break;
+		case REMOVE:
+		    i -= 1;
+		    break;
 		}
-
-		// really simple first iteration of plan
-		//TODO: optimize the map in phases
-		plan.add(map.keySet());
+		map.put(e, i);
+		System.out.println(e + " " + e.getOperator());
+	    }
 	}
+		
+	// really simple first iteration of plan
+	//TODO: optimize the map in phases
+	Set<FlowEntry> phase = new HashSet<>();
+	for(FlowEntry e : map.keySet()) {
+	    Integer i = map.get(e);
+	    if(i == 0) {
+		continue;
+	    }
+	    else if(i > 0) {
+		e.setOperator(Operator.ADD);
+	    }
+	    else if(i < 0) {
+		e.setOperator(Operator.REMOVE);
+	    }
+	    phase.add(e);
+	}
+	plan.add(phase);
+		
+	return plan;
+    }
 }
diff --git a/src/main/java/net/onrc/onos/intent/runtime/PlanInstallModule.java b/src/main/java/net/onrc/onos/intent/runtime/PlanInstallModule.java
new file mode 100644
index 0000000..df4c762
--- /dev/null
+++ b/src/main/java/net/onrc/onos/intent/runtime/PlanInstallModule.java
@@ -0,0 +1,116 @@
+package net.onrc.onos.intent.runtime;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.module.FloodlightModuleContext;
+import net.floodlightcontroller.core.module.FloodlightModuleException;
+import net.floodlightcontroller.core.module.IFloodlightModule;
+import net.floodlightcontroller.core.module.IFloodlightService;
+import net.onrc.onos.datagrid.IDatagridService;
+import net.onrc.onos.datagrid.IEventChannelListener;
+import net.onrc.onos.intent.FlowEntry;
+import net.onrc.onos.intent.IntentOperationList;
+import net.onrc.onos.ofcontroller.flowprogrammer.IFlowPusherService;
+import net.onrc.onos.ofcontroller.networkgraph.NetworkGraph;
+
+public class PlanInstallModule implements IFloodlightModule {
+    protected volatile IFloodlightProviderService floodlightProvider;
+    protected volatile NetworkGraph networkGraph;
+    protected volatile IDatagridService datagridService;
+    protected volatile IFlowPusherService flowPusher;
+    private PlanCalcRuntime planCalc;
+    private PlanInstallRuntime planInstall;
+    private EventListener eventListener;
+
+    private static final String PATH_INTENT_CHANNEL_NAME = "onos.pathintent";
+    
+    @Override
+    public void init(FloodlightModuleContext context)
+	    throws FloodlightModuleException {
+	floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
+	//networkGraph = context.getServiceImpl(INetworkGraphService.class);
+	datagridService = context.getServiceImpl(IDatagridService.class);
+	flowPusher = context.getServiceImpl(IFlowPusherService.class);
+	planCalc = new PlanCalcRuntime(networkGraph);
+	planInstall = new PlanInstallRuntime(networkGraph, floodlightProvider, flowPusher);
+	eventListener = new EventListener();
+    }
+
+    class EventListener extends Thread
+    	implements IEventChannelListener<Long, IntentOperationList> {
+	
+	private BlockingQueue<IntentOperationList> intentQueue = new LinkedBlockingQueue<>();
+	
+	@Override
+	public void run() {
+	    while(true) {
+		try {
+		    IntentOperationList intents = intentQueue.take();
+		    //TODO: drain the remaining intent lists
+		    processIntents(intents);
+		} catch (InterruptedException e) {
+		    //TODO: log the exception
+		}
+	    }
+	}
+	
+	private void processIntents(IntentOperationList intents) {
+	    List<Set<FlowEntry>> plan = planCalc.computePlan(intents);
+	    planInstall.installPlan(plan);
+	}
+	
+	@Override
+	public void entryAdded(IntentOperationList value) {
+	    intentQueue.add(value);
+	}
+
+	@Override
+	public void entryRemoved(IntentOperationList value) {
+	    // This channel is a queue, so this method is not needed
+	}
+
+	@Override
+	public void entryUpdated(IntentOperationList value) {
+	    // This channel is a queue, so this method is not needed
+	}
+    }
+    @Override
+    public void startUp(FloodlightModuleContext context) {
+	eventListener.start();
+	datagridService.addListener(PATH_INTENT_CHANNEL_NAME, 
+				    new EventListener(), 
+				    Long.class, 
+				    IntentOperationList.class);
+    }
+    
+    @Override
+    public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
+	Collection<Class<? extends IFloodlightService>> l =
+		new ArrayList<Class<? extends IFloodlightService>>();
+	l.add(IFloodlightProviderService.class);
+//	l.add(INetworkGraphService.class);
+	l.add(IDatagridService.class);
+	l.add(IFlowPusherService.class);
+	return l;
+    }
+    
+    @Override
+    public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+	// TODO Auto-generated method stub
+	return null;
+    }
+
+    @Override
+    public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
+	// TODO Auto-generated method stub
+	return null;
+    }
+
+}
diff --git a/src/main/java/net/onrc/onos/intent/runtime/PathInstallRuntime.java b/src/main/java/net/onrc/onos/intent/runtime/PlanInstallRuntime.java
similarity index 81%
rename from src/main/java/net/onrc/onos/intent/runtime/PathInstallRuntime.java
rename to src/main/java/net/onrc/onos/intent/runtime/PlanInstallRuntime.java
index 0b74cfc..3e4ff4a 100644
--- a/src/main/java/net/onrc/onos/intent/runtime/PathInstallRuntime.java
+++ b/src/main/java/net/onrc/onos/intent/runtime/PlanInstallRuntime.java
@@ -18,22 +18,23 @@
  *
  */
 
-public class PathInstallRuntime {
+public class PlanInstallRuntime {
     NetworkGraph graph;
     IFlowPusherService pusher;
     IFloodlightProviderService provider;
-    protected List<Set<FlowEntry>> plan;
 
-    public PathInstallRuntime(NetworkGraph graph) {
+    public PlanInstallRuntime(NetworkGraph graph, 
+	    		      IFloodlightProviderService provider,
+	                      IFlowPusherService pusher) {
 	this.graph = graph;
+	this.provider = provider;
+	this.pusher = pusher;
     }
 
     public void installPlan(List<Set<FlowEntry>> plan) {
-	this.plan = plan;
 	Map<Long,IOFSwitch> switches = provider.getSwitches();
 	for(Set<FlowEntry> phase : plan) {
-	    Set<Pair<IOFSwitch, net.onrc.onos.ofcontroller.util.FlowEntry>> entries 
-	    = new HashSet<>();
+	    Set<Pair<IOFSwitch, net.onrc.onos.ofcontroller.util.FlowEntry>> entries = new HashSet<>();
 	    // convert flow entries and create pairs
 	    for(FlowEntry entry : phase) {
 		entries.add(new Pair<>(switches.get(entry.getSwitch().getDpid()),