Better handling of extensions in PiPipeconf

Now built using a URL, while input streams are generated on-demand.
Before it could happen that the input stream was completelly read by
someone, leaving it unusable by others.

Change-Id: I61a76bf8b8c1d2f6e2d987661025e0323d59e1c7
diff --git a/core/api/src/main/java/org/onosproject/net/pi/model/DefaultPiPipeconf.java b/core/api/src/main/java/org/onosproject/net/pi/model/DefaultPiPipeconf.java
index e457052..e6f9406 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/model/DefaultPiPipeconf.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/model/DefaultPiPipeconf.java
@@ -19,12 +19,16 @@
 import com.google.common.collect.ImmutableMap;
 import org.onosproject.net.driver.Behaviour;
 
+import java.io.IOException;
 import java.io.InputStream;
+import java.net.URL;
 import java.util.Collection;
 import java.util.Map;
 import java.util.Optional;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static java.lang.String.format;
 
 /**
  * Default pipeconf implementation.
@@ -34,11 +38,11 @@
     private final PiPipeconfId id;
     private final PiPipelineModel pipelineModel;
     private final Map<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviours;
-    private final Map<ExtensionType, InputStream> extensions;
+    private final Map<ExtensionType, URL> extensions;
 
     private DefaultPiPipeconf(PiPipeconfId id, PiPipelineModel pipelineModel,
                               Map<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviours,
-                              Map<ExtensionType, InputStream> extensions) {
+                              Map<ExtensionType, URL> extensions) {
         this.id = id;
         this.pipelineModel = pipelineModel;
         this.behaviours = behaviours;
@@ -72,7 +76,15 @@
 
     @Override
     public Optional<InputStream> extension(ExtensionType type) {
-        return Optional.ofNullable(extensions.get(type));
+        if (extensions.containsKey(type)) {
+            try {
+                return Optional.of(extensions.get(type).openStream());
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        } else {
+            return Optional.empty();
+        }
     }
 
     /**
@@ -93,7 +105,7 @@
         private PiPipelineModel pipelineModel;
         private ImmutableMap.Builder<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviourMapBuilder
                 = ImmutableMap.builder();
-        private ImmutableMap.Builder<ExtensionType, InputStream> extensionMapBuilder = ImmutableMap.builder();
+        private ImmutableMap.Builder<ExtensionType, URL> extensionMapBuilder = ImmutableMap.builder();
 
         /**
          * Sets the identifier of this pipeconf.
@@ -134,17 +146,27 @@
         /**
          * Adds an extension to this pipeconf.
          *
-         * @param type        extension type
-         * @param inputStream input stream pointing at the extension
+         * @param type extension type
+         * @param url  url pointing at the extension file
          * @return this
          */
-        public Builder addExtension(ExtensionType type, InputStream inputStream) {
+        public Builder addExtension(ExtensionType type, URL url) {
             checkNotNull(type);
-            checkNotNull(inputStream);
-            extensionMapBuilder.put(type, inputStream);
+            checkNotNull(url);
+            checkArgument(checkUrl(url), format("Extension url %s seems to be empty/non existent", url.toString()));
+            extensionMapBuilder.put(type, url);
             return this;
         }
 
+        private boolean checkUrl(URL url) {
+            try {
+                int byteCount = url.openStream().available();
+                return byteCount > 0;
+            } catch (IOException e) {
+                return false;
+            }
+        }
+
         /**
          * Creates a new pipeconf.
          *
diff --git a/core/api/src/main/java/org/onosproject/net/pi/model/PiPipeconf.java b/core/api/src/main/java/org/onosproject/net/pi/model/PiPipeconf.java
index f94ae6e..389da5c 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/model/PiPipeconf.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/model/PiPipeconf.java
@@ -68,9 +68,9 @@
     boolean hasBehaviour(Class<? extends Behaviour> behaviourClass);
 
     /**
-     * Returns, if present, an input stream of ad device-specific or control
-     * protocol-specific extension of this configuration. For example, if requesting a
-     * target-specific P4 binary, this will return the same bytes produced by the P4 compiler.
+     * Returns, if present, an input stream pointing at the beginning of a file representing a device-specific or
+     * control protocol-specific extension of this configuration. For example, if requesting a target-specific P4
+     * binary, this will return the same bytes produced by the P4 compiler.
      *
      * @param type extension type
      * @return extension input stream