Support state change notifications

- defined state change event channel name
- added IEventChannelListener implementations to PathCalcRuntimeModule class
- register IntentStateList class to KryoFactory

Change-Id: Ibf09ca0d400954cba27e4dbc3f8c97a91d261435
diff --git a/src/main/java/net/onrc/onos/intent/IntentMap.java b/src/main/java/net/onrc/onos/intent/IntentMap.java
index 53667ad..8e2e125 100644
--- a/src/main/java/net/onrc/onos/intent/IntentMap.java
+++ b/src/main/java/net/onrc/onos/intent/IntentMap.java
@@ -5,10 +5,10 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
-import java.util.Map;
 import java.util.Map.Entry;
 
 import net.onrc.onos.intent.Intent.IntentState;
+import net.onrc.onos.intent.runtime.IntentStateList;
 
 /**
  * @author Toshio Koide (t-koide@onlab.us)
@@ -88,7 +88,7 @@
 		notifyEvents();
 	}
 
-	public void changeStates(Map<String, IntentState> states) {
+	public void changeStates(IntentStateList states) {
 		for (Entry<String, IntentState> state: states.entrySet()) {
 			setState(state.getKey(), state.getValue());
 		}
@@ -167,6 +167,7 @@
 		case CREATED:
 		case INST_REQ:
 		case INST_ACK:
+		case REROUTE_REQ:
 			setState(targetIntent.getId(), IntentState.INST_NACK);
 			break;
 		case DEL_REQ:
diff --git a/src/main/java/net/onrc/onos/intent/runtime/IntentStateList.java b/src/main/java/net/onrc/onos/intent/runtime/IntentStateList.java
new file mode 100644
index 0000000..4d2fc75
--- /dev/null
+++ b/src/main/java/net/onrc/onos/intent/runtime/IntentStateList.java
@@ -0,0 +1,12 @@
+package net.onrc.onos.intent.runtime;
+
+import java.util.HashMap;
+
+import net.onrc.onos.intent.Intent.IntentState;
+
+/**
+ * @author Toshio Koide (t-koide@onlab.us)
+ */
+public class IntentStateList extends HashMap<String, IntentState> {
+	private static final long serialVersionUID = -3674903999581438936L;
+}
diff --git a/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntimeModule.java b/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntimeModule.java
index b559028..67902dd 100755
--- a/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntimeModule.java
+++ b/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntimeModule.java
@@ -6,6 +6,7 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.Map.Entry;
 
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.core.module.FloodlightModuleException;
@@ -13,6 +14,8 @@
 import net.floodlightcontroller.core.module.IFloodlightService;
 import net.onrc.onos.datagrid.IDatagridService;
 import net.onrc.onos.datagrid.IEventChannel;
+import net.onrc.onos.datagrid.IEventChannelListener;
+import net.onrc.onos.intent.Intent;
 import net.onrc.onos.intent.Intent.IntentState;
 import net.onrc.onos.intent.IntentMap;
 import net.onrc.onos.intent.IntentOperation;
@@ -20,6 +23,7 @@
 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.intent.persist.PersistIntent;
 import net.onrc.onos.ofcontroller.networkgraph.DeviceEvent;
 import net.onrc.onos.ofcontroller.networkgraph.INetworkGraphListener;
@@ -32,7 +36,7 @@
 /**
  * @author Toshio Koide (t-koide@onlab.us)
  */
