ONOS-6555 Default pipeconf implementation and builder

Change-Id: I80ac4f6e939d30a943653a1d63d5cff07b368620
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
new file mode 100644
index 0000000..e457052
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/model/DefaultPiPipeconf.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.model;
+
+import com.google.common.collect.ImmutableMap;
+import org.onosproject.net.driver.Behaviour;
+
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Default pipeconf implementation.
+ */
+public final class DefaultPiPipeconf implements PiPipeconf {
+
+    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 DefaultPiPipeconf(PiPipeconfId id, PiPipelineModel pipelineModel,
+                              Map<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviours,
+                              Map<ExtensionType, InputStream> extensions) {
+        this.id = id;
+        this.pipelineModel = pipelineModel;
+        this.behaviours = behaviours;
+        this.extensions = extensions;
+    }
+
+    @Override
+    public PiPipeconfId id() {
+        return id;
+    }
+
+    @Override
+    public PiPipelineModel pipelineModel() {
+        return pipelineModel;
+    }
+
+    @Override
+    public Collection<Class<? extends Behaviour>> behaviours() {
+        return behaviours.keySet();
+    }
+
+    @Override
+    public Optional<Class<? extends Behaviour>> implementation(Class<? extends Behaviour> behaviour) {
+        return Optional.ofNullable(behaviours.get(behaviour));
+    }
+
+    @Override
+    public boolean hasBehaviour(Class<? extends Behaviour> behaviourClass) {
+        return behaviours.containsKey(behaviourClass);
+    }
+
+    @Override
+    public Optional<InputStream> extension(ExtensionType type) {
+        return Optional.ofNullable(extensions.get(type));
+    }
+
+    /**
+     * Returns a new pipeconf builder.
+     *
+     * @return pipeconf builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder of pipeconf implementations.
+     */
+    public static class Builder {
+
+        private PiPipeconfId id;
+        private PiPipelineModel pipelineModel;
+        private ImmutableMap.Builder<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviourMapBuilder
+                = ImmutableMap.builder();
+        private ImmutableMap.Builder<ExtensionType, InputStream> extensionMapBuilder = ImmutableMap.builder();
+
+        /**
+         * Sets the identifier of this pipeconf.
+         *
+         * @param id pipeconf identifier
+         * @return this
+         */
+        public Builder withId(PiPipeconfId id) {
+            this.id = id;
+            return this;
+        }
+
+        /**
+         * Sets the pipeline model of this pipeconf.
+         *
+         * @param model pipeline model
+         * @return this
+         */
+        public Builder withPipelineModel(PiPipelineModel model) {
+            this.pipelineModel = model;
+            return this;
+        }
+
+        /**
+         * Adds a behaviour to this pipeconf.
+         *
+         * @param clazz          behavior interface class
+         * @param implementation behavior implementation class
+         * @return this
+         */
+        public Builder addBehaviour(Class<? extends Behaviour> clazz, Class<? extends Behaviour> implementation) {
+            checkNotNull(clazz);
+            checkNotNull(implementation);
+            behaviourMapBuilder.put(clazz, implementation);
+            return this;
+        }
+
+        /**
+         * Adds an extension to this pipeconf.
+         *
+         * @param type        extension type
+         * @param inputStream input stream pointing at the extension
+         * @return this
+         */
+        public Builder addExtension(ExtensionType type, InputStream inputStream) {
+            checkNotNull(type);
+            checkNotNull(inputStream);
+            extensionMapBuilder.put(type, inputStream);
+            return this;
+        }
+
+        /**
+         * Creates a new pipeconf.
+         *
+         * @return pipeconf instance
+         */
+        public PiPipeconf build() {
+            checkNotNull(id);
+            checkNotNull(pipelineModel);
+            return new DefaultPiPipeconf(id, pipelineModel, behaviourMapBuilder.build(), extensionMapBuilder.build());
+        }
+
+    }
+}
diff --git a/core/net/src/test/java/org/onosproject/net/pi/impl/MockPipeconf.java b/core/net/src/test/java/org/onosproject/net/pi/impl/MockPipeconf.java
deleted file mode 100644
index 4e98567..0000000
--- a/core/net/src/test/java/org/onosproject/net/pi/impl/MockPipeconf.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.onosproject.net.pi.impl;
-
-import com.eclipsesource.json.Json;
-import com.google.common.collect.Maps;
-import org.onosproject.bmv2.model.Bmv2PipelineModelParser;
-import org.onosproject.net.driver.Behaviour;
-import org.onosproject.net.pi.model.PiPipeconf;
-import org.onosproject.net.pi.model.PiPipeconfId;
-import org.onosproject.net.pi.model.PiPipelineInterpreter;
-import org.onosproject.net.pi.model.PiPipelineModel;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.Collection;
-import java.util.Map;
-import java.util.Optional;
-
-/**
- * Mock pipeconf implementation.
- */
-public class MockPipeconf implements PiPipeconf {
-
-    private static final String PIPECONF_ID = "org.project.pipeconf.default";
-    private static final String JSON_PATH = "/org/onosproject/net/pi/impl/default.json";
-
-    private final PiPipeconfId id;
-    private final PiPipelineModel pipelineModel;
-    protected final Map<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviours;
-
-    public MockPipeconf() throws IOException {
-        this.id = new PiPipeconfId(PIPECONF_ID);
-        this.pipelineModel = loadDefaultModel();
-        this.behaviours = Maps.newHashMap();
-
-        behaviours.put(PiPipelineInterpreter.class, MockInterpreter.class);
-    }
-
-    static PiPipelineModel loadDefaultModel() throws IOException {
-        return Bmv2PipelineModelParser.parse(Json.parse(new BufferedReader(new InputStreamReader(
-                MockPipeconf.class.getResourceAsStream(JSON_PATH)))).asObject());
-    }
-
-    @Override
-    public PiPipeconfId id() {
-        return this.id;
-    }
-
-    @Override
-    public PiPipelineModel pipelineModel() {
-        return pipelineModel;
-    }
-
-    @Override
-    public Collection<Class<? extends Behaviour>> behaviours() {
-        return behaviours.keySet();
-    }
-
-    @Override
-    public Optional<Class<? extends Behaviour>> implementation(Class<? extends Behaviour> behaviour) {
-        return Optional.ofNullable(behaviours.get(behaviour));
-    }
-
-    @Override
-    public boolean hasBehaviour(Class<? extends Behaviour> behaviourClass) {
-        return behaviours.containsKey(behaviourClass);
-    }
-
-    @Override
-    public Optional<InputStream> extension(ExtensionType type) {
-        return Optional.empty();
-    }
-}
diff --git a/core/net/src/test/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorTest.java b/core/net/src/test/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorTest.java
index 547e957..f060125 100644
--- a/core/net/src/test/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorTest.java
+++ b/core/net/src/test/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorTest.java
@@ -20,6 +20,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.onlab.packet.MacAddress;
+import org.onosproject.bmv2.model.Bmv2PipelineModelParser;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.DefaultApplicationId;
 import org.onosproject.net.DeviceId;
