ONOS-239 Retrigger compilation when new resources are available

When an intent is withdrawn and frees up resources, trigger
recompilation of any failed intents to allow the ones waiting
for resources to be installed.

Change-Id: Ic15678378ce41516a7eab890b4b4898aeb901f78
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java
index 7773ecf..fb6aa40 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java
@@ -15,10 +15,18 @@
  */
 package org.onlab.onos.net.intent.impl;
 
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -50,22 +58,20 @@
 import org.onlab.onos.net.intent.IntentStoreDelegate;
 import org.slf4j.Logger;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static java.util.concurrent.Executors.newSingleThreadExecutor;
-import static org.onlab.onos.net.intent.IntentState.*;
+import static org.onlab.onos.net.intent.IntentState.COMPILING;
+import static org.onlab.onos.net.intent.IntentState.FAILED;
+import static org.onlab.onos.net.intent.IntentState.INSTALLED;
+import static org.onlab.onos.net.intent.IntentState.INSTALLING;
+import static org.onlab.onos.net.intent.IntentState.WITHDRAWING;
+import static org.onlab.onos.net.intent.IntentState.WITHDRAWN;
 import static org.onlab.util.Tools.namedThreads;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -159,8 +165,8 @@
         checkNotNull(oldIntentId, INTENT_ID_NULL);
         checkNotNull(newIntent, INTENT_NULL);
         execute(IntentOperations.builder()
-                        .addReplaceOperation(oldIntentId, newIntent)
-                        .build());
+                .addReplaceOperation(oldIntentId, newIntent)
+                .build());
     }
 
     @Override
@@ -681,7 +687,7 @@
             // TODO: clean this up, or set to debug
             IntentState oldState = stateMap.get(intent);
             log.debug("intent id: {}, old state: {}, new state: {}",
-                     intent.id(), oldState, newState);
+                    intent.id(), oldState, newState);
 
             stateMap.put(intent, newState);
             IntentEvent event = store.setState(intent, newState);
@@ -845,4 +851,5 @@
             log.warn("NOT IMPLEMENTED -- Cancel operations: {}", operations);
         }
     }
+
 }
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTracker.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTracker.java
index c984632..7eeec65 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTracker.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTracker.java
@@ -15,8 +15,11 @@
  */
 package org.onlab.onos.net.intent.impl;
 
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.SetMultimap;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -29,15 +32,16 @@
 import org.onlab.onos.net.NetworkResource;
 import org.onlab.onos.net.intent.IntentId;
 import org.onlab.onos.net.link.LinkEvent;
+import org.onlab.onos.net.resource.LinkResourceEvent;
+import org.onlab.onos.net.resource.LinkResourceListener;
+import org.onlab.onos.net.resource.LinkResourceService;
 import org.onlab.onos.net.topology.TopologyEvent;
 import org.onlab.onos.net.topology.TopologyListener;
 import org.onlab.onos.net.topology.TopologyService;
 import org.slf4j.Logger;
 
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.ExecutorService;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.SetMultimap;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -53,7 +57,7 @@
  * Entity responsible for tracking installed flows and for monitoring topology
  * events to determine what flows are affected by topology changes.
  */
-@Component
+@Component(immediate = true)
 @Service
 public class ObjectiveTracker implements ObjectiveTrackerService {
 
@@ -65,21 +69,28 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected TopologyService topologyService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected LinkResourceService resourceManager;
+
     private ExecutorService executorService =
             newSingleThreadExecutor(namedThreads("onos-flowtracker"));
 
     private TopologyListener listener = new InternalTopologyListener();
+    private LinkResourceListener linkResourceListener =
+            new InternalLinkResourceListener();
     private TopologyChangeDelegate delegate;
 
     @Activate
     public void activate() {
         topologyService.addListener(listener);
+        resourceManager.addListener(linkResourceListener);
         log.info("Started");
     }
 
     @Deactivate
     public void deactivate() {
         topologyService.removeListener(listener);
+        resourceManager.removeListener(linkResourceListener);
         log.info("Stopped");
     }
 
@@ -173,4 +184,37 @@
         }
     }
 