-public class PathCalcRuntimeModule implements IFloodlightModule, IPathCalcRuntimeService, INetworkGraphListener {
+public class PathCalcRuntimeModule implements IFloodlightModule, IPathCalcRuntimeService, INetworkGraphListener, IEventChannelListener<Long, IntentStateList> {
 	private PathCalcRuntime runtime;
 	private IDatagridService datagridService;
 	private INetworkGraphService networkGraphService;
@@ -41,8 +45,9 @@
 	private IControllerRegistryService controllerRegistry;
 	private PersistIntent persistIntent;
 
-	private IEventChannel<Long, IntentOperationList> eventChannel;
-	private static final String EVENT_CHANNEL_NAME = "onos.pathintent";
+	private IEventChannel<Long, IntentOperationList> opEventChannel;
+	private static final String INTENT_OP_EVENT_CHANNEL_NAME = "onos.pathintent";
+	private static final String INTENT_STATE_EVENT_CHANNEL_NAME = "onos.pathintent_state";
 
 	// ================================================================================
 	// private methods
@@ -105,10 +110,10 @@
 		highLevelIntents = new IntentMap();
 		runtime = new PathCalcRuntime(networkGraphService.getNetworkGraph());
 		pathIntents = new PathIntentMap();
-		eventChannel = datagridService.createChannel(EVENT_CHANNEL_NAME, Long.class, IntentOperationList.class);
+		opEventChannel = datagridService.createChannel(INTENT_OP_EVENT_CHANNEL_NAME, Long.class, IntentOperationList.class);
+		datagridService.addListener(INTENT_STATE_EVENT_CHANNEL_NAME, this, Long.class, IntentStateList.class);
 		networkGraphService.registerNetworkGraphListener(this);
 		persistIntent = new PersistIntent(controllerRegistry, networkGraphService);
-
 	}
 
 	// ================================================================================
@@ -121,7 +126,7 @@
 		highLevelIntents.executeOperations(list);
 
 		// change states of high-level intents
-		HashMap<String, IntentState> states = new HashMap<>();
+		IntentStateList states = new IntentStateList();
 		for (IntentOperation op : list) {
 			String id = op.intent.getId();
 			if (op.intent.getState().equals(IntentState.INST_ACK))
@@ -161,7 +166,7 @@
 				op.intent = pathIntents.getIntent(op.intent.getId());
 			}
 		}
-		eventChannel.addEntry(key, pathIntentOperations);
+		opEventChannel.addEntry(key, pathIntentOperations);
 		return pathIntentOperations;
 	}
 
@@ -194,8 +199,51 @@
 			Collection<LinkEvent> removedLinkEvents,
 			Collection<DeviceEvent> addedDeviceEvents,
 			Collection<DeviceEvent> removedDeviceEvents) {
-		// TODO need optimization.
-		// Only high-level intents that affected by link down are rerouted.
+		// TODO add getIntentsByPort() and getIntentsBySwitch() to PathIntentMap.
 		reroutePaths(removedLinkEvents);
 	}
+
+	// ================================================================================
+	// IEventChannelListener implementations
+	// ================================================================================
+
+	@Override
+	public void entryAdded(IntentStateList value) {
+		entryUpdated(value);
+	}
+
+	@Override
+	public void entryRemoved(IntentStateList value) {
+		// do nothing
+	}
+
+	@Override
+	public void entryUpdated(IntentStateList value) {
+		// reflect state changes of path-level intent into application-level intents
+		IntentStateList parentStates = new IntentStateList();
+		for (Entry<String, IntentState> entry: value.entrySet()) {
+			PathIntent pathIntent = (PathIntent) pathIntents.getIntent(entry.getKey());
+			if (pathIntent == null) continue;
+
+			Intent parentIntent = pathIntent.getParentIntent();
+			if (parentIntent == null ||
+					!(parentIntent instanceof ShortestPathIntent) ||
+					!((ShortestPathIntent) parentIntent).getPathIntentId().equals(pathIntent.getId()))
+				continue;
+
+			IntentState state = entry.getValue();
+			switch (state) {
+			case INST_ACK:
+			case INST_NACK:
+			case DEL_ACK:
+			case DEL_PENDING:
+				parentStates.put(parentIntent.getId(), state);
+				break;
+			default:
+				break;
+			}
+		}
+		highLevelIntents.changeStates(parentStates);
+		pathIntents.changeStates(value);
+	}
 }
\ No newline at end of file
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
index 07b173d..df93614 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/serializers/KryoFactory.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/serializers/KryoFactory.java
@@ -12,6 +12,7 @@
 import net.onrc.onos.intent.IntentOperationList;
 import net.onrc.onos.intent.PathIntent;
 import net.onrc.onos.intent.ShortestPathIntent;