@@ -30,7 +31,10 @@
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.pi.model.DefaultPiPipeconf;
 import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.model.PiPipeconfId;
+import org.onosproject.net.pi.model.PiPipelineInterpreter;
 import org.onosproject.net.pi.runtime.PiTableEntry;
 import org.onosproject.net.pi.runtime.PiTernaryFieldMatch;
 
@@ -48,12 +52,18 @@
 @SuppressWarnings("ConstantConditions")
 public class PiFlowRuleTranslatorTest {
 
+    private static final String BMV2_JSON_PATH = "/org/onosproject/net/pi/impl/default.json";
+
     private Random random = new Random();
     private PiPipeconf pipeconf;
 
     @Before
     public void setUp() throws Exception {
-        pipeconf = new MockPipeconf();
+        pipeconf = DefaultPiPipeconf.builder()
+                .withId(new PiPipeconfId("mock-pipeconf"))
+                .withPipelineModel(Bmv2PipelineModelParser.parse(this.getClass().getResourceAsStream(BMV2_JSON_PATH)))
+                .addBehaviour(PiPipelineInterpreter.class, MockInterpreter.class)
+                .build();
     }
 
     @Test
diff --git a/core/net/src/test/java/org/onosproject/net/pi/impl/PiPipeconfManagerTest.java b/core/net/src/test/java/org/onosproject/net/pi/impl/PiPipeconfManagerTest.java
index a3f5cfb..a0a6c04 100644
--- a/core/net/src/test/java/org/onosproject/net/pi/impl/PiPipeconfManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/pi/impl/PiPipeconfManagerTest.java
@@ -23,6 +23,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.onlab.util.ItemNotFoundException;
+import org.onosproject.bmv2.model.Bmv2PipelineModelParser;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.behaviour.Pipeliner;
 import org.onosproject.net.behaviour.PipelinerAdapter;
@@ -45,6 +46,8 @@
 import org.onosproject.net.driver.DriverProvider;
 import org.onosproject.net.driver.DriverService;
 import org.onosproject.net.driver.DriverServiceAdapter;
+import org.onosproject.net.pi.model.DefaultPiPipeconf;
+import org.onosproject.net.pi.model.PiPipeconf;
 import org.onosproject.net.pi.model.PiPipeconfId;
 import org.onosproject.net.pi.model.PiPipelineInterpreter;
 import org.onosproject.net.pi.runtime.PiPipeconfConfig;
@@ -57,9 +60,7 @@
 import java.util.Map;
 import java.util.Set;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.*;
 
 
 /**
@@ -67,6 +68,11 @@
  */
 public class PiPipeconfManagerTest {
 
+    private static final String PIPECONF_ID = "org.project.pipeconf.default";
+    private static final String BMV2_JSON_PATH = "/org/onosproject/net/pi/impl/default.json";
+
+    private final InputStream bmv2JsonConfigStream = this.getClass().getResourceAsStream(BMV2_JSON_PATH);
+
     private static final DeviceId DEVICE_ID = DeviceId.deviceId("test:test");
     private static final String BASE_DRIVER = "baseDriver";
     private static final Set<Class<? extends Behaviour>> EXPECTED_BEHAVIOURS =
@@ -93,14 +99,17 @@
 
     //Services
     private PiPipeconfManager piPipeconfService;
-    private MockPipeconf piPipeconf;
+    private PiPipeconf piPipeconf;
 
     @Before
     public void setUp() throws IOException {
         piPipeconfService = new PiPipeconfManager();
-        piPipeconf = new MockPipeconf();
+        piPipeconf = DefaultPiPipeconf.builder()
+                .withId(new PiPipeconfId(PIPECONF_ID))
+                .withPipelineModel(Bmv2PipelineModelParser.parse(bmv2JsonConfigStream))
+                .addBehaviour(Pipeliner.class, PipelinerAdapter.class)
+                .build();
         completeDriverName = BASE_DRIVER + ":" + piPipeconf.id();
-        piPipeconf.behaviours.put(Pipeliner.class, PipelinerAdapter.class);
         piPipeconfService.cfgService = cfgService;
         piPipeconfService.driverService = driverService;
         piPipeconfService.driverAdminService = driverAdminService;
@@ -132,7 +141,7 @@
         assertEquals("Incorrect configuration service", null, piPipeconfService.cfgService);
         assertFalse("Config factory should be unregistered", cfgFactories.contains(piPipeconfService.factory));
         assertFalse("Network configuration listener should be unregistered",
-                netCfgListeners.contains(piPipeconfService.cfgListener));
+                    netCfgListeners.contains(piPipeconfService.cfgListener));
     }
 
     @Test
@@ -145,7 +154,7 @@
     public void getPipeconf() {
         piPipeconfService.register(piPipeconf);
         assertEquals("Returned PiPipeconf is not correct", piPipeconf,
-                piPipeconfService.getPipeconf(piPipeconf.id()).get());
+                     piPipeconfService.getPipeconf(piPipeconf.id()).get());
     }
 
 