+    /**
+     * Internal re-actor to resource available events.
+     */
+    private class InternalLinkResourceListener implements LinkResourceListener {
+        @Override
+        public void event(LinkResourceEvent event) {
+            executorService.execute(new ResourceAvailableHandler(event));
+        }
+    }
+
+    /*
+     * Re-dispatcher of resource available events.
+     */
+    private class ResourceAvailableHandler implements Runnable {
+
+        private final LinkResourceEvent event;
+
+        ResourceAvailableHandler(LinkResourceEvent event) {
+            this.event = event;
+        }
+
+        @Override
+        public void run() {
+            // If there is no delegate, why bother? Just bail.
+            if (delegate == null) {
+                return;
+            }
+
+            delegate.triggerCompile(new HashSet<>(), true);
+        }
+    }
+
+
 }
diff --git a/core/net/src/main/java/org/onlab/onos/net/resource/impl/LinkResourceManager.java b/core/net/src/main/java/org/onlab/onos/net/resource/impl/LinkResourceManager.java
index e7f22be..9f5d763 100644
--- a/core/net/src/main/java/org/onlab/onos/net/resource/impl/LinkResourceManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/resource/impl/LinkResourceManager.java
@@ -32,6 +32,8 @@
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.event.AbstractListenerRegistry;
+import org.onlab.onos.event.EventDeliveryService;
 import org.onlab.onos.net.Link;
 import org.onlab.onos.net.intent.IntentId;
 import org.onlab.onos.net.resource.BandwidthResourceAllocation;
@@ -41,9 +43,12 @@
 import org.onlab.onos.net.resource.LambdaResourceAllocation;
 import org.onlab.onos.net.resource.LambdaResourceRequest;
 import org.onlab.onos.net.resource.LinkResourceAllocations;
+import org.onlab.onos.net.resource.LinkResourceEvent;
+import org.onlab.onos.net.resource.LinkResourceListener;
 import org.onlab.onos.net.resource.LinkResourceRequest;
 import org.onlab.onos.net.resource.LinkResourceService;
 import org.onlab.onos.net.resource.LinkResourceStore;
+import org.onlab.onos.net.resource.LinkResourceStoreDelegate;
 import org.onlab.onos.net.resource.ResourceAllocation;
 import org.onlab.onos.net.resource.ResourceRequest;
 import org.onlab.onos.net.resource.ResourceType;
@@ -58,11 +63,18 @@
 
     private final Logger log = getLogger(getClass());
 
+    protected final AbstractListenerRegistry<LinkResourceEvent, LinkResourceListener>
+            listenerRegistry = new AbstractListenerRegistry<>();
+
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     private LinkResourceStore store;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected EventDeliveryService eventDispatcher;
+
     @Activate
     public void activate() {
+        eventDispatcher.addSink(LinkResourceEvent.class, listenerRegistry);
         log.info("Started");
     }
 
@@ -150,7 +162,10 @@
 
     @Override
     public void releaseResources(LinkResourceAllocations allocations) {
-        store.releaseResources(allocations);
+        final LinkResourceEvent event = store.releaseResources(allocations);
+        if (event != null) {
+            post(event);
+        }
     }
 
     @Override
@@ -205,4 +220,32 @@
         return result;
     }
 
+    @Override
+    public void addListener(LinkResourceListener listener) {
+        listenerRegistry.addListener(listener);
+    }
+
+    @Override
+    public void removeListener(LinkResourceListener listener) {
+        listenerRegistry.removeListener(listener);
+    }
+
+    /**
+     * Posts the specified event to the local event dispatcher.
+     */
+    private void post(LinkResourceEvent event) {
+        if (event != null) {
+            eventDispatcher.post(event);
+        }
+    }
+
+    /**
+     * Store delegate to re-post events emitted from the store.
+     */
+    private class InternalStoreDelegate implements LinkResourceStoreDelegate {
+        @Override
+        public void notify(LinkResourceEvent event) {
+            post(event);
+        }
+    }
 }