+import net.onrc.onos.intent.runtime.IntentStateList;
 import net.onrc.onos.ofcontroller.networkgraph.DeviceEvent;
 import net.onrc.onos.ofcontroller.networkgraph.LinkEvent;
 import net.onrc.onos.ofcontroller.networkgraph.Path;
@@ -44,6 +45,7 @@
 import net.onrc.onos.ofcontroller.util.Switch;
 // import net.onrc.onos.ofcontroller.util.SwitchPort;
 
+
 import com.esotericsoftware.kryo.Kryo;
 
 /**
@@ -186,6 +188,7 @@
 	kryo.register(Intent.IntentState.class);
 	kryo.register(Path.class);
 	kryo.register(IntentOperation.Operator.class);
+	kryo.register(IntentStateList.class);
 
 	return kryo;
     }
diff --git a/src/test/java/net/onrc/onos/intent/ConstrainedShortestPathIntentTest.java b/src/test/java/net/onrc/onos/intent/ConstrainedShortestPathIntentTest.java
index 354e053..e8759b1 100644
--- a/src/test/java/net/onrc/onos/intent/ConstrainedShortestPathIntentTest.java
+++ b/src/test/java/net/onrc/onos/intent/ConstrainedShortestPathIntentTest.java
@@ -11,6 +11,9 @@
 import com.esotericsoftware.kryo.io.Input;
 import com.esotericsoftware.kryo.io.Output;
 
+/**
+ * @author Toshio Koide (t-koide@onlab.us)
+ */
 public class ConstrainedShortestPathIntentTest {
 	NetworkGraph g;
 
diff --git a/src/test/java/net/onrc/onos/intent/IntentMapTest.java b/src/test/java/net/onrc/onos/intent/IntentMapTest.java
index bad546d..b636167 100644
--- a/src/test/java/net/onrc/onos/intent/IntentMapTest.java
+++ b/src/test/java/net/onrc/onos/intent/IntentMapTest.java
@@ -1,12 +1,21 @@
 package net.onrc.onos.intent;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import net.onrc.onos.intent.ErrorIntent.ErrorType;
 import net.onrc.onos.intent.Intent.IntentState;
+import net.onrc.onos.intent.IntentMap.ChangedEventType;
+import net.onrc.onos.intent.IntentOperation.Operator;
+import net.onrc.onos.intent.runtime.IntentStateList;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
+/**
+ * @author Toshio Koide (t-koide@onlab.us)
+ */
 public class IntentMapTest {
 
 	@Before
@@ -18,55 +27,212 @@
 	}
 
 	@Test
-	public void test() {
+	public void testCreate() {
 		IntentMap intents = new IntentMap();
-		IntentOperationList operations = new IntentOperationList();
+		assertEquals(0, intents.getAllIntents().size());
+	}
 
-		// add three intents
+	@Test
+	public void testChangedEventCreate() {
+		IntentMap intents = new IntentMap();
+		IntentMap.ChangedEvent event = intents.new ChangedEvent(
+				ChangedEventType.ADDED,
+				new Intent("id1"));
+		assertEquals(ChangedEventType.ADDED, event.eventType);
+		assertEquals("id1", event.intent.getId());
+	}
 
-		ShortestPathIntent intent1 =
-				new ShortestPathIntent("1", 11L, 12L, 13L, 14L, 15L, 16L);
+	@Test
+	public void testAddOperations() {
+		IntentMap intents = new IntentMap();
+		assertEquals(0, intents.getAllIntents().size());
+
+		Intent intent1 = new Intent("1");
 		ShortestPathIntent intent2 =
 				new ShortestPathIntent("2", 21L, 22L, 23L, 24L, 25L, 26L);
 		ConstrainedShortestPathIntent intent3 =
 				new ConstrainedShortestPathIntent("3", 31L, 32L, 33L, 34L, 35L, 36L, 1000.0);
 
-		operations.add(new IntentOperation(IntentOperation.Operator.ADD, intent1));
-		operations.add(new IntentOperation(IntentOperation.Operator.ADD, intent2));
-		operations.add(new IntentOperation(IntentOperation.Operator.ADD, intent3));
+		IntentOperationList operations = new IntentOperationList();
+		operations.add(Operator.ADD, intent1);
+		operations.add(Operator.ADD, intent2);
+		operations.add(Operator.ADD, intent3);
+		assertEquals(3, operations.size());
+
 		intents.executeOperations(operations);
-
-		// check
-
 		assertEquals(3, intents.getAllIntents().size());
-		assertEquals(intent1, intents.getIntent("1"));
-		assertEquals(intent2, intents.getIntent("2"));
-		assertEquals(intent3, intents.getIntent("3"));
+		assertSame(intent1, intents.getIntent("1"));
+		assertSame(intent2, intents.getIntent("2"));
+		assertSame(intent3, intents.getIntent("3"));
+	}
 
-		// request removal of an intent
+	@Test
+	public void testAddOperationsOverwrite() {
+		IntentMap intents = new IntentMap();
 
+		Intent intent1 = new Intent("1");
+		Intent intent2 = new Intent("2");
+		Intent intent3 = new Intent("3");
 		Intent intent4 = new Intent("1");
+		Intent intent5 = new Intent("2");
+		Intent intent6 = new Intent("4");
+
+		IntentOperationList operations = new IntentOperationList();
+		operations.add(Operator.ADD, intent1);
+		operations.add(Operator.ADD, intent2);
+		operations.add(Operator.ADD, intent3);
+		assertEquals(3, operations.size());
+
+		intents.executeOperations(operations);
+		assertEquals(3, intents.getAllIntents().size());
+		assertSame(intent1, intents.getIntent("1"));
+		assertSame(intent2, intents.getIntent("2"));
+		assertSame(intent3, intents.getIntent("3"));
+
 		operations.clear();
-		operations.add(new IntentOperation(IntentOperation.Operator.REMOVE, intent4));
+		operations.add(Operator.ADD, intent4);
+		operations.add(Operator.ADD, intent5);
+		operations.add(Operator.ADD, intent6);
+		assertEquals(3, operations.size());
+
+		intents.executeOperations(operations);
+		assertEquals(4, intents.getAllIntents().size());
+		assertSame(intent4, intents.getIntent("1"));
+		assertSame(intent5, intents.getIntent("2"));
+		assertSame(intent3, intents.getIntent("3"));
+		assertSame(intent6, intents.getIntent("4"));
+	}
+
+	@Test
+	public void testRemoveOperation() {
+		IntentMap intents = new IntentMap();
+
+		Intent intent1 = new Intent("1");
+		ShortestPathIntent intent2 =
+				new ShortestPathIntent("2", 21L, 22L, 23L, 24L, 25L, 26L);
+		ConstrainedShortestPathIntent intent3 =
+				new ConstrainedShortestPathIntent("3", 31L, 32L, 33L, 34L, 35L, 36L, 1000.0);
+
+		IntentOperationList operations = new IntentOperationList();
+		operations.add(Operator.ADD, intent1);
+		operations.add(Operator.ADD, intent2);
+		operations.add(Operator.ADD, intent3);
+		intents.executeOperations(operations);
+		assertEquals(3, intents.getAllIntents().size());
+		assertSame(intent1, intents.getIntent("1"));
+		assertSame(intent2, intents.getIntent("2"));
+		assertSame(intent3, intents.getIntent("3"));
+
+		operations.clear();
+		operations.add(Operator.REMOVE, new Intent("1"));
+		operations.add(Operator.REMOVE, new Intent("3"));
+		intents.executeOperations(operations);
+		assertEquals(3, intents.getAllIntents().size());
+		assertSame(intent1, intents.getIntent("1"));
+		assertSame(intent2, intents.getIntent("2"));
+		assertSame(intent3, intents.getIntent("3"));
+		assertEquals(IntentState.DEL_REQ, intents.getIntent("1").getState());
+		assertEquals(IntentState.CREATED, intents.getIntent("2").getState());
+		assertEquals(IntentState.DEL_REQ, intents.getIntent("3").getState());
+	}
+
+	@Test
+	public void testErrorOperation() {
+		IntentMap intents = new IntentMap();
+		IntentOperationList operations = new IntentOperationList();
+		operations.add(Operator.ADD, new Intent("1", IntentState.CREATED));
+		operations.add(Operator.ADD, new Intent("2", IntentState.INST_REQ));
+		operations.add(Operator.ADD, new Intent("3", IntentState.INST_ACK));
+		operations.add(Operator.ADD, new Intent("4", IntentState.INST_NACK));
+		operations.add(Operator.ADD, new Intent("5", IntentState.REROUTE_REQ));
+		operations.add(Operator.ADD, new Intent("6", IntentState.DEL_REQ));
+		operations.add(Operator.ADD, new Intent("7", IntentState.DEL_ACK));
+		operations.add(Operator.ADD, new Intent("8", IntentState.DEL_PENDING));
+		intents.executeOperations(operations);
+		assertEquals(8, intents.getAllIntents().size());
+
+		operations.clear();
+		operations.add(Operator.ERROR, new ErrorIntent(ErrorType.PATH_NOT_FOUND, "", new Intent("1")));
+		operations.add(Operator.ERROR, new ErrorIntent(ErrorType.PATH_NOT_FOUND, "", new Intent("2")));
+		operations.add(Operator.ERROR, new ErrorIntent(ErrorType.PATH_NOT_FOUND, "", new Intent("3")));
+		operations.add(Operator.ERROR, new ErrorIntent(ErrorType.PATH_NOT_FOUND, "", new Intent("4")));
+		operations.add(Operator.ERROR, new ErrorIntent(ErrorType.PATH_NOT_FOUND, "", new Intent("5")));
+		operations.add(Operator.ERROR, new ErrorIntent(ErrorType.PATH_NOT_FOUND, "", new Intent("6")));
+		operations.add(Operator.ERROR, new ErrorIntent(ErrorType.PATH_NOT_FOUND, "", new Intent("7")));
+		operations.add(Operator.ERROR, new ErrorIntent(ErrorType.PATH_NOT_FOUND, "", new Intent("8")));
 		intents.executeOperations(operations);
 
-		// check
+		assertEquals(IntentState.INST_NACK, intents.getIntent("1").getState());
+		assertEquals(IntentState.INST_NACK, intents.getIntent("2").getState());
+		assertEquals(IntentState.INST_NACK, intents.getIntent("3").getState());
+		assertEquals(IntentState.INST_NACK, intents.getIntent("4").getState());
+		assertEquals(IntentState.INST_NACK, intents.getIntent("5").getState());
+		assertEquals(IntentState.DEL_PENDING, intents.getIntent("6").getState());
+		assertEquals(IntentState.DEL_ACK, intents.getIntent("7").getState());
+		assertEquals(IntentState.DEL_PENDING, intents.getIntent("8").getState());
+	}
 
-		assertEquals(3, intents.getAllIntents().size());
-		assertEquals(IntentState.DEL_REQ, intent1.getState());
-
-		// change intents' state which will be purged 
-
-		intent2.setState(IntentState.INST_NACK);
-		intent3.setState(IntentState.DEL_ACK);
-
-		// purge
+	@Test
+	public void testPurge() {
+		IntentMap intents = new IntentMap();
+		IntentOperationList operations = new IntentOperationList();
+		operations.add(Operator.ADD, new Intent("1", IntentState.CREATED));
+		operations.add(Operator.ADD, new Intent("2", IntentState.INST_REQ));
+		operations.add(Operator.ADD, new Intent("3", IntentState.INST_ACK));
+		operations.add(Operator.ADD, new Intent("4", IntentState.INST_NACK));
+		operations.add(Operator.ADD, new Intent("5", IntentState.REROUTE_REQ));
+		operations.add(Operator.ADD, new Intent("6", IntentState.DEL_REQ));
+		operations.add(Operator.ADD, new Intent("7", IntentState.DEL_ACK));
+		operations.add(Operator.ADD, new Intent("8", IntentState.DEL_PENDING));
+		intents.executeOperations(operations);
+		assertEquals(8, intents.getAllIntents().size());
 
 		intents.purge();
 
-		// check
+		assertEquals(6, intents.getAllIntents().size());
+		assertEquals("1", intents.getIntent("1").getId());
+		assertEquals("2", intents.getIntent("2").getId());
+		assertEquals("3", intents.getIntent("3").getId());
+		assertNull(intents.getIntent("4"));
+		assertEquals("5", intents.getIntent("5").getId());
+		assertEquals("6", intents.getIntent("6").getId());
+		assertNull("7", intents.getIntent("7"));
+		assertEquals("8", intents.getIntent("8").getId());
+	}
 
-		assertEquals(1, intents.getAllIntents().size());
-		assertEquals("1", intents.getAllIntents().iterator().next().getId());
+	@Test
+	public void testChangeStates() {
+		IntentMap intents = new IntentMap();
+		IntentOperationList operations = new IntentOperationList();
+		operations.add(Operator.ADD, new Intent("1", IntentState.CREATED));
+		operations.add(Operator.ADD, new Intent("2", IntentState.INST_REQ));
+		operations.add(Operator.ADD, new Intent("3", IntentState.INST_ACK));
+		operations.add(Operator.ADD, new Intent("4", IntentState.INST_NACK));
+		operations.add(Operator.ADD, new Intent("5", IntentState.REROUTE_REQ));
+		operations.add(Operator.ADD, new Intent("6", IntentState.DEL_REQ));
+		operations.add(Operator.ADD, new Intent("7", IntentState.DEL_ACK));
+		operations.add(Operator.ADD, new Intent("8", IntentState.DEL_PENDING));
+		intents.executeOperations(operations);
+		assertEquals(8, intents.getAllIntents().size());
+
+		IntentStateList states = new IntentStateList();
+		states.put("8", IntentState.CREATED);
+		states.put("1", IntentState.INST_REQ);
+		states.put("2", IntentState.INST_ACK);
+		states.put("3", IntentState.INST_NACK);
+		states.put("4", IntentState.REROUTE_REQ);
+		states.put("5", IntentState.DEL_REQ);
+		states.put("6", IntentState.DEL_ACK);
+		states.put("7", IntentState.DEL_PENDING);
+		intents.changeStates(states);
+
+		assertEquals(IntentState.INST_REQ, intents.getIntent("1").getState());
+		assertEquals(IntentState.INST_ACK, intents.getIntent("2").getState());
+		assertEquals(IntentState.INST_NACK, intents.getIntent("3").getState());
+		assertEquals(IntentState.REROUTE_REQ, intents.getIntent("4").getState());
+		assertEquals(IntentState.DEL_REQ, intents.getIntent("5").getState());
+		assertEquals(IntentState.DEL_ACK, intents.getIntent("6").getState());
+		assertEquals(IntentState.DEL_PENDING, intents.getIntent("7").getState());
+		assertEquals(IntentState.CREATED, intents.getIntent("8").getState());
 	}
 }
diff --git a/src/test/java/net/onrc/onos/intent/IntentOperationListTest.java b/src/test/java/net/onrc/onos/intent/IntentOperationListTest.java
index ad5315b..e0e907f 100644
--- a/src/test/java/net/onrc/onos/intent/IntentOperationListTest.java
+++ b/src/test/java/net/onrc/onos/intent/IntentOperationListTest.java
@@ -13,6 +13,9 @@
 import com.esotericsoftware.kryo.io.Input;
 import com.esotericsoftware.kryo.io.Output;
 
+/**
+ * @author Toshio Koide (t-koide@onlab.us)
+ */
 public class IntentOperationListTest {
 
 	@Before
diff --git a/src/test/java/net/onrc/onos/intent/MockNetworkGraph.java b/src/test/java/net/onrc/onos/intent/MockNetworkGraph.java
index a8f928a..5ae7b13 100644
--- a/src/test/java/net/onrc/onos/intent/MockNetworkGraph.java
+++ b/src/test/java/net/onrc/onos/intent/MockNetworkGraph.java
@@ -8,6 +8,9 @@
 import net.onrc.onos.ofcontroller.networkgraph.Switch;
 import net.onrc.onos.ofcontroller.networkgraph.SwitchImpl;
 
+/**
+ * @author Toshio Koide (t-koide@onlab.us)
+ */
 public class MockNetworkGraph extends NetworkGraphImpl {
 	class DetachableLinkImpl extends LinkImpl {
 		public DetachableLinkImpl(NetworkGraph graph, Port srcPort, Port dstPort) {
@@ -16,7 +19,7 @@
 
 		public void detachFromGraph() {
 			unsetFromPorts();
-		}	
+		}
 	}
 	public Switch addSwitch(Long switchId) {
 		SwitchImpl sw = new SwitchImpl(this, switchId);
@@ -39,7 +42,7 @@
 
 		return links;
 	}
-	
+
 	public void createSampleTopology() {
 		// add 10 switches (24 ports switch)
 		for (Long dpid=1L; dpid<10L; dpid++) {
diff --git a/src/test/java/net/onrc/onos/intent/PathIntentTest.java b/src/test/java/net/onrc/onos/intent/PathIntentTest.java
index 3c76f3f..211a68a 100644
--- a/src/test/java/net/onrc/onos/intent/PathIntentTest.java
+++ b/src/test/java/net/onrc/onos/intent/PathIntentTest.java
@@ -14,6 +14,9 @@
 import com.esotericsoftware.kryo.io.Input;
 import com.esotericsoftware.kryo.io.Output;
 
+/**
+ * @author Toshio Koide (t-koide@onlab.us)
+ */
 public class PathIntentTest {
 	NetworkGraph g;
 
diff --git a/src/test/java/net/onrc/onos/intent/ShortestPathIntentTest.java b/src/test/java/net/onrc/onos/intent/ShortestPathIntentTest.java
index e325be8..e118b66 100644
--- a/src/test/java/net/onrc/onos/intent/ShortestPathIntentTest.java
+++ b/src/test/java/net/onrc/onos/intent/ShortestPathIntentTest.java
@@ -11,6 +11,9 @@
 import com.esotericsoftware.kryo.io.Input;
 import com.esotericsoftware.kryo.io.Output;
 
+/**
+ * @author Toshio Koide (t-koide@onlab.us)
+ */
 public class ShortestPathIntentTest {
 	NetworkGraph g;
 
diff --git a/src/test/java/net/onrc/onos/intent/runtime/UseCaseTest.java b/src/test/java/net/onrc/onos/intent/runtime/UseCaseTest.java
index 00da877..cb4ab9e 100755
--- a/src/test/java/net/onrc/onos/intent/runtime/UseCaseTest.java
+++ b/src/test/java/net/onrc/onos/intent/runtime/UseCaseTest.java
@@ -1,6 +1,7 @@
 package net.onrc.onos.intent.runtime;
 
-import java.util.HashMap;
+import static org.easymock.EasyMock.*;
+
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
@@ -9,12 +10,12 @@
 import net.floodlightcontroller.core.module.FloodlightModuleException;
 import net.onrc.onos.datagrid.IDatagridService;
 import net.onrc.onos.datagrid.IEventChannel;
+import net.onrc.onos.datagrid.IEventChannelListener;
 import net.onrc.onos.intent.ConstrainedShortestPathIntent;
 import net.onrc.onos.intent.FlowEntry;
 import net.onrc.onos.intent.Intent;
 import net.onrc.onos.intent.Intent.IntentState;
 import net.onrc.onos.intent.IntentOperation.Operator;
-import net.onrc.onos.intent.IntentOperation;
 import net.onrc.onos.intent.IntentOperationList;
 import net.onrc.onos.intent.MockNetworkGraph;
 import net.onrc.onos.intent.PathIntent;
@@ -30,7 +31,6 @@
 import net.onrc.onos.ofcontroller.networkgraph.SwitchEvent;
 import net.onrc.onos.registry.controller.IControllerRegistryService;
 
-import org.easymock.EasyMock;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -61,47 +61,54 @@
 		graph.createSampleTopology();
 		g = graph;
 
-		datagridService = EasyMock.createMock(IDatagridService.class);
-		networkGraphService = EasyMock.createMock(INetworkGraphService.class);
-		controllerRegistryService = EasyMock.createMock(IControllerRegistryService.class);
-		modContext = EasyMock.createMock(FloodlightModuleContext.class);
-		eventChannel = EasyMock.createMock(IEventChannel.class);
+		datagridService = createMock(IDatagridService.class);
+		networkGraphService = createMock(INetworkGraphService.class);
+		controllerRegistryService = createMock(IControllerRegistryService.class);
+		modContext = createMock(FloodlightModuleContext.class);
+		eventChannel = createMock(IEventChannel.class);
 		persistIntent = PowerMock.createMock(PersistIntent.class);
 
 		PowerMock.expectNew(PersistIntent.class,
-				EasyMock.anyObject(IControllerRegistryService.class),
-				EasyMock.anyObject(INetworkGraphService.class)).andReturn(persistIntent);
+				anyObject(IControllerRegistryService.class),
+				anyObject(INetworkGraphService.class)).andReturn(persistIntent);
 
-		EasyMock.expect(modContext.getServiceImpl(EasyMock.eq(IDatagridService.class)))
+		expect(modContext.getServiceImpl(IDatagridService.class))
 		.andReturn(datagridService).once();
-		EasyMock.expect(modContext.getServiceImpl(EasyMock.eq(INetworkGraphService.class)))
+		expect(modContext.getServiceImpl(INetworkGraphService.class))
 		.andReturn(networkGraphService).once();
-		EasyMock.expect(modContext.getServiceImpl(EasyMock.eq(IControllerRegistryService.class)))
+		expect(modContext.getServiceImpl(IControllerRegistryService.class))
 		.andReturn(controllerRegistryService).once();
-		EasyMock.expect(persistIntent.getKey()).andReturn(1L).anyTimes();
-		EasyMock.expect(persistIntent.persistIfLeader(EasyMock.eq(1L),
-				EasyMock.anyObject(IntentOperationList.class))).andReturn(true).anyTimes();
+		expect(persistIntent.getKey()).andReturn(1L).anyTimes();
+		expect(persistIntent.persistIfLeader(eq(1L),
+				anyObject(IntentOperationList.class))).andReturn(true).anyTimes();
 
-		EasyMock.expect(networkGraphService.getNetworkGraph()).andReturn(g).anyTimes();
-		networkGraphService.registerNetworkGraphListener(EasyMock.anyObject(INetworkGraphListener.class));
-		EasyMock.expectLastCall();
+		expect(networkGraphService.getNetworkGraph()).andReturn(g).anyTimes();
+		networkGraphService.registerNetworkGraphListener(anyObject(INetworkGraphListener.class));
+		expectLastCall();
 
-		EasyMock.expect(datagridService.createChannel("onos.pathintent", Long.class, IntentOperationList.class))
+		expect(datagridService.createChannel("onos.pathintent", Long.class, IntentOperationList.class))
 		.andReturn(eventChannel).once();
 
-		EasyMock.replay(datagridService);
-		EasyMock.replay(networkGraphService);
-		EasyMock.replay(modContext);
-		EasyMock.replay(controllerRegistryService);
+		expect(datagridService.addListener(
+				eq("onos.pathintent_state"),
+				anyObject(IEventChannelListener.class),
+				eq(Long.class),
+				eq(IntentStateList.class)))
+		.andReturn(eventChannel).once();
+
+		replay(datagridService);
+		replay(networkGraphService);
+		replay(modContext);
+		replay(controllerRegistryService);
 		PowerMock.replay(persistIntent, PersistIntent.class);
 	}
 
 	@After
 	public void tearDown() {
-		EasyMock.verify(datagridService);
-		EasyMock.verify(networkGraphService);
-		EasyMock.verify(modContext);
-		EasyMock.verify(controllerRegistryService);
+		verify(datagridService);
+		verify(networkGraphService);
+		verify(modContext);
+		verify(controllerRegistryService);
 		PowerMock.verify(persistIntent, PersistIntent.class);
 	}
 
@@ -221,7 +228,7 @@
 		System.out.println(plan);
 
 		// TODO this state changes should be triggered by notification of plan module
-		HashMap<String, IntentState> states = new HashMap<>();
+		IntentStateList states = new IntentStateList();
 		states.put("1", IntentState.INST_ACK);
 		states.put("2", IntentState.INST_ACK);
 		states.put("3", IntentState.INST_ACK);