@@ -159,7 +168,7 @@
 
         piPipeconfService.register(piPipeconf);
         assertEquals("Returned PiPipeconf is not correct", piPipeconf,
-                piPipeconfService.getPipeconf(piPipeconf.id()).get());
+                     piPipeconfService.getPipeconf(piPipeconf.id()).get());
 
         piPipeconfService.bindToDevice(piPipeconfId, DEVICE_ID).whenComplete((booleanResult, ex) -> {
 
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DefaultPipeconf.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DefaultPipeconf.java
deleted file mode 100644
index ac2dbd7..0000000
--- a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DefaultPipeconf.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.onosproject.drivers.bmv2;
-
-import com.eclipsesource.json.Json;
-import com.google.common.collect.ImmutableMap;
-import org.onosproject.bmv2.model.Bmv2PipelineModelParser;
-import org.onosproject.net.driver.Behaviour;
-import org.onosproject.net.pi.model.PiPipeconf;
-import org.onosproject.net.pi.model.PiPipeconfId;
-import org.onosproject.net.pi.model.PiPipelineInterpreter;
-import org.onosproject.net.pi.model.PiPipelineModel;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.Collection;
-import java.util.Map;
-import java.util.Optional;
-
-/**
- * Implementation of the default pipeconf for a BMv2 device.
- */
-public final class Bmv2DefaultPipeconf implements PiPipeconf {
-
-    private static final String PIPECONF_ID = "bmv2-default-pipeconf";
-    private static final String JSON_PATH = "/default.json";
-    private static final String P4INFO_PATH = "/default.p4info";
-
-    private final PiPipeconfId id;
-    private final PiPipelineModel pipelineModel;
-    private final InputStream jsonConfigStream = this.getClass().getResourceAsStream(JSON_PATH);
-    private final InputStream p4InfoStream = this.getClass().getResourceAsStream(P4INFO_PATH);
-    private final Map<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviours;
-
-    Bmv2DefaultPipeconf() {
-        this.id = new PiPipeconfId(PIPECONF_ID);
-        try {
-            this.pipelineModel = Bmv2PipelineModelParser.parse(
-                    Json.parse(new BufferedReader(new InputStreamReader(jsonConfigStream))).asObject());
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-
-        this.behaviours = ImmutableMap.of(
-                PiPipelineInterpreter.class, Bmv2DefaultInterpreter.class
-                // TODO: reuse default single table pipeliner.
-        );
-    }
-
-    @Override
-    public PiPipeconfId id() {
-        return this.id;
-    }
-
-    @Override
-    public PiPipelineModel pipelineModel() {
-        return pipelineModel;
-    }
-
-    @Override
-    public Collection<Class<? extends Behaviour>> behaviours() {
-        return behaviours.keySet();
-    }
-
-    @Override
-    public Optional<Class<? extends Behaviour>> implementation(Class<? extends Behaviour> behaviour) {
-        return Optional.ofNullable(behaviours.get(behaviour));
-    }
-
-    @Override
-    public boolean hasBehaviour(Class<? extends Behaviour> behaviourClass) {
-        return behaviours.containsKey(behaviourClass);
-    }
-
-    @Override
-    public Optional<InputStream> extension(ExtensionType type) {
-
-        switch (type) {
-            case BMV2_JSON:
-                return Optional.of(jsonConfigStream);
-            case P4_INFO_TEXT:
-                return Optional.of(p4InfoStream);
-            default:
-                return Optional.empty();
-        }
-    }
-}
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DefaultPipeconfFactory.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DefaultPipeconfFactory.java
new file mode 100644
index 0000000..4903c3e
--- /dev/null
+++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DefaultPipeconfFactory.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.bmv2;
+
+import org.onosproject.bmv2.model.Bmv2PipelineModelParser;
+import org.onosproject.net.pi.model.DefaultPiPipeconf;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.model.PiPipeconfId;
+import org.onosproject.net.pi.model.PiPipelineInterpreter;
+
+import java.io.InputStream;
+
+import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.BMV2_JSON;
+import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.P4_INFO_TEXT;
+
+/**
+ * Factory of pipeconf implementation for the default.p4 program on BMv2.
+ */
+final class Bmv2DefaultPipeconfFactory {
+
+    private static final String PIPECONF_ID = "bmv2-default-pipeconf";
+    private static final String JSON_PATH = "/default.json";
+    private static final String P4INFO_PATH = "/default.p4info";
+
+    private Bmv2DefaultPipeconfFactory() {
+        // Hides constructor.
+    }
+
+    static PiPipeconf get() {
+
+        final InputStream jsonConfigStream = Bmv2DefaultPipeconfFactory.class.getResourceAsStream(JSON_PATH);
+        final InputStream p4InfoStream = Bmv2DefaultPipeconfFactory.class.getResourceAsStream(P4INFO_PATH);
+
+        return DefaultPiPipeconf.builder()
+                .withId(new PiPipeconfId(PIPECONF_ID))
+                .withPipelineModel(Bmv2PipelineModelParser.parse(jsonConfigStream))
+                // TODO: reuse default single table pipeliner.
+                .addBehaviour(PiPipelineInterpreter.class, Bmv2DefaultInterpreter.class)
+                .addExtension(P4_INFO_TEXT, p4InfoStream)
+                .addExtension(BMV2_JSON, jsonConfigStream)
+                .build();
+    }
+}
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PipelineProgrammable.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PipelineProgrammable.java
index 735c142..88fc0ab 100644
--- a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PipelineProgrammable.java
+++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PipelineProgrammable.java
@@ -39,7 +39,7 @@
  */
 public class Bmv2PipelineProgrammable extends AbstractHandlerBehaviour implements PiPipelineProgrammable {
 
-    private static final PiPipeconf DEFAULT_PIPECONF = new Bmv2DefaultPipeconf();
+    private static final PiPipeconf DEFAULT_PIPECONF = Bmv2DefaultPipeconfFactory.get();
 
     private final Logger log = getLogger(getClass());
 
diff --git a/incubator/bmv2/model/src/main/java/org/onosproject/bmv2/model/Bmv2PipelineModelParser.java b/incubator/bmv2/model/src/main/java/org/onosproject/bmv2/model/Bmv2PipelineModelParser.java
index c674753..b7a1f15c 100644
--- a/incubator/bmv2/model/src/main/java/org/onosproject/bmv2/model/Bmv2PipelineModelParser.java
+++ b/incubator/bmv2/model/src/main/java/org/onosproject/bmv2/model/Bmv2PipelineModelParser.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.bmv2.model;
 
+import com.eclipsesource.json.Json;
 import com.eclipsesource.json.JsonArray;
 import com.eclipsesource.json.JsonObject;
 import com.eclipsesource.json.JsonValue;
@@ -25,6 +26,10 @@
 import org.onosproject.net.pi.model.PiMatchType;
 import org.slf4j.Logger;
 
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -34,8 +39,8 @@
 /**
  * BMv2 pipeline model parser.
  *
- * @see <a href="https://github.com/p4lang/behavioral-model/blob/master/docs/JSON_format.md">
- * BMv2 JSON specification</a>
+ * @see <a href="https://github.com/p4lang/behavioral-model/blob/master/docs/JSON_format.md"> BMv2 JSON
+ * specification</a>
  */
 @Beta
 public final class Bmv2PipelineModelParser {
@@ -51,10 +56,10 @@
     }
 
     /**
-     * Translate BMv2 json config to Bmv2PipelineModel object.
+     * Parse the given JSON object representing a BMv2 configuration, to a Bmv2PipelineModel object.
      *
      * @param jsonObject the BMv2 json config
-     * @return Bmv2PipelineModel object for the json config
+     * @return Bmv2PipelineModel BMv2 pipeline model object
      */
     public static Bmv2PipelineModel parse(JsonObject jsonObject) {
         List<Bmv2HeaderTypeModel> headerTypeModels = HeaderTypesParser.parse(jsonObject);
@@ -68,6 +73,21 @@
     }
 
     /**
+     * Parse the input stream pointing to a BMv2 JSON configuration, to a Bmv2PipelineModel object.
+     *
+     * @param jsonInputStream input stream pointing to a BMv2 JSON configuration
+     * @return Bmv2PipelineModel BMv2 pipeline model object
+     */
+    public static Bmv2PipelineModel parse(InputStream jsonInputStream) {
+        try {
+            return parse(Json.parse(new BufferedReader(
+                    new InputStreamReader(jsonInputStream))).asObject());
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
      * Parser for BMv2 header types.
      */
     private static class HeaderTypesParser {
@@ -80,7 +100,7 @@
 
         private static List<Bmv2HeaderTypeModel> parse(JsonObject jsonObject) {
             List<Bmv2HeaderTypeModel> headerTypeModels = Lists.newArrayList();
-            jsonObject.get(HEADER_TYPES).asArray().forEach(jsonValue ->  {
+            jsonObject.get(HEADER_TYPES).asArray().forEach(jsonValue -> {
                 JsonObject headerFieldType = jsonValue.asObject();
                 String name = headerFieldType.getString(NAME, null);
                 int id = headerFieldType.getInt(ID, NO_ID);