Major refactoring of the BMv2 protocol module (onos1.6 cherry-pick)

- Created 3 separate sub-modules: API (doesn't depend on
    Thrift), CTL (depends on Thrift), THRIFT-API (to generate Thrift
    sources)
- Implemented 2 new services (for device configuration swapping and
    table entry management) needed to distribute BMv2-specific state
    among ONOS instances.
- Implemented a BMv2 controller (previously other modules where
    using separately a Thrift client and a server)
- Added a default BMv2 JSON configuration (default.json) and interpreter
    to be used for devices that connect for the first time to ONOS.
    This allows for basic services to work (i.e. LLDP link discovery,
    ARP proxy. etc.).
- Changed behavior of the flow rule translator and extension selector,
    now it allows extension to specify only some of the match parameters
    (before extension selectors were expected to describe the whole
    match key, i.e. all fields)
- Various renaming to better represent the API
- Various java doc fixes / improvements

Change-Id: Ida4b5e546b0def97c3552a6c05f7bce76fd32c28
diff --git a/protocols/bmv2/api/pom.xml b/protocols/bmv2/api/pom.xml
new file mode 100644
index 0000000..13ad693
--- /dev/null
+++ b/protocols/bmv2/api/pom.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2016-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.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>onos-bmv2-protocol</artifactId>
+        <groupId>org.onosproject</groupId>
+        <version>1.6.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <packaging>bundle</packaging>
+
+    <artifactId>onos-bmv2-protocol-api</artifactId>
+
+   <dependencies>
+       <dependency>
+           <groupId>org.onosproject</groupId>
+           <artifactId>onos-api</artifactId>
+           <version>${project.version}</version>
+       </dependency>
+       <dependency>
+           <groupId>org.apache.felix</groupId>
+           <artifactId>org.apache.felix.scr.annotations</artifactId>
+       </dependency>
+   </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2ActionModel.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2ActionModel.java
new file mode 100644
index 0000000..ea70b65
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2ActionModel.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2016-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.bmv2.api.context;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * A BMv2 action model.
+ */
+@Beta
+public final class Bmv2ActionModel {
+
+    private final String name;
+    private final int id;
+    private final LinkedHashMap<String, Bmv2RuntimeDataModel> runtimeDatas = Maps.newLinkedHashMap();
+
+    /**
+     * Creates a new action model.
+     *
+     * @param name         name
+     * @param id           id
+     * @param runtimeDatas list of runtime data
+     */
+    protected Bmv2ActionModel(String name, int id, List<Bmv2RuntimeDataModel> runtimeDatas) {
+        this.name = name;
+        this.id = id;
+        runtimeDatas.forEach(r -> this.runtimeDatas.put(r.name(), r));
+    }
+
+    /**
+     * Returns the name of this action.
+     *
+     * @return a string value
+     */
+    public String name() {
+        return name;
+    }
+
+    /**
+     * Returns the id of this action.
+     *
+     * @return an integer value
+     */
+    public int id() {
+        return id;
+    }
+
+    /**
+     * Returns this action's runtime data defined by the given name, null
+     * if not present.
+     *
+     * @return runtime data or null
+     */
+    public Bmv2RuntimeDataModel runtimeData(String name) {
+        return runtimeDatas.get(name);
+    }
+
+    /**
+     * Returns an immutable list of runtime data for this action.
+     * The list is ordered according to the values defined in the configuration.
+     *
+     * @return list of runtime data.
+     */
+    public List<Bmv2RuntimeDataModel> runtimeDatas() {
+        return ImmutableList.copyOf(runtimeDatas.values());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(name, id, runtimeDatas);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bmv2ActionModel other = (Bmv2ActionModel) obj;
+        return Objects.equals(this.name, other.name)
+                && Objects.equals(this.id, other.id)
+                && Objects.equals(this.runtimeDatas, other.runtimeDatas);
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("name", name)
+                .add("id", id)
+                .add("runtimeDatas", runtimeDatas)
+                .toString();
+    }
+
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2Configuration.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2Configuration.java
new file mode 100644
index 0000000..1cbb146
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2Configuration.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2016-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.bmv2.api.context;
+
+import com.eclipsesource.json.JsonObject;
+import com.google.common.annotations.Beta;
+
+import java.util.List;
+
+/**
+ * BMv2 packet processing configuration. Such a configuration is used to define the way BMv2 should process packets
+ * (i.e. it defines the device ingress/egress pipelines, parser, tables, actions, etc.). It must be noted that this
+ * class exposes only a subset of the configuration properties of a BMv2 device (only those that are needed for the
+ * purpose of translating ONOS structures to BMv2 structures). Such a configuration is backed by a JSON object.
+ * BMv2 JSON configuration files are usually generated using a P4 frontend compiler such as p4c-bmv2.
+ */
+@Beta
+public interface Bmv2Configuration {
+
+    /**
+     * Return an unmodifiable view of the JSON backing this configuration.
+     *
+     * @return a JSON object.
+     */
+    JsonObject json();
+
+    /**
+     * Returns the header type associated with the given numeric ID, null if there's no such an ID in the configuration.
+     *
+     * @param id integer value
+     * @return header type object or null
+     */
+    Bmv2HeaderTypeModel headerType(int id);
+
+    /**
+     * Returns the header type associated with the given name, null if there's no such a name in the configuration.
+     *
+     * @param name string value
+     * @return header type object or null
+     */
+    Bmv2HeaderTypeModel headerType(String name);
+
+    /**
+     * Returns the list of all the header types defined by in this configuration. Values returned are sorted in
+     * ascending order based on the numeric ID.
+     *
+     * @return list of header types
+     */
+    List<Bmv2HeaderTypeModel> headerTypes();
+
+    /**
+     * Returns the header associated with the given numeric ID, null if there's no such an ID in the configuration.
+     *
+     * @param id integer value
+     * @return header object or null
+     */
+    Bmv2HeaderModel header(int id);
+
+    /**
+     * Returns the header associated with the given name, null if there's no such a name in the configuration.
+     *
+     * @param name string value
+     * @return header object or null
+     */
+    Bmv2HeaderModel header(String name);
+
+    /**
+     * Returns the list of all the header instances defined in this configuration. Values returned are sorted in
+     * ascending order based on the numeric ID.
+     *
+     * @return list of header types
+     */
+    List<Bmv2HeaderModel> headers();
+
+    /**
+     * Returns the action associated with the given numeric ID, null if there's no such an ID in the configuration.
+     *
+     * @param id integer value
+     * @return action object or null
+     */
+    Bmv2ActionModel action(int id);
+
+    /**
+     * Returns the action associated with the given name, null if there's no such a name in the configuration.
+     *
+     * @param name string value
+     * @return action object or null
+     */
+    Bmv2ActionModel action(String name);
+
+    /**
+     * Returns the list of all the actions defined by in this configuration. Values returned are sorted in ascending
+     * order based on the numeric ID.
+     *
+     * @return list of actions
+     */
+    List<Bmv2ActionModel> actions();
+
+    /**
+     * Returns the table associated with the given numeric ID, null if there's no such an ID in the configuration.
+     *
+     * @param id integer value
+     * @return table object or null
+     */
+    Bmv2TableModel table(int id);
+
+    /**
+     * Returns the table associated with the given name, null if there's no such a name in the configuration.
+     *
+     * @param name string value
+     * @return table object or null
+     */
+    Bmv2TableModel table(String name);
+
+    /**
+     * Returns the list of all the tables defined by in this configuration. Values returned are sorted in ascending
+     * order based on the numeric ID.
+     *
+     * @return list of actions
+     */
+    List<Bmv2TableModel> tables();
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2DefaultConfiguration.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2DefaultConfiguration.java
new file mode 100644
index 0000000..53a31b9
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2DefaultConfiguration.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright 2016-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.bmv2.api.context;
+
+import com.eclipsesource.json.JsonArray;
+import com.eclipsesource.json.JsonObject;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.onosproject.bmv2.api.runtime.Bmv2MatchParam;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Default implementation of a BMv2 configuration backed by a JSON object.
+ */
+public final class Bmv2DefaultConfiguration implements Bmv2Configuration {
+
+    private final JsonObject json;
+    private final DualKeySortedMap<Bmv2HeaderTypeModel> headerTypes = new DualKeySortedMap<>();
+    private final DualKeySortedMap<Bmv2HeaderModel> headers = new DualKeySortedMap<>();
+    private final DualKeySortedMap<Bmv2ActionModel> actions = new DualKeySortedMap<>();
+    private final DualKeySortedMap<Bmv2TableModel> tables = new DualKeySortedMap<>();
+
+    private Bmv2DefaultConfiguration(JsonObject json) {
+        this.json = JsonObject.unmodifiableObject(json);
+    }
+
+    /**
+     * Returns a new BMv2 configuration object by parsing the passed JSON.
+     *
+     * @param json json
+     * @return a new BMv2 configuration object
+     * @see <a href="https://github.com/p4lang/behavioral-configuration/blob/master/docs/JSON_format.md">
+     * BMv2 JSON specification</a>
+     */
+    public static Bmv2DefaultConfiguration parse(JsonObject json) {
+        checkArgument(json != null, "json cannot be null");
+        // TODO: implement caching, no need to parse a json if we already have the configuration
+        Bmv2DefaultConfiguration configuration = new Bmv2DefaultConfiguration(json);
+        configuration.doParse();
+        return configuration;
+    }
+
+    @Override
+    public Bmv2HeaderTypeModel headerType(int id) {
+        return headerTypes.get(id);
+    }
+
+    @Override
+    public Bmv2HeaderTypeModel headerType(String name) {
+        return headerTypes.get(name);
+    }
+
+    @Override
+    public List<Bmv2HeaderTypeModel> headerTypes() {
+        return ImmutableList.copyOf(headerTypes.sortedMap().values());
+    }
+
+    @Override
+    public Bmv2HeaderModel header(int id) {
+        return headers.get(id);
+    }
+
+    @Override
+    public Bmv2HeaderModel header(String name) {
+        return headers.get(name);
+    }
+
+    @Override
+    public List<Bmv2HeaderModel> headers() {
+        return ImmutableList.copyOf(headers.sortedMap().values());
+    }
+
+    @Override
+    public Bmv2ActionModel action(int id) {
+        return actions.get(id);
+    }
+
+    @Override
+    public Bmv2ActionModel action(String name) {
+        return actions.get(name);
+    }
+
+    @Override
+    public List<Bmv2ActionModel> actions() {
+        return ImmutableList.copyOf(actions.sortedMap().values());
+    }
+
+    @Override
+    public Bmv2TableModel table(int id) {
+        return tables.get(id);
+    }
+
+    @Override
+    public Bmv2TableModel table(String name) {
+        return tables.get(name);
+    }
+
+    @Override
+    public List<Bmv2TableModel> tables() {
+        return ImmutableList.copyOf(tables.sortedMap().values());
+    }
+
+    @Override
+    public JsonObject json() {
+        return this.json;
+    }
+
+    /**
+     * Generates a hash code for this BMv2 configuration. The hash function is based solely on the JSON backing this
+     * configuration.
+     */
+    @Override
+    public int hashCode() {
+        return json.hashCode();
+    }
+
+    /**
+     * Indicates whether some other BMv2 configuration is equal to this one.
+     * Equality is based solely on the low-level JSON representation.
+     *
+     * @param obj other object
+     * @return true if equals, false elsewhere
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bmv2DefaultConfiguration other = (Bmv2DefaultConfiguration) obj;
+        return Objects.equal(this.json, other.json);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("jsonHash", json.hashCode())
+                .toString();
+    }
+
+    /**
+     * Parse the JSON object and build the corresponding objects.
+     */
+    private void doParse() {
+        // parse header types
+        json.get("header_types").asArray().forEach(val -> {
+
+            JsonObject jHeaderType = val.asObject();
+
+            // populate fields list
+            List<Bmv2FieldTypeModel> fieldTypes = Lists.newArrayList();
+
+            jHeaderType.get("fields").asArray().forEach(x -> fieldTypes.add(
+                    new Bmv2FieldTypeModel(
+                            x.asArray().get(0).asString(),
+                            x.asArray().get(1).asInt())));
+
+            // add header type instance
+            String name = jHeaderType.get("name").asString();
+            int id = jHeaderType.get("id").asInt();
+
+            Bmv2HeaderTypeModel headerType = new Bmv2HeaderTypeModel(name,
+                                                                     id,
+                                                                     fieldTypes);
+
+            headerTypes.put(name, id, headerType);
+        });
+
+        // parse headers
+        json.get("headers").asArray().forEach(val -> {
+
+            JsonObject jHeader = val.asObject();
+
+            String name = jHeader.get("name").asString();
+            int id = jHeader.get("id").asInt();
+            String typeName = jHeader.get("header_type").asString();
+
+            Bmv2HeaderModel header = new Bmv2HeaderModel(name,
+                                                         id,
+                                                         headerTypes.get(typeName),
+                                                         jHeader.get("metadata").asBoolean());
+
+            // add instance
+            headers.put(name, id, header);
+        });
+
+        // parse actions
+        json.get("actions").asArray().forEach(val -> {
+
+            JsonObject jAction = val.asObject();
+
+            // populate runtime data list
+            List<Bmv2RuntimeDataModel> runtimeDatas = Lists.newArrayList();
+
+            jAction.get("runtime_data").asArray().forEach(jData -> runtimeDatas.add(
+                    new Bmv2RuntimeDataModel(
+                            jData.asObject().get("name").asString(),
+                            jData.asObject().get("bitwidth").asInt()
+                    )));
+
+            // add action instance
+            String name = jAction.get("name").asString();
+            int id = jAction.get("id").asInt();
+
+            Bmv2ActionModel action = new Bmv2ActionModel(name,
+                                                         id,
+                                                         runtimeDatas);
+
+            actions.put(name, id, action);
+        });
+
+        // parse tables
+        json.get("pipelines").asArray().forEach(pipeline -> {
+
+            pipeline.asObject().get("tables").asArray().forEach(val -> {
+
+                JsonObject jTable = val.asObject();
+
+                // populate keys
+                List<Bmv2TableKeyModel> keys = Lists.newArrayList();
+
+                jTable.get("key").asArray().forEach(jKey -> {
+                    JsonArray target = jKey.asObject().get("target").asArray();
+
+                    Bmv2HeaderModel header = header(target.get(0).asString());
+                    String typeName = target.get(1).asString();
+
+                    Bmv2FieldModel field = new Bmv2FieldModel(
+                            header, header.type().field(typeName));
+
+                    String matchTypeStr = jKey.asObject().get("match_type").asString();
+
+                    Bmv2MatchParam.Type matchType;
+
+                    switch (matchTypeStr) {
+                        case "ternary":
+                            matchType = Bmv2MatchParam.Type.TERNARY;
+                            break;
+                        case "exact":
+                            matchType = Bmv2MatchParam.Type.EXACT;
+                            break;
+                        case "lpm":
+                            matchType = Bmv2MatchParam.Type.LPM;
+                            break;
+                        case "valid":
+                            matchType = Bmv2MatchParam.Type.VALID;
+                            break;
+                        default:
+                            throw new RuntimeException(
+                                    "Unable to parse match type: " + matchTypeStr);
+                    }
+
+                    keys.add(new Bmv2TableKeyModel(matchType, field));
+                });
+
+                // populate actions set
+                Set<Bmv2ActionModel> actionzz = Sets.newHashSet();
+                jTable.get("actions").asArray().forEach(
+                        jAction -> actionzz.add(action(jAction.asString())));
+
+                // add table instance
+                String name = jTable.get("name").asString();
+                int id = jTable.get("id").asInt();
+
+                Bmv2TableModel table = new Bmv2TableModel(name,
+                                                          id,
+                                                          jTable.get("match_type").asString(),
+                                                          jTable.get("type").asString(),
+                                                          jTable.get("max_size").asInt(),
+                                                          jTable.get("with_counters").asBoolean(),
+                                                          jTable.get("support_timeout").asBoolean(),
+                                                          keys,
+                                                          actionzz);
+
+                tables.put(name, id, table);
+            });
+        });
+    }
+
+    /**
+     * Handy class for a map indexed by two keys, a string and an integer.
+     *
+     * @param <T> type of value stored by the map
+     */
+    private class DualKeySortedMap<T> {
+        private final SortedMap<Integer, T> intMap = Maps.newTreeMap();
+        private final Map<String, Integer> strToIntMap = Maps.newHashMap();
+
+        private void put(String name, int id, T object) {
+            strToIntMap.put(name, id);
+            intMap.put(id, object);
+        }
+
+        private T get(int id) {
+            return intMap.get(id);
+        }
+
+        private T get(String name) {
+            return strToIntMap.get(name) == null ? null : get(strToIntMap.get(name));
+        }
+
+        private SortedMap<Integer, T> sortedMap() {
+            return intMap;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(intMap, strToIntMap);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null || getClass() != obj.getClass()) {
+                return false;
+            }
+            final DualKeySortedMap other = (DualKeySortedMap) obj;
+            return Objects.equal(this.intMap, other.intMap)
+                    && Objects.equal(this.strToIntMap, other.strToIntMap);
+        }
+    }
+}
\ No newline at end of file
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2DeviceContext.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2DeviceContext.java
new file mode 100644
index 0000000..f54cc1d
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2DeviceContext.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2016-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.bmv2.api.context;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * A BMv2 device context, defined by a configuration and an interpreter.
+ */
+@Beta
+public final class Bmv2DeviceContext {
+
+    private final Bmv2Configuration configuration;
+    private final Bmv2Interpreter interpreter;
+
+    /**
+     * Creates a new BMv2 device context.
+     *
+     * @param configuration a configuration
+     * @param interpreter   an interpreter
+     */
+    public Bmv2DeviceContext(Bmv2Configuration configuration, Bmv2Interpreter interpreter) {
+        this.configuration = checkNotNull(configuration, "configuration cannot be null");
+        this.interpreter = checkNotNull(interpreter, "interpreter cannot be null");
+    }
+
+    /**
+     * Returns the BMv2 configuration of this context.
+     *
+     * @return a configuration
+     */
+    public Bmv2Configuration configuration() {
+        return configuration;
+    }
+
+    /**
+     * Returns the BMv2 interpreter of this context.
+     *
+     * @return an interpreter
+     */
+    public Bmv2Interpreter interpreter() {
+        return interpreter;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(configuration, interpreter);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bmv2DeviceContext other = (Bmv2DeviceContext) obj;
+        return Objects.equal(this.configuration, other.configuration)
+                && Objects.equal(this.interpreter, other.interpreter);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("configuration", configuration)
+                .add("interpreter", interpreter)
+                .toString();
+    }
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2FieldModel.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2FieldModel.java
new file mode 100644
index 0000000..b1b5ce8
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2FieldModel.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2016-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.bmv2.api.context;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * A BMv2 header field model.
+ */
+@Beta
+public final class Bmv2FieldModel {
+
+    private final Bmv2HeaderModel header;
+    private final Bmv2FieldTypeModel type;
+
+    protected Bmv2FieldModel(Bmv2HeaderModel header, Bmv2FieldTypeModel type) {
+        this.header = header;
+        this.type = type;
+    }
+
+    /**
+     * Returns the header instance of this field instance.
+     *
+     * @return a header instance
+     */
+    public Bmv2HeaderModel header() {
+        return header;
+    }
+
+    /**
+     * Returns the type of this field instance.
+     *
+     * @return a field type value
+     */
+    public Bmv2FieldTypeModel type() {
+        return type;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(header, type);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bmv2FieldModel other = (Bmv2FieldModel) obj;
+        return Objects.equal(this.header, other.header)
+                && Objects.equal(this.type, other.type);
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("header", header)
+                .add("type", type)
+                .toString();
+    }
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2FieldTypeModel.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2FieldTypeModel.java
new file mode 100644
index 0000000..5b944cc
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2FieldTypeModel.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2016-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.bmv2.api.context;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * A BMv2 header type field model.
+ */
+@Beta
+public final class Bmv2FieldTypeModel {
+
+    private final String name;
+    private final int bitWidth;
+
+    protected Bmv2FieldTypeModel(String name, int bitWidth) {
+        this.name = name;
+        this.bitWidth = bitWidth;
+    }
+
+    /**
+     * Returns the name of this header type field.
+     *
+     * @return a string value
+     */
+    public String name() {
+        return name;
+    }
+
+    /**
+     * Returns the bit width of this header type field.
+     *
+     * @return an integer value
+     */
+    public int bitWidth() {
+        return bitWidth;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(name, bitWidth);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bmv2FieldTypeModel other = (Bmv2FieldTypeModel) obj;
+        return Objects.equal(this.name, other.name)
+                && Objects.equal(this.bitWidth, other.bitWidth);
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("name", name)
+                .add("bitWidth", bitWidth)
+                .toString();
+    }
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2FlowRuleTranslator.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2FlowRuleTranslator.java
new file mode 100644
index 0000000..3fb5b11
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2FlowRuleTranslator.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2016-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.bmv2.api.context;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.bmv2.api.runtime.Bmv2TableEntry;
+import org.onosproject.net.flow.FlowRule;
+
+/**
+ * Translator of ONOS flow rules to BMv2 table entries.
+ */
+@Beta
+public interface Bmv2FlowRuleTranslator {
+
+    /**
+     * Returns a BMv2 table entry equivalent to the given flow rule for the given context.
+     * <p>
+     * Translation is performed according to the following logic:
+     * <ul>
+     *  <li> table name: obtained from the context interpreter {@link Bmv2Interpreter#tableIdMap() table ID map}.
+     *  <li> match key: is built using both the context interpreter {@link Bmv2Interpreter#criterionTypeMap() criterion
+     *  map} and all {@link org.onosproject.bmv2.api.runtime.Bmv2ExtensionSelector extension selectors} (if any).
+     *  <li> action: is built using the context interpreter
+     *          {@link Bmv2Interpreter#mapTreatment(org.onosproject.net.flow.TrafficTreatment, Bmv2Configuration)
+     *          treatment mapping function} or the flow rule
+     *          {@link org.onosproject.bmv2.api.runtime.Bmv2ExtensionTreatment} extension treatment} (if any).
+     *  <li> timeout: if the table supports timeout, use the same as the flow rule, otherwise none (i.e. permanent
+     *          entry).
+     * </ul>
+     *
+     * @param rule    a flow rule
+     * @param context a context
+     * @return a BMv2 table entry
+     * @throws Bmv2FlowRuleTranslatorException if the flow rule cannot be translated
+     */
+    Bmv2TableEntry translate(FlowRule rule, Bmv2DeviceContext context) throws Bmv2FlowRuleTranslatorException;
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2FlowRuleTranslatorException.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2FlowRuleTranslatorException.java
new file mode 100644
index 0000000..49f999d
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2FlowRuleTranslatorException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2016-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.bmv2.api.context;
+
+/**
+ * BMv2 flow rule translator exception.
+ */
+public class Bmv2FlowRuleTranslatorException extends Exception {
+
+    public Bmv2FlowRuleTranslatorException(String msg) {
+        super(msg);
+    }
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2HeaderModel.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2HeaderModel.java
new file mode 100644
index 0000000..5c1f4ff
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2HeaderModel.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2016-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.bmv2.api.context;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * BMv2 header instance model.
+ */
+@Beta
+public final class Bmv2HeaderModel {
+
+    private final String name;
+    private final int id;
+    private final Bmv2HeaderTypeModel type;
+    private final boolean isMetadata;
+
+    /**
+     * Creates a new header instance model.
+     *
+     * @param name     name
+     * @param id       id
+     * @param type     header type
+     * @param metadata if is metadata
+     */
+    protected Bmv2HeaderModel(String name, int id, Bmv2HeaderTypeModel type, boolean metadata) {
+        this.name = name;
+        this.id = id;
+        this.type = type;
+        this.isMetadata = metadata;
+    }
+
+    /**
+     * Returns the name of this header instance.
+     *
+     * @return a string value
+     */
+    public String name() {
+        return name;
+    }
+
+    /**
+     * Return the id of this header instance.
+     *
+     * @return an integer value
+     */
+    public int id() {
+        return id;
+    }
+
+    /**
+     * Return the type of this header instance.
+     *
+     * @return a header type value
+     */
+    public Bmv2HeaderTypeModel type() {
+        return type;
+    }
+
+    /**
+     * Return true if this header instance is a metadata, false elsewhere.
+     *
+     * @return a boolean value
+     */
+    public boolean isMetadata() {
+        return isMetadata;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(name, id, type, isMetadata);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bmv2HeaderModel other = (Bmv2HeaderModel) obj;
+        return Objects.equal(this.name, other.name)
+                && Objects.equal(this.id, other.id)
+                && Objects.equal(this.type, other.type)
+                && Objects.equal(this.isMetadata, other.isMetadata);
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("name", name)
+                .add("id", id)
+                .add("type", type)
+                .add("isMetadata", isMetadata)
+                .toString();
+    }
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2HeaderTypeModel.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2HeaderTypeModel.java
new file mode 100644
index 0000000..5350340
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2HeaderTypeModel.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2016-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.bmv2.api.context;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * BMv2 header type model.
+ */
+@Beta
+public final class Bmv2HeaderTypeModel {
+
+    private final String name;
+    private final int id;
+    private final LinkedHashMap<String, Bmv2FieldTypeModel> fields = Maps.newLinkedHashMap();
+
+    /**
+     * Creates a new header type model.
+     *
+     * @param name       name
+     * @param id         id
+     * @param fieldTypes fields
+     */
+    protected Bmv2HeaderTypeModel(String name, int id, List<Bmv2FieldTypeModel> fieldTypes) {
+        this.name = name;
+        this.id = id;
+        fieldTypes.forEach(f -> this.fields.put(f.name(), f));
+    }
+
+    /**
+     * Returns this header type name.
+     *
+     * @return name
+     */
+    public String name() {
+        return name;
+    }
+
+    /**
+     * Returns this header type id.
+     *
+     * @return id
+     */
+    public int id() {
+        return id;
+    }
+
+    /**
+     * Returns this header type's field defined by the passed name, null if
+     * not present.
+     *
+     * @param fieldName field name
+     * @return field or null
+     */
+    public Bmv2FieldTypeModel field(String fieldName) {
+        return fields.get(fieldName);
+    }
+
+    /**
+     * Return and immutable list of header fields for this header
+     * type. The list is ordered according to the values defined in the
+     * model.
+     *
+     * @return list of fields
+     */
+    public List<Bmv2FieldTypeModel> fields() {
+        return ImmutableList.copyOf(fields.values());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(name, id, fields);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bmv2HeaderTypeModel other = (Bmv2HeaderTypeModel) obj;
+        return Objects.equal(this.name, other.name)
+                && Objects.equal(this.id, other.id)
+                && Objects.equal(this.fields, other.fields);
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("name", name)
+                .add("id", id)
+                .add("fields", fields)
+                .toString();
+    }
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2Interpreter.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2Interpreter.java
new file mode 100644
index 0000000..8d44494
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2Interpreter.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2016-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.bmv2.api.context;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableBiMap;
+import org.onosproject.bmv2.api.runtime.Bmv2Action;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criterion;
+
+/**
+ * A BMv2 configuration interpreter.
+ */
+@Beta
+public interface Bmv2Interpreter {
+
+    /**
+     * Returns a bi-map describing a one-to-one relationship between ONOS flow rule table IDs and BMv2 table names.
+     *
+     * @return a {@link com.google.common.collect.BiMap} where the key is a ONOS flow rule table id and
+     * the value is a BMv2 table names
+     */
+    ImmutableBiMap<Integer, String> tableIdMap();
+
+    /**
+     * Returns a bi-map describing a one-to-one relationship between ONOS criterion types and BMv2 header field names.
+     * Header field names are formatted using the notation {@code header_name.field_member_name}.
+     *
+     * @return a {@link com.google.common.collect.BiMap} where the keys are ONOS criterion types and the values are
+     * BMv2 header field names
+     */
+    ImmutableBiMap<Criterion.Type, String> criterionTypeMap();
+
+    /**
+     * Return a BMv2 action that is functionally equivalent to the given ONOS traffic treatment for the given
+     * configuration.
+     *
+     * @param treatment     a ONOS traffic treatment
+     * @param configuration a BMv2 configuration
+     * @return a BMv2 action object
+     * @throws Bmv2InterpreterException if the treatment cannot be mapped to any BMv2 action
+     */
+    Bmv2Action mapTreatment(TrafficTreatment treatment, Bmv2Configuration configuration)
+            throws Bmv2InterpreterException;
+
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2InterpreterException.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2InterpreterException.java
new file mode 100644
index 0000000..5e774ad
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2InterpreterException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2016-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.bmv2.api.context;
+
+/**
+ * A BMv2 interpreter exception.
+ */
+public class Bmv2InterpreterException extends Exception {
+
+    public Bmv2InterpreterException(String message) {
+        super(message);
+    }
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2RuntimeDataModel.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2RuntimeDataModel.java
new file mode 100644
index 0000000..e97d2bb
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2RuntimeDataModel.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2016-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.bmv2.api.context;
+
+import com.google.common.annotations.Beta;
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * A BMv2 action runtime data model.
+ */
+@Beta
+public final class Bmv2RuntimeDataModel {
+
+    private final String name;
+    private final int bitWidth;
+
+    /**
+     * Creates a new runtime data model.
+     *
+     * @param name     name
+     * @param bitWidth bitwidth
+     */
+    protected Bmv2RuntimeDataModel(String name, int bitWidth) {
+        this.name = name;
+        this.bitWidth = bitWidth;
+    }
+
+    /**
+     * Return the name of this runtime data.
+     *
+     * @return a string value
+     */
+    public String name() {
+        return name;
+    }
+
+    /**
+     * Return the bit width of this runtime data.
+     *
+     * @return an integer value
+     */
+    public int bitWidth() {
+        return bitWidth;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(name, bitWidth);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bmv2RuntimeDataModel other = (Bmv2RuntimeDataModel) obj;
+        return Objects.equals(this.name, other.name)
+                && Objects.equals(this.bitWidth, other.bitWidth);
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("name", name)
+                .add("bitWidth", bitWidth)
+                .toString();
+    }
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2TableKeyModel.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2TableKeyModel.java
new file mode 100644
index 0000000..8f2f0222
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2TableKeyModel.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2016-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.bmv2.api.context;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+import org.onosproject.bmv2.api.runtime.Bmv2MatchParam;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * A BMv2 table key model.
+ */
+@Beta
+public final class Bmv2TableKeyModel {
+
+    private final Bmv2MatchParam.Type matchType;
+    private final Bmv2FieldModel field;
+
+    /**
+     * Creates a new table key model.
+     *
+     * @param matchType match type
+     * @param field     field instance
+     */
+    protected Bmv2TableKeyModel(Bmv2MatchParam.Type matchType, Bmv2FieldModel field) {
+        this.matchType = matchType;
+        this.field = field;
+    }
+
+    /**
+     * Returns the match type of this key.
+     *
+     * @return a string value
+     * TODO returns enum of match type
+     */
+    public Bmv2MatchParam.Type matchType() {
+        return matchType;
+    }
+
+    /**
+     * Returns the header field instance matched by this key.
+     *
+     * @return a header field value
+     */
+    public Bmv2FieldModel field() {
+        return field;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(matchType, field);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bmv2TableKeyModel other = (Bmv2TableKeyModel) obj;
+        return Objects.equal(this.matchType, other.matchType)
+                && Objects.equal(this.field, other.field);
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("matchType", matchType)
+                .add("field", field)
+                .toString();
+    }
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2TableModel.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2TableModel.java
new file mode 100644
index 0000000..8f6fa55
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/Bmv2TableModel.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2016-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.bmv2.api.context;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * A BMv2 table model.
+ */
+@Beta
+public final class Bmv2TableModel {
+
+    private final String name;
+    private final int id;
+    private final String matchType;
+    private final String type;
+    private final int maxSize;
+    private final boolean hasCounters;
+    private final boolean hasTimeouts;
+    private final List<Bmv2TableKeyModel> keys;
+    private final Set<Bmv2ActionModel> actions;
+
+    /**
+     * Creates a new table model.
+     *
+     * @param name           name
+     * @param id             id
+     * @param matchType      match type
+     * @param type           type
+     * @param maxSize        max number of entries
+     * @param withCounters   if table has counters
+     * @param supportTimeout if table supports aging
+     * @param keys           list of match keys
+     * @param actions        list of actions
+     */
+    protected Bmv2TableModel(String name, int id, String matchType, String type,
+                             int maxSize, boolean withCounters, boolean supportTimeout,
+                             List<Bmv2TableKeyModel> keys, Set<Bmv2ActionModel> actions) {
+        this.name = name;
+        this.id = id;
+        this.matchType = matchType;
+        this.type = type;
+        this.maxSize = maxSize;
+        this.hasCounters = withCounters;
+        this.hasTimeouts = supportTimeout;
+        this.keys = keys;
+        this.actions = actions;
+    }
+
+    /**
+     * Returns the name of this table.
+     *
+     * @return a string value
+     */
+    public String name() {
+        return name;
+    }
+
+    /**
+     * Returns the id of this table.
+     *
+     * @return an integer value
+     */
+    public int id() {
+        return id;
+    }
+
+    /**
+     * Return the match type of this table.
+     *
+     * @return a string value
+     */
+    public String matchType() {
+        return matchType;
+    }
+
+    /**
+     * Return the match type of this table.
+     *
+     * @return a string value
+     */
+    public String type() {
+        return type;
+    }
+
+    /**
+     * Returns the maximum number of entries supported by this table.
+     *
+     * @return an integer value
+     */
+    public int maxSize() {
+        return maxSize;
+    }
+
+    /**
+     * Returns true if this table has counters, false otherwise.
+     *
+     * @return a boolean value
+     */
+    public boolean hasCounters() {
+        return hasCounters;
+    }
+
+    /**
+     * Returns true if this table supports aging, false otherwise.
+     *
+     * @return a boolean value
+     */
+    public boolean hasTimeouts() {
+        return hasTimeouts;
+    }
+
+    /**
+     * Returns the list of match keys supported by this table.
+     * The list is ordered accordingly to the model's table definition.
+     *
+     * @return a list of match keys
+     */
+    public List<Bmv2TableKeyModel> keys() {
+        return keys;
+    }
+
+    /**
+     * Returns the set of actions supported by this table.
+     *
+     * @return a list of actions
+     */
+    public Set<Bmv2ActionModel> actions() {
+        return actions;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(name, id, matchType, type, maxSize, hasCounters,
+                                hasTimeouts, keys, actions);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bmv2TableModel other = (Bmv2TableModel) obj;
+        return Objects.equal(this.name, other.name)
+                && Objects.equal(this.id, other.id)
+                && Objects.equal(this.matchType, other.matchType)
+                && Objects.equal(this.type, other.type)
+                && Objects.equal(this.maxSize, other.maxSize)
+                && Objects.equal(this.hasCounters, other.hasCounters)
+                && Objects.equal(this.hasTimeouts, other.hasTimeouts)
+                && Objects.equal(this.keys, other.keys)
+                && Objects.equal(this.actions, other.actions);
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("name", name)
+                .add("id", id)
+                .add("matchType", matchType)
+                .add("type", type)
+                .add("maxSize", maxSize)
+                .add("hasCounters", hasCounters)
+                .add("hasTimeouts", hasTimeouts)
+                .add("keys", keys)
+                .add("actions", actions)
+                .toString();
+    }
+
+}
\ No newline at end of file
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/package-info.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/package-info.java
new file mode 100644
index 0000000..7489aa4
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/context/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016-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.
+ */
+
+/**
+ * BMv2 device context API.
+ */
+package org.onosproject.bmv2.api.context;
\ No newline at end of file
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/package-info.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/package-info.java
new file mode 100644
index 0000000..d7e4475
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014-2016 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.
+ */
+
+/**
+ * BMv2 protocol API.
+ */
+package org.onosproject.bmv2.api;
\ No newline at end of file
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2Action.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2Action.java
new file mode 100644
index 0000000..0f1983f
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2Action.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2016-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.bmv2.api.runtime;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.Lists;
+import org.onlab.util.ImmutableByteSequence;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * An action of a BMv2 match-action table entry.
+ */
+public final class Bmv2Action {
+
+    private final String name;
+    private final List<ImmutableByteSequence> parameters;
+
+    private Bmv2Action(String name, List<ImmutableByteSequence> parameters) {
+        // hide constructor
+        this.name = name;
+        this.parameters = parameters;
+    }
+
+    /**
+     * Returns a new action builder.
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Return the name of this action.
+     *
+     * @return action name
+     */
+    public final String name() {
+        return name;
+    }
+
+    /**
+     * Returns an immutable view of the ordered list of parameters of this
+     * action.
+     *
+     * @return list of byte sequence
+     */
+    public final List<ImmutableByteSequence> parameters() {
+        return Collections.unmodifiableList(parameters);
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(name, parameters);
+    }
+
+    @Override
+    public final boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bmv2Action other = (Bmv2Action) obj;
+        return Objects.equals(this.name, other.name)
+                && Objects.equals(this.parameters, other.parameters);
+    }
+
+    @Override
+    public final String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("name", name)
+                .add("parameters", parameters)
+                .toString();
+    }
+
+    /**
+     * A BMv2 action builder.
+     */
+    public static final class Builder {
+
+        private String name = null;
+        private List<ImmutableByteSequence> parameters;
+
+        private Builder() {
+            this.parameters = Lists.newArrayList();
+        }
+
+        /**
+         * Set the action name.
+         *
+         * @param actionName a string value
+         * @return this
+         */
+        public Builder withName(String actionName) {
+            this.name = checkNotNull(actionName);
+            return this;
+        }
+
+        /**
+         * Add a parameter at the end of the parameters list.
+         *
+         * @param parameter a ByteBuffer value
+         * @return this
+         */
+        public Builder addParameter(ImmutableByteSequence parameter) {
+            parameters.add(checkNotNull(parameter));
+            return this;
+        }
+
+        /**
+         * Builds a BMv2 action object.
+         *
+         * @return a BMv2 action
+         */
+        public Bmv2Action build() {
+            checkState(name != null, "action name not set");
+            return new Bmv2Action(name, parameters);
+        }
+    }
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2Device.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2Device.java
new file mode 100644
index 0000000..703ec59
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2Device.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2016-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.bmv2.api.runtime;
+
+import com.google.common.base.Objects;
+import org.onosproject.net.DeviceId;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Representation of a BMv2 device.
+ */
+public final class Bmv2Device {
+
+    public static final String N_A = "n/a";
+
+    public static final String SCHEME = "bmv2";
+    public static final String PROTOCOL = "bmv2-thrift";
+    public static final String MANUFACTURER = "p4.org";
+    public static final String HW_VERSION = "bmv2";
+    public static final String SW_VERSION = N_A;
+    public static final String SERIAL_NUMBER = N_A;
+
+    private final String thriftServerHost;
+    private final int thriftServerPort;
+    private final int internalDeviceId;
+
+    /**
+     * Creates a new BMv2 device object.
+     *
+     * @param thriftServerHost the hostname / IP address of the Thrift runtime server running on the device
+     * @param thriftServerPort the port of the Thrift runtime server running on the device
+     * @param internalDeviceId the internal device ID number
+     */
+    public Bmv2Device(String thriftServerHost, int thriftServerPort, int internalDeviceId) {
+        this.thriftServerHost = checkNotNull(thriftServerHost, "host cannot be null");
+        this.thriftServerPort = checkNotNull(thriftServerPort, "port cannot be null");
+        this.internalDeviceId = internalDeviceId;
+    }
+
+    /**
+     * Returns a Bmv2Device representing the given deviceId.
+     *
+     * @param deviceId a deviceId
+     * @return
+     */
+    public static Bmv2Device of(DeviceId deviceId) {
+        return DeviceIdParser.parse(checkNotNull(deviceId, "deviceId cannot be null"));
+    }
+
+    /**
+     * Returns the hostname (or IP address) of the Thrift runtime server running on the device.
+     *
+     * @return a string value
+     */
+    public String thriftServerHost() {
+        return thriftServerHost;
+    }
+
+    /**
+     * Returns the port of the Thrift runtime server running on the device.
+     *
+     * @return an integer value
+     */
+    public int thriftServerPort() {
+        return thriftServerPort;
+    }
+
+    /**
+     * Returns the BMv2-internal device ID, which is an integer arbitrary chosen at device boot.
+     * Such an ID must not be confused with the ONOS-internal {@link org.onosproject.net.DeviceId}.
+     *
+     * @return an integer value
+     */
+    public int internalDeviceId() {
+        return internalDeviceId;
+    }
+
+    /**
+     * Returns a new ONOS device ID for this device.
+     *
+     * @return a new device ID
+     */
+    public DeviceId asDeviceId() {
+        try {
+            // TODO: include internalDeviceId number in the deviceId URI
+            return DeviceId.deviceId(new URI(SCHEME, this.thriftServerHost + ":" + this.thriftServerPort,
+                                             String.valueOf(this.internalDeviceId)));
+        } catch (URISyntaxException e) {
+            throw new IllegalArgumentException("Unable to build deviceID for device " + this.toString(), e);
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(thriftServerHost, thriftServerPort, internalDeviceId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bmv2Device other = (Bmv2Device) obj;
+        return Objects.equal(this.thriftServerHost, other.thriftServerHost)
+                && Objects.equal(this.thriftServerPort, other.thriftServerPort)
+                && Objects.equal(this.internalDeviceId, other.internalDeviceId);
+    }
+
+    @Override
+    public String toString() {
+        return asDeviceId().toString();
+    }
+
+    private static class DeviceIdParser {
+
+        private static final Pattern REGEX = Pattern.compile(SCHEME + ":(.+):(\\d+)#(\\d+)");
+
+        public static Bmv2Device parse(DeviceId deviceId) {
+            Matcher matcher = REGEX.matcher(deviceId.toString());
+            if (matcher.find()) {
+                String host = matcher.group(1);
+                int port = Integer.valueOf(matcher.group(2));
+                int internalDeviceId = Integer.valueOf(matcher.group(3));
+                return new Bmv2Device(host, port, internalDeviceId);
+            } else {
+                throw new RuntimeException("Unable to parse bmv2 device id string: " + deviceId.toString());
+            }
+        }
+    }
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2DeviceAgent.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2DeviceAgent.java
new file mode 100644
index 0000000..ff02ea3
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2DeviceAgent.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2016-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.bmv2.api.runtime;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.DeviceId;
+
+import java.util.Collection;
+
+/**
+ * An agent to control a BMv2 device.
+ */
+public interface Bmv2DeviceAgent {
+
+    /**
+     * Returns the device ID of this agent.
+     *
+     * @return a device id
+     */
+    DeviceId deviceId();
+
+    /**
+     * Pings the device, returns true if the device is reachable, false otherwise.
+     *
+     * @return true if reachable, false otherwise
+     */
+    boolean ping();
+
+    /**
+     * Adds a new table entry.
+     *
+     * @param entry a table entry value
+     * @return table-specific entry ID
+     * @throws Bmv2RuntimeException if any error occurs
+     */
+    long addTableEntry(Bmv2TableEntry entry) throws Bmv2RuntimeException;
+
+    /**
+     * Modifies an existing table entry by updating its action.
+     *
+     * @param tableName string value of table name
+     * @param entryId   long value of entry ID
+     * @param action    an action value
+     * @throws Bmv2RuntimeException if any error occurs
+     */
+    void modifyTableEntry(String tableName, long entryId, Bmv2Action action) throws Bmv2RuntimeException;
+
+    /**
+     * Deletes currently installed entry.
+     *
+     * @param tableName string value of table name
+     * @param entryId   long value of entry ID
+     * @throws Bmv2RuntimeException if any error occurs
+     */
+    void deleteTableEntry(String tableName, long entryId) throws Bmv2RuntimeException;
+
+    /**
+     * Sets table default action.
+     *
+     * @param tableName string value of table name
+     * @param action    an action value
+     * @throws Bmv2RuntimeException if any error occurs
+     */
+    void setTableDefaultAction(String tableName, Bmv2Action action) throws Bmv2RuntimeException;
+
+    /**
+     * Returns information on the ports currently configured in the switch.
+     *
+     * @return collection of port information
+     * @throws Bmv2RuntimeException if any error occurs
+     */
+    Collection<Bmv2PortInfo> getPortsInfo() throws Bmv2RuntimeException;
+
+    /**
+     * Return a string representation of the given table content.
+     *
+     * @param tableName string value of table name
+     * @return table string dump
+     * @throws Bmv2RuntimeException if any error occurs
+     */
+    String dumpTable(String tableName) throws Bmv2RuntimeException;
+
+    /**
+     * Requests the device to transmit a given byte sequence over the given port.
+     *
+     * @param portNumber a port number
+     * @param packet a byte sequence
+     * @throws Bmv2RuntimeException
+     */
+    void transmitPacket(int portNumber, ImmutableByteSequence packet) throws Bmv2RuntimeException;
+
+    /**
+     * Resets the state of the switch (e.g. delete all entries, etc.).
+     *
+     * @throws Bmv2RuntimeException if any error occurs
+     */
+    void resetState() throws Bmv2RuntimeException;
+
+    /**
+     * Returns the JSON configuration currently used to process packets.
+     *
+     * @return a JSON-formatted string value
+     * @throws Bmv2RuntimeException if any error occurs
+     */
+    String dumpJsonConfig() throws Bmv2RuntimeException;
+
+    /**
+     * Returns the md5 sum of the JSON-formatted model configuration currently used to process packets.
+     *
+     * @return a string value
+     * @throws Bmv2RuntimeException if any error occurs
+     */
+    String getJsonConfigMd5() throws Bmv2RuntimeException;
+
+    /**
+     * Returns the counter values for a given table and entry.
+     *
+     * @param tableName a table name
+     * @param entryId an entry id
+     * @return a pair of long values, where the left value is the number of bytes and the right the number of packets
+     * @throws Bmv2RuntimeException if any error occurs
+     */
+    Pair<Long, Long> readTableEntryCounter(String tableName, long entryId) throws Bmv2RuntimeException;
+
+    /**
+     * Returns the counter values for a given counter and index.
+     *
+     * @param counterName a counter name
+     * @param index       an integer value
+     * @return a pair of long values, where the left value is the number of bytes and the right value is the number of
+     * packets
+     * @throws Bmv2RuntimeException if any error occurs
+     */
+    Pair<Long, Long> readCounter(String counterName, int index) throws Bmv2RuntimeException;
+
+    /**
+     * Returns the ID of the current BMv2 process instance (used to distinguish between different executions of the
+     * same BMv2 device).
+     *
+     * @return an integer value
+     * @throws Bmv2RuntimeException if any error occurs
+     */
+    int getProcessInstanceId() throws Bmv2RuntimeException;
+
+    /**
+     * Uploads a new JSON configuration on the device.
+     *
+     * @param jsonString a string value
+     * @throws Bmv2RuntimeException if any error occurs
+     */
+    void loadNewJsonConfig(String jsonString) throws Bmv2RuntimeException;
+
+    /**
+     * Triggers a configuration swap on the device.
+     *
+     * @throws Bmv2RuntimeException
+     */
+    void swapJsonConfig() throws Bmv2RuntimeException;
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExactMatchParam.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExactMatchParam.java
new file mode 100644
index 0000000..d02dd2d
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExactMatchParam.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2016-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.bmv2.api.runtime;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import org.onlab.util.ImmutableByteSequence;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Representation of a BMv2 exact match parameter.
+ */
+public final class Bmv2ExactMatchParam implements Bmv2MatchParam {
+
+    private final ImmutableByteSequence value;
+
+    /**
+     * Creates a new match parameter object that matches exactly on the
+     * given byte sequence.
+     *
+     * @param value a byte sequence value
+     */
+    public Bmv2ExactMatchParam(ImmutableByteSequence value) {
+        this.value = checkNotNull(value, "value cannot be null");
+    }
+
+    @Override
+    public Type type() {
+        return Type.EXACT;
+    }
+
+    /**
+     * Return the byte sequence value matched by this parameter.
+     *
+     * @return an immutable byte buffer value
+     */
+    public ImmutableByteSequence value() {
+        return this.value;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(value);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bmv2ExactMatchParam other = (Bmv2ExactMatchParam) obj;
+        return Objects.equal(this.value, other.value);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("value", value)
+                .toString();
+    }
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionSelector.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionSelector.java
new file mode 100644
index 0000000..10bc9dc
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionSelector.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2016-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.bmv2.api.runtime;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.net.flow.AbstractExtension;
+import org.onosproject.net.flow.criteria.ExtensionSelector;
+import org.onosproject.net.flow.criteria.ExtensionSelectorType;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Extension selector for BMv2 used as a wrapper for multiple BMv2 match parameters.
+ */
+public final class Bmv2ExtensionSelector extends AbstractExtension implements ExtensionSelector {
+
+    private final KryoNamespace appKryo = new KryoNamespace.Builder()
+            .register(HashMap.class)
+            .register(Bmv2MatchParam.class)
+            .register(Bmv2ExactMatchParam.class)
+            .register(Bmv2TernaryMatchParam.class)
+            .register(Bmv2LpmMatchParam.class)
+            .register(Bmv2ValidMatchParam.class)
+            .build();
+
+    private Map<String, Bmv2MatchParam> parameterMap;
+
+    /**
+     * Creates a new BMv2 extension selector for the given match parameters map, where the keys are expected to be field
+     * names formatted as headerName.fieldMemberName (e.g. ethernet.dstAddr).
+     *
+     * @param paramMap a map
+     */
+    public Bmv2ExtensionSelector(Map<String, Bmv2MatchParam> paramMap) {
+        this.parameterMap = checkNotNull(paramMap, "param map cannot be null");
+    }
+
+    /**
+     * Returns the match parameters map of this selector.
+     *
+     * @return a match parameter map
+     */
+    public Map<String, Bmv2MatchParam> parameterMap() {
+        return parameterMap;
+    }
+
+
+    @Override
+    public ExtensionSelectorType type() {
+        return ExtensionSelectorType.ExtensionSelectorTypes.BMV2_MATCH_PARAMS.type();
+    }
+
+    @Override
+    public byte[] serialize() {
+        return appKryo.serialize(parameterMap);
+    }
+
+    @Override
+    public void deserialize(byte[] data) {
+        this.parameterMap = appKryo.deserialize(data);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(parameterMap);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bmv2ExtensionSelector other = (Bmv2ExtensionSelector) obj;
+        return Objects.equal(this.parameterMap, other.parameterMap);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("parameterMap", parameterMap)
+                .toString();
+    }
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionTreatment.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionTreatment.java
new file mode 100644
index 0000000..b4c9790
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionTreatment.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2016-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.bmv2.api.runtime;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.net.flow.AbstractExtension;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
+import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
+
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.BMV2_ACTION;
+
+/**
+ * Extension treatment for BMv2 used as a wrapper for a {@link Bmv2Action}.
+ */
+public final class Bmv2ExtensionTreatment extends AbstractExtension implements ExtensionTreatment {
+
+    private final KryoNamespace appKryo = new KryoNamespace.Builder().build();
+    private Bmv2Action action;
+
+    public Bmv2ExtensionTreatment(Bmv2Action action) {
+        this.action = action;
+    }
+
+    public Bmv2Action getAction() {
+        return action;
+    }
+
+    @Override
+    public ExtensionTreatmentType type() {
+        return BMV2_ACTION.type();
+    }
+
+    @Override
+    public byte[] serialize() {
+        return appKryo.serialize(action);
+    }
+
+    @Override
+    public void deserialize(byte[] data) {
+        action = appKryo.deserialize(data);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(action);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bmv2ExtensionTreatment other = (Bmv2ExtensionTreatment) obj;
+        return Objects.equal(this.action, other.action);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("action", action)
+                .toString();
+    }
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2FlowRuleWrapper.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2FlowRuleWrapper.java
new file mode 100644
index 0000000..a7b5b46
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2FlowRuleWrapper.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2016-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.bmv2.api.runtime;
+
+import com.google.common.base.Objects;
+import org.onosproject.net.flow.FlowRule;
+
+import java.util.Date;
+
+/**
+ * A wrapper class for a ONOS flow rule installed on a BMv2 device.
+ */
+public class Bmv2FlowRuleWrapper {
+
+    private final FlowRule rule;
+    private final long entryId;
+    private final Date creationDate;
+
+    /**
+     * Creates a new flow rule wrapper.
+     *
+     * @param rule         a flow rule
+     * @param entryId      a BMv2 table entry ID
+     * @param creationDate the creation date of the flow rule
+     */
+    public Bmv2FlowRuleWrapper(FlowRule rule, long entryId, Date creationDate) {
+        this.rule = rule;
+        this.entryId = entryId;
+        this.creationDate = new Date();
+    }
+
+    /**
+     * Returns the flow rule contained by this wrapper.
+     *
+     * @return a flow rule
+     */
+    public FlowRule rule() {
+        return rule;
+    }
+
+    /**
+     * Return the seconds since when this flow rule was installed on the device.
+     *
+     * @return an integer value
+     */
+    public long lifeInSeconds() {
+        return (new Date().getTime() - creationDate.getTime()) / 1000;
+    }
+
+    /**
+     * Returns the creation date of this flow rule.
+     *
+     * @return a date
+     */
+    public Date creationDate() {
+        return creationDate;
+    }
+
+    /**
+     * Returns the BMv2 entry ID of this flow rule.
+     *
+     * @return a long value
+     */
+    public long entryId() {
+        return entryId;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(rule, entryId, creationDate);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bmv2FlowRuleWrapper other = (Bmv2FlowRuleWrapper) obj;
+        return Objects.equal(this.rule, other.rule)
+                && Objects.equal(this.entryId, other.entryId)
+                && Objects.equal(this.creationDate, other.creationDate);
+    }
+
+    @Override
+    public String toString() {
+        return creationDate + "-" + rule.hashCode();
+    }
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2LpmMatchParam.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2LpmMatchParam.java
new file mode 100644
index 0000000..ee75d3b
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2LpmMatchParam.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2016-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.bmv2.api.runtime;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import org.onlab.util.ImmutableByteSequence;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Representation of a BMv2 longest prefix match (LPM) parameter.
+ */
+public final class Bmv2LpmMatchParam implements Bmv2MatchParam {
+
+    private final ImmutableByteSequence value;
+    private final int prefixLength;
+
+    /**
+     * Creates a new LPM parameter using the given byte sequence value and
+     * prefix length.
+     *
+     * @param value        a byte sequence value
+     * @param prefixLength an integer value
+     */
+    public Bmv2LpmMatchParam(ImmutableByteSequence value, int prefixLength) {
+        checkArgument(prefixLength >= 0, "prefix length cannot be negative");
+        this.value = checkNotNull(value);
+        this.prefixLength = prefixLength;
+    }
+
+    @Override
+    public Bmv2MatchParam.Type type() {
+        return Type.LPM;
+    }
+
+    /**
+     * Returns the byte sequence value of this parameter.
+     *
+     * @return a byte sequence value
+     */
+    public ImmutableByteSequence value() {
+        return this.value;
+    }
+
+    /**
+     * Returns the prefix length of this parameter.
+     *
+     * @return an integer value
+     */
+    public int prefixLength() {
+        return this.prefixLength;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(value, prefixLength);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bmv2LpmMatchParam other = (Bmv2LpmMatchParam) obj;
+        return Objects.equal(this.value, other.value)
+                && Objects.equal(this.prefixLength, other.prefixLength);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("value", value)
+                .add("prefixLength", prefixLength)
+                .toString();
+    }
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2MatchKey.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2MatchKey.java
new file mode 100644
index 0000000..c0c6b7c
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2MatchKey.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2016-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.bmv2.api.runtime;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.Lists;
+import org.onlab.util.ImmutableByteSequence;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * A match key of a BMv2 match-action table entry.
+ */
+public final class Bmv2MatchKey {
+
+    private final List<Bmv2MatchParam> matchParams;
+
+    private Bmv2MatchKey(List<Bmv2MatchParam> matchParams) {
+        // ban constructor
+        this.matchParams = matchParams;
+    }
+
+    /**
+     * Returns a new match key builder.
+     *
+     * @return a match key builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Returns the list of match parameters of this match key.
+     *
+     * @return list match parameters
+     */
+    public final List<Bmv2MatchParam> matchParams() {
+        return Collections.unmodifiableList(matchParams);
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hashCode(matchParams);
+    }
+
+    @Override
+    public final boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bmv2MatchKey other = (Bmv2MatchKey) obj;
+
+        return Objects.equals(this.matchParams, other.matchParams);
+    }
+
+    @Override
+    public final String toString() {
+        return MoreObjects.toStringHelper(this)
+                .addValue(matchParams)
+                .toString();
+    }
+
+    /**
+     * Builder of a BMv2 match key.
+     */
+    public static final class Builder {
+
+        private List<Bmv2MatchParam> matchParams;
+
+        private Builder() {
+            this.matchParams = Lists.newArrayList();
+        }
+
+        /**
+         * Adds a match parameter to the match key.
+         *
+         * @param param a match parameter
+         * @return this
+         */
+        public Builder add(Bmv2MatchParam param) {
+            this.matchParams.add(checkNotNull(param));
+            return this;
+        }
+
+        /**
+         * Adds a ternary match parameter where all bits are don't-care.
+         *
+         * @param byteLength length in bytes of the parameter
+         * @return this
+         */
+        public Builder withWildcard(int byteLength) {
+            checkArgument(byteLength > 0, "length must be a positive integer");
+            return add(new Bmv2TernaryMatchParam(
+                    ImmutableByteSequence.ofZeros(byteLength),
+                    ImmutableByteSequence.ofZeros(byteLength)));
+        }
+
+        /**
+         * Builds a new match key object.
+         *
+         * @return match key
+         */
+        public Bmv2MatchKey build() {
+            return new Bmv2MatchKey(this.matchParams);
+        }
+    }
+}
\ No newline at end of file
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2MatchParam.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2MatchParam.java
new file mode 100644
index 0000000..3b7142f
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2MatchParam.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2016-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.bmv2.api.runtime;
+
+/**
+ * Representation of a BMv2 match parameter.
+ */
+public interface Bmv2MatchParam {
+
+    /**
+     * Returns the match type of this parameter.
+     *
+     * @return a match type value
+     */
+    Type type();
+
+    /**
+     * BMv2 match types.
+     */
+    enum Type {
+        /**
+         * Exact match type.
+         */
+        EXACT,
+        /**
+         * Ternary match type.
+         */
+        TERNARY,
+        /**
+         * Longest-prefix match type.
+         */
+        LPM,
+        /**
+         * Valid match type.
+         */
+        VALID;
+    }
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ParsedTableEntry.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ParsedTableEntry.java
new file mode 100644
index 0000000..9b4435f
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ParsedTableEntry.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2016-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.bmv2.api.runtime;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+
+/**
+ * Representation of a table entry obtained by parsing a BMv2 table dump.
+ */
+public final class Bmv2ParsedTableEntry {
+    private final long entryId;
+    private final Bmv2MatchKey matchKey;
+    private final Bmv2Action action;
+
+    /**
+     * Creates a new parsed table entry.
+     *
+     * @param entryId  an entry ID
+     * @param matchKey a match key
+     * @param action   an action
+     */
+    public Bmv2ParsedTableEntry(long entryId, Bmv2MatchKey matchKey, Bmv2Action action) {
+        this.entryId = entryId;
+        this.matchKey = matchKey;
+        this.action = action;
+    }
+
+    /**
+     * Returns the entry ID.
+     *
+     * @return a long value
+     */
+    public long entryId() {
+        return entryId;
+    }
+
+    /**
+     * Returns the match key.
+     *
+     * @return a match key object
+     */
+    public Bmv2MatchKey matchKey() {
+        return matchKey;
+    }
+
+    /**
+     * Returns the action.
+     *
+     * @return an action object
+     */
+    public Bmv2Action action() {
+        return action;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(entryId, matchKey, action);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bmv2ParsedTableEntry other = (Bmv2ParsedTableEntry) obj;
+        return Objects.equal(this.entryId, other.entryId)
+                && Objects.equal(this.matchKey, other.matchKey)
+                && Objects.equal(this.action, other.action);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("entryId", entryId)
+                .add("matchKey", matchKey)
+                .add("action", action)
+                .toString();
+    }
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2PortInfo.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2PortInfo.java
new file mode 100644
index 0000000..f53a436
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2PortInfo.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2016-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.bmv2.api.runtime;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+
+/**
+ * Information of a port of a BMv2 device.
+ */
+public final class Bmv2PortInfo {
+
+    private final String ifaceName;
+    private final int number;
+    private final boolean isUp;
+
+    /**
+     * Creates a new port description.
+     *
+     * @param ifaceName the common name of the network interface
+     * @param number    a port number
+     * @param isUp      interface status
+     */
+    public Bmv2PortInfo(String ifaceName, int number, boolean isUp) {
+        this.ifaceName = ifaceName;
+        this.number = number;
+        this.isUp = isUp;
+    }
+
+    /**
+     * Returns the common name the network interface used by this port.
+     *
+     * @return a string value
+     */
+    public String ifaceName() {
+        return ifaceName;
+    }
+
+    /**
+     * Returns the number of this port.
+     *
+     * @return an integer value
+     */
+    public int number() {
+        return number;
+    }
+
+    /**
+     * Returns true if the port is up, false otherwise.
+     *
+     * @return a boolean value
+     */
+    public boolean isUp() {
+        return isUp;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(ifaceName, number, isUp);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bmv2PortInfo other = (Bmv2PortInfo) obj;
+        return Objects.equal(this.ifaceName, other.ifaceName)
+                && Objects.equal(this.number, other.number)
+                && Objects.equal(this.isUp, other.isUp);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("ifaceName", ifaceName)
+                .add("number", number)
+                .add("isUp", isUp)
+                .toString();
+    }
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2RuntimeException.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2RuntimeException.java
new file mode 100644
index 0000000..697c8d2
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2RuntimeException.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2016-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.bmv2.api.runtime;
+
+/**
+ * General exception of the BMv2 runtime APIs.
+ */
+public final class Bmv2RuntimeException extends Exception {
+
+    private final Code code;
+    private String codeString;
+
+    public Bmv2RuntimeException(String message) {
+        super(message);
+        this.code = Code.OTHER;
+        this.codeString = message;
+    }
+
+    public Bmv2RuntimeException(Throwable cause) {
+        super(cause);
+        this.code = Code.OTHER;
+        this.codeString = cause.toString();
+    }
+
+    public Bmv2RuntimeException(Code code) {
+        super(code.name());
+        this.code = code;
+    }
+
+    public Code getCode() {
+        return this.code;
+    }
+
+    public String explain() {
+        return (codeString == null) ? code.name() : code.name() + " " + codeString;
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + " " + explain();
+    }
+
+    public enum Code {
+        TABLE_FULL,
+        TABLE_INVALID_HANDLE,
+        TABLE_EXPIRED_HANDLE,
+        TABLE_COUNTERS_DISABLED,
+        TABLE_METERS_DISABLED,
+        TABLE_AGEING_DISABLED,
+        TABLE_INVALID_TABLE_NAME,
+        TABLE_INVALID_ACTION_NAME,
+        TABLE_WRONG_TABLE_TYPE,
+        TABLE_INVALID_MBR_HANDLE,
+        TABLE_MBR_STILL_USED,
+        TABLE_MBR_ALREADY_IN_GRP,
+        TABLE_MBR_NOT_IN_GRP,
+        TABLE_INVALID_GRP_HANDLE,
+        TABLE_GRP_STILL_USED,
+        TABLE_EMPTY_GRP,
+        TABLE_DUPLICATE_ENTRY,
+        TABLE_BAD_MATCH_KEY,
+        TABLE_INVALID_METER_OPERATION,
+        TABLE_DEFAULT_ACTION_IS_CONST,
+        TABLE_DEFAULT_ENTRY_IS_CONST,
+        TABLE_GENERAL_ERROR,
+        TABLE_UNKNOWN_ERROR,
+
+        DEV_MGR_ERROR_GENERAL,
+        DEV_MGR_UNKNOWN,
+
+        COUNTER_INVALID_NAME,
+        COUNTER_INVALID_INDEX,
+        COUNTER_ERROR_GENERAL,
+        COUNTER_ERROR_UNKNOWN,
+
+        SWAP_CONFIG_DISABLED,
+        SWAP_ONGOING,
+        SWAP_NO_ONGOING,
+        SWAP_ERROR_UKNOWN,
+
+        // TODO: add other codes based on autogenerated Thrift APIs
+
+        OTHER
+    }
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2TableEntry.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2TableEntry.java
new file mode 100644
index 0000000..ce6bd83
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2TableEntry.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2016-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.bmv2.api.runtime;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * An entry of a match-action table in a BMv2 device.
+ */
+public final class Bmv2TableEntry {
+
+    private static final int NO_PRIORITY_VALUE = -1;
+    private static final int NO_TIMEOUT_VALUE = -1;
+
+    private final String tableName;
+    private final Bmv2MatchKey matchKey;
+    private final Bmv2Action action;
+    private final int priority;
+    private final double timeout;
+
+    private Bmv2TableEntry(String tableName, Bmv2MatchKey matchKey,
+                           Bmv2Action action, int priority, double timeout) {
+        this.tableName = tableName;
+        this.matchKey = matchKey;
+        this.action = action;
+        this.priority = priority;
+        this.timeout = timeout;
+    }
+
+    /**
+     * Returns a new BMv2 table entry builder.
+     *
+     * @return a new builder.
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Returns the name of the table where this entry is installed.
+     *
+     * @return table name
+     */
+    public final String tableName() {
+        return this.tableName;
+    }
+
+    /**
+     * Returns the match key of this table entry.
+     *
+     * @return match key
+     */
+    public final Bmv2MatchKey matchKey() {
+        return matchKey;
+    }
+
+    /**
+     * Returns the action of this table entry.
+     *
+     * @return action
+     */
+    public final Bmv2Action action() {
+        return action;
+    }
+
+    /**
+     * Returns true is the entry has a valid priority.
+     *
+     * @return true if priority is set, false elsewhere
+     */
+    public final boolean hasPriority() {
+        return this.priority != NO_PRIORITY_VALUE;
+    }
+
+    /**
+     * Return the priority of this table entry.
+     *
+     * @return priority
+     */
+    public final int priority() {
+        return priority;
+    }
+
+    /**
+     * Returns true is this table entry has a valid timeout.
+     *
+     * @return true if timeout is set, false elsewhere
+     */
+    public final boolean hasTimeout() {
+        return this.timeout != NO_PRIORITY_VALUE;
+    }
+
+    /**
+     * Returns the timeout (in fractional seconds) of this table entry.
+     *
+     * @return a timeout vale (in fractional seconds)
+     */
+    public final double timeout() {
+        return timeout;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(matchKey, action, priority, timeout);
+    }
+
+    @Override
+    public final boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bmv2TableEntry other = (Bmv2TableEntry) obj;
+        return Objects.equals(this.matchKey, other.matchKey)
+                && Objects.equals(this.action, other.action)
+                && Objects.equals(this.priority, other.priority)
+                && Objects.equals(this.timeout, other.timeout);
+    }
+
+    @Override
+    public final String toString() {
+        return com.google.common.base.MoreObjects.toStringHelper(this)
+                .addValue(matchKey)
+                .addValue(action)
+                .add("priority", priority)
+                .add("timeout", timeout)
+                .toString();
+    }
+
+    public static final class Builder {
+
+        private String tableName;
+        private Bmv2MatchKey matchKey;
+        private Bmv2Action action;
+        private int priority = NO_PRIORITY_VALUE;
+        private double timeout = NO_TIMEOUT_VALUE;
+
+        private Builder() {
+            // hide constructor
+        }
+
+        /**
+         * Sets the table name.
+         *
+         * @param tableName a string value
+         * @return this
+         */
+        public Builder withTableName(String tableName) {
+            this.tableName = checkNotNull(tableName, "table name cannot be null");
+            return this;
+        }
+
+        /**
+         * Sets the match key.
+         *
+         * @param matchKey a match key value
+         * @return this
+         */
+        public Builder withMatchKey(Bmv2MatchKey matchKey) {
+            this.matchKey = checkNotNull(matchKey, "match key cannot be null");
+            return this;
+        }
+
+        /**
+         * Sets the action.
+         *
+         * @param action an action value
+         * @return this
+         */
+        public Builder withAction(Bmv2Action action) {
+            this.action = checkNotNull(action, "action cannot be null");
+            return this;
+        }
+
+        public Builder withPriority(int priority) {
+            checkArgument(priority >= 0, "priority cannot be negative");
+            this.priority = priority;
+            return this;
+        }
+
+        /**
+         * Sets the timeout.
+         *
+         * @param timeout a timeout value in fractional seconds
+         * @return this
+         */
+        public Builder withTimeout(double timeout) {
+            checkArgument(timeout > 0, "timeout must be a positive non-zero value");
+            this.timeout = timeout;
+            return this;
+        }
+
+        /**
+         * Build the table entry.
+         *
+         * @return a new table entry object
+         */
+        public Bmv2TableEntry build() {
+            return new Bmv2TableEntry(tableName, matchKey, action, priority,
+                                      timeout);
+
+        }
+    }
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2TableEntryReference.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2TableEntryReference.java
new file mode 100644
index 0000000..fd59fb9
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2TableEntryReference.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2016-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.bmv2.api.runtime;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import org.onosproject.net.DeviceId;
+
+/**
+ * A reference to a table entry installed on a BMv2 device.
+ */
+public final class Bmv2TableEntryReference {
+
+
+    private final DeviceId deviceId;
+    private final String tableName;
+    private final Bmv2MatchKey matchKey;
+
+    /**
+     * Creates a new table entry reference.
+     *
+     * @param deviceId a device ID
+     * @param tableName a table name
+     * @param matchKey a match key
+     */
+    public Bmv2TableEntryReference(DeviceId deviceId, String tableName, Bmv2MatchKey matchKey) {
+        this.deviceId = deviceId;
+        this.tableName = tableName;
+        this.matchKey = matchKey;
+    }
+
+    /**
+     * Returns the device ID of this table entry reference.
+     *
+     * @return a device ID
+     */
+    public DeviceId deviceId() {
+        return deviceId;
+    }
+
+    /**
+     * Returns the name of the table of this table entry reference.
+     *
+     * @return a table name
+     */
+    public String tableName() {
+        return tableName;
+    }
+
+    /**
+     * Returns the match key of this table entry reference.
+     *
+     * @return a match key
+     */
+    public Bmv2MatchKey matchKey() {
+        return matchKey;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(deviceId, tableName, matchKey);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bmv2TableEntryReference other = (Bmv2TableEntryReference) obj;
+        return Objects.equal(this.deviceId, other.deviceId)
+                && Objects.equal(this.tableName, other.tableName)
+                && Objects.equal(this.matchKey, other.matchKey);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("deviceId", deviceId)
+                .add("tableName", tableName)
+                .add("matchKey", matchKey)
+                .toString();
+    }
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2TernaryMatchParam.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2TernaryMatchParam.java
new file mode 100644
index 0000000..60b38f0
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2TernaryMatchParam.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2016-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.bmv2.api.runtime;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import org.onlab.util.ImmutableByteSequence;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * Representation of a BMv2 ternary match parameter.
+ */
+public final class Bmv2TernaryMatchParam implements Bmv2MatchParam {
+
+    private final ImmutableByteSequence value;
+    private final ImmutableByteSequence mask;
+
+    /**
+     * Creates a new ternary match parameter using the given byte sequences of
+     * value and mask.
+     *
+     * @param value a byte sequence value
+     * @param mask  a byte sequence value
+     */
+    public Bmv2TernaryMatchParam(ImmutableByteSequence value,
+                                 ImmutableByteSequence mask) {
+        this.value = checkNotNull(value, "value cannot be null");
+        this.mask = checkNotNull(mask, "value cannot be null");
+        checkState(value.size() == mask.size(),
+                   "value and mask must have equal size");
+    }
+
+    @Override
+    public Type type() {
+        return Type.TERNARY;
+    }
+
+    /**
+     * Returns the byte sequence value of by this parameter.
+     *
+     * @return a byte sequence value
+     */
+    public ImmutableByteSequence value() {
+        return this.value;
+    }
+
+    /**
+     * Returns the byte sequence mask of by this parameter.
+     *
+     * @return a byte sequence value
+     */
+    public ImmutableByteSequence mask() {
+        return this.mask;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(value, mask);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bmv2TernaryMatchParam other = (Bmv2TernaryMatchParam) obj;
+        return Objects.equal(this.value, other.value)
+                && Objects.equal(this.mask, other.mask);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("value", value)
+                .add("mask", mask)
+                .toString();
+    }
+}
\ No newline at end of file
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ValidMatchParam.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ValidMatchParam.java
new file mode 100644
index 0000000..38b8c2a
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ValidMatchParam.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2016-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.bmv2.api.runtime;
+
+
+import com.google.common.base.MoreObjects;
+
+import java.util.Objects;
+
+/**
+ * Representation of a BMv2 valid match parameter.
+ */
+public final class Bmv2ValidMatchParam implements Bmv2MatchParam {
+
+    private final boolean flag;
+
+    /**
+     * Creates a new valid match parameter using the given boolean flag.
+     *
+     * @param flag a boolean value
+     */
+    public Bmv2ValidMatchParam(boolean flag) {
+        this.flag = flag;
+    }
+
+    @Override
+    public Type type() {
+        return Type.VALID;
+    }
+
+    /**
+     * Returns the boolean flag of this parameter.
+     *
+     * @return a boolean value
+     */
+    public boolean flag() {
+        return flag;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(flag);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bmv2ValidMatchParam other = (Bmv2ValidMatchParam) obj;
+        return this.flag == other.flag;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("flag", flag)
+                .toString();
+    }
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/package-info.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/package-info.java
new file mode 100644
index 0000000..d4b1279
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016-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.
+ */
+
+/**
+ * BMv2 runtime API.
+ */
+package org.onosproject.bmv2.api.runtime;
\ No newline at end of file
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/service/Bmv2Controller.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/service/Bmv2Controller.java
new file mode 100644
index 0000000..62b1f55
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/service/Bmv2Controller.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2016-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.bmv2.api.service;
+
+import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent;
+import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
+import org.onosproject.net.DeviceId;
+
+/**
+ * A controller of BMv2 devices.
+ */
+public interface Bmv2Controller {
+
+    /**
+     * Default port.
+     */
+    int DEFAULT_PORT = 40123;
+
+    /**
+     * Return an agent to operate on the given device.
+     *
+     * @param deviceId a device ID
+     * @return a BMv2 agent
+     * @throws Bmv2RuntimeException if the agent is not available
+     */
+    Bmv2DeviceAgent getAgent(DeviceId deviceId) throws Bmv2RuntimeException;
+
+    /**
+     * Returns true if the given device is reachable from this controller, false otherwise.
+     *
+     * @param deviceId a device ID
+     * @return a boolean value
+     */
+    boolean isReacheable(DeviceId deviceId);
+
+    /**
+     * Register the given device listener.
+     *
+     * @param listener a device listener
+     */
+    void addDeviceListener(Bmv2DeviceListener listener);
+
+    /**
+     * Unregister the given device listener.
+     *
+     * @param listener a device listener
+     */
+    void removeDeviceListener(Bmv2DeviceListener listener);
+
+    /**
+     * Register the given packet listener.
+     *
+     * @param listener a packet listener
+     */
+    void addPacketListener(Bmv2PacketListener listener);
+
+    /**
+     * Unregister the given packet listener.
+     *
+     * @param listener a packet listener
+     */
+    void removePacketListener(Bmv2PacketListener listener);
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/service/Bmv2DeviceContextService.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/service/Bmv2DeviceContextService.java
new file mode 100644
index 0000000..ca0a0e5
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/service/Bmv2DeviceContextService.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2016-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.bmv2.api.service;
+
+import org.onosproject.bmv2.api.context.Bmv2DeviceContext;
+import org.onosproject.bmv2.api.context.Bmv2Interpreter;
+import org.onosproject.net.DeviceId;
+
+/**
+ * A service for managing BMv2 device contexts.
+ */
+public interface Bmv2DeviceContextService {
+
+    // TODO: handle the potential configuration states (e.g. RUNNING, SWAP_REQUESTED, etc.)
+
+    /**
+     * Returns the context of a given device. The context returned is the last one for which a configuration swap was
+     * triggered, hence there's no guarantees that the device is enforcing the returned context's  configuration at the
+     * time of the call.
+     *
+     * @param deviceId a device ID
+     * @return a BMv2 device context
+     */
+    Bmv2DeviceContext getContext(DeviceId deviceId);
+
+    /**
+     * Triggers a configuration swap on a given device.
+     *
+     * @param deviceId a device ID
+     * @param context  a BMv2 device context
+     */
+    void triggerConfigurationSwap(DeviceId deviceId, Bmv2DeviceContext context);
+
+    /**
+     * Binds the given interpreter with the given class loader so that other ONOS instances in the cluster can properly
+     * load the interpreter.
+     *
+     * @param interpreterClass an interpreter class
+     * @param loader           a class loader
+     */
+    void registerInterpreterClassLoader(Class<? extends Bmv2Interpreter> interpreterClass, ClassLoader loader);
+
+    /**
+     * Notifies this service that a given device has been updated, meaning a potential context change.
+     * It returns true if the device configuration is the same as the last for which a swap was triggered, false
+     * otherwise. In the last case, the service will asynchronously trigger a swap to the last
+     * configuration stored by this service. If no swap has already been triggered then a default configuration will be
+     * applied.
+     *
+     * @param deviceId a device ID
+     * @return a boolean value
+     */
+    boolean notifyDeviceChange(DeviceId deviceId);
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/service/Bmv2DeviceListener.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/service/Bmv2DeviceListener.java
new file mode 100644
index 0000000..dbd6d65
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/service/Bmv2DeviceListener.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016-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.bmv2.api.service;
+
+import org.onosproject.bmv2.api.runtime.Bmv2Device;
+
+/**
+ * A listener of BMv2 device events.
+ */
+public interface Bmv2DeviceListener {
+
+    /**
+     * Handles a hello message.
+     *
+     * @param device        the BMv2 device that originated the message
+     * @param instanceId    the ID of the BMv2 process instance
+     * @param jsonConfigMd5 the MD5 sum of the JSON configuration currently running on the device
+     */
+    void handleHello(Bmv2Device device, int instanceId, String jsonConfigMd5);
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/service/Bmv2PacketListener.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/service/Bmv2PacketListener.java
new file mode 100644
index 0000000..19365c9
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/service/Bmv2PacketListener.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016-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.bmv2.api.service;
+
+import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.bmv2.api.runtime.Bmv2Device;
+
+/**
+ * A listener of BMv2 packet events.
+ */
+public interface Bmv2PacketListener {
+
+    /**
+     * Handles a packet-in message.
+     *
+     * @param device    the BMv2 device that originated the message
+     * @param inputPort the device port where the packet was received
+     * @param reason    a reason code
+     * @param tableId   the ID of table that originated this packet-in
+     * @param contextId the ID of the BMv2 context where the packet-in was originated
+     * @param packet    the packet raw data
+     */
+    void handlePacketIn(Bmv2Device device, int inputPort, long reason, int tableId, int contextId,
+                        ImmutableByteSequence packet);
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/service/Bmv2TableEntryService.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/service/Bmv2TableEntryService.java
new file mode 100644
index 0000000..5b55764
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/service/Bmv2TableEntryService.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2016-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.bmv2.api.service;
+
+
+import org.onosproject.bmv2.api.context.Bmv2FlowRuleTranslator;
+import org.onosproject.bmv2.api.runtime.Bmv2FlowRuleWrapper;
+import org.onosproject.bmv2.api.runtime.Bmv2ParsedTableEntry;
+import org.onosproject.bmv2.api.runtime.Bmv2TableEntryReference;
+import org.onosproject.net.DeviceId;
+
+import java.util.List;
+
+/**
+ * A service for managing BMv2 table entries.
+ */
+public interface Bmv2TableEntryService {
+
+    /**
+     * Returns a flow rule translator.
+     *
+     * @return a flow rule translator
+     */
+    Bmv2FlowRuleTranslator getFlowRuleTranslator();
+
+    /**
+     * Returns a list of table entries installed in the given device and table. The table entries returned are the
+     * result of a table dump parse.
+     *
+     * @param deviceId  a device id
+     * @param tableName a table name
+     * @return a list of parsed table entries
+     */
+    List<Bmv2ParsedTableEntry> getTableEntries(DeviceId deviceId, String tableName);
+
+    /**
+     * Binds the given ONOS flow rule with a BMv2 table entry reference.
+     *
+     * @param entryRef a table entry reference
+     * @param rule     a BMv2 flow rule wrapper
+     */
+    void bindEntryReference(Bmv2TableEntryReference entryRef, Bmv2FlowRuleWrapper rule);
+
+    /**
+     * Returns the ONOS flow rule associated with the given BMv2 table entry reference, or null if there's no such a
+     * mapping.
+     *
+     * @param entryRef a table entry reference
+     * @return a BMv2 flow rule wrapper
+     */
+    Bmv2FlowRuleWrapper lookupEntryReference(Bmv2TableEntryReference entryRef);
+
+    /**
+     * Removes any flow rule previously bound with a given BMv2 table entry reference.
+     *
+     * @param entryRef a table entry reference
+     */
+    void unbindEntryReference(Bmv2TableEntryReference entryRef);
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/service/package-info.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/service/package-info.java
new file mode 100644
index 0000000..07bb1a0
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/service/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016-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.
+ */
+
+/**
+ * BMv2 service API.
+ */
+package org.onosproject.bmv2.api.service;
\ No newline at end of file
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/utils/Bmv2TranslatorUtils.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/utils/Bmv2TranslatorUtils.java
new file mode 100644
index 0000000..8d8966f
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/utils/Bmv2TranslatorUtils.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2016-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.bmv2.api.utils;
+
+import org.onlab.util.HexString;
+import org.onlab.util.ImmutableByteSequence;
+
+import java.util.Arrays;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Collection of util methods to deal with flow rule translation.
+ */
+public final class Bmv2TranslatorUtils {
+
+    private Bmv2TranslatorUtils() {
+        // Ban constructor.
+    }
+
+    /**
+     * Returns the number of bytes necessary to contain the given bit-width.
+     *
+     * @param bitWidth an integer value
+     * @return an integer value
+     */
+    public static int roundToBytes(int bitWidth) {
+        return (int) Math.ceil((double) bitWidth / 8);
+    }
+
+    /**
+     * Trims or expands the given byte sequence so to fit a given bit-width.
+     *
+     * @param original a byte sequence
+     * @param bitWidth an integer value
+     * @return a new byte sequence
+     * @throws ByteSequenceFitException if the byte sequence cannot be fitted in the given bit-width
+     */
+    public static ImmutableByteSequence fitByteSequence(ImmutableByteSequence original, int bitWidth)
+            throws ByteSequenceFitException {
+
+        checkNotNull(original, "byte sequence cannot be null");
+        checkArgument(bitWidth > 0, "byte width must a non-zero positive integer");
+
+        int newByteWidth = roundToBytes(bitWidth);
+
+        if (original.size() == newByteWidth) {
+            // nothing to do
+            return original;
+        }
+
+        byte[] originalBytes = original.asArray();
+
+        if (newByteWidth > original.size()) {
+            // pad missing bytes with zeros
+            return ImmutableByteSequence.copyFrom(Arrays.copyOf(originalBytes, newByteWidth));
+        }
+
+        byte[] newBytes = new byte[newByteWidth];
+        // ImmutableByteSequence is always big-endian, hence check the array in reverse order
+        int diff = originalBytes.length - newByteWidth;
+        for (int i = originalBytes.length - 1; i > 0; i--) {
+            byte ob = originalBytes[i]; // original byte
+            byte nb; // new byte
+            if (i > diff) {
+                // no need to truncate, copy as is
+                nb = ob;
+            } else if (i == diff) {
+                // truncate this byte, check if we're loosing something
+                byte mask = (byte) ((1 >> ((bitWidth % 8) + 1)) - 1);
+                if ((ob & ~mask) != 0) {
+                    throw new ByteSequenceFitException(originalBytes, bitWidth);
+                } else {
+                    nb = (byte) (ob & mask);
+                }
+            } else {
+                // drop this byte, check if we're loosing something
+                if (originalBytes[i] != 0) {
+                    throw new ByteSequenceFitException(originalBytes, bitWidth);
+                } else {
+                    continue;
+                }
+            }
+            newBytes[i - diff] = nb;
+        }
+
+        return ImmutableByteSequence.copyFrom(newBytes);
+    }
+
+    /**
+     * A byte sequence fit exception.
+     */
+    public static class ByteSequenceFitException extends Exception {
+        public ByteSequenceFitException(byte[] bytes, int bitWidth) {
+            super("cannot fit " + HexString.toHexString(bytes) + " into a " + bitWidth + " bits value");
+        }
+    }
+}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/utils/package-info.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/utils/package-info.java
new file mode 100644
index 0000000..bdae039
--- /dev/null
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/utils/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016-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.
+ */
+
+/**
+ * BMv2 utils.
+ */
+package org.onosproject.bmv2.api.utils;
\ No newline at end of file
diff --git a/protocols/bmv2/api/src/test/java/org/onosproject/bmv2/api/context/Bmv2ConfigurationTest.java b/protocols/bmv2/api/src/test/java/org/onosproject/bmv2/api/context/Bmv2ConfigurationTest.java
new file mode 100644
index 0000000..bc1659d
--- /dev/null
+++ b/protocols/bmv2/api/src/test/java/org/onosproject/bmv2/api/context/Bmv2ConfigurationTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2016-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.bmv2.api.context;
+
+import com.eclipsesource.json.Json;
+import com.eclipsesource.json.JsonObject;
+import com.google.common.testing.EqualsTester;
+import org.hamcrest.collection.IsIterableContainingInOrder;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.bmv2.api.runtime.Bmv2MatchParam;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.core.IsEqual.equalTo;
+
+/**
+ * BMv2 JSON configuration parser test.
+ */
+public class Bmv2ConfigurationTest {
+
+    private JsonObject json;
+    private JsonObject json2;
+
+    @Before
+    public void setUp() throws Exception {
+        json = Json.parse(new BufferedReader(new InputStreamReader(
+                this.getClass().getResourceAsStream("/simple.json")))).asObject();
+        json2 = Json.parse(new BufferedReader(new InputStreamReader(
+                this.getClass().getResourceAsStream("/simple.json")))).asObject();
+    }
+
+    @Test
+    public void testParse() throws Exception {
+        Bmv2Configuration config = Bmv2DefaultConfiguration.parse(json);
+        Bmv2Configuration config2 = Bmv2DefaultConfiguration.parse(json2);
+
+        new EqualsTester()
+                .addEqualityGroup(config, config2)
+                .testEquals();
+
+        /* Check header types */
+        Bmv2HeaderTypeModel stdMetaT = config.headerType("standard_metadata_t");
+        Bmv2HeaderTypeModel ethernetT = config.headerType("ethernet_t");
+        Bmv2HeaderTypeModel intrinsicMetaT = config.headerType("intrinsic_metadata_t");
+
+        Bmv2HeaderTypeModel stdMetaT2 = config2.headerType("standard_metadata_t");
+        Bmv2HeaderTypeModel ethernetT2 = config2.headerType("ethernet_t");
+        Bmv2HeaderTypeModel intrinsicMetaT2 = config2.headerType("intrinsic_metadata_t");
+
+        new EqualsTester()
+                .addEqualityGroup(stdMetaT, stdMetaT2)
+                .addEqualityGroup(ethernetT, ethernetT2)
+                .addEqualityGroup(intrinsicMetaT, intrinsicMetaT2)
+                .testEquals();
+
+        // existence
+        assertThat("Json parsed value is null", stdMetaT, notNullValue());
+        assertThat("Json parsed value is null", ethernetT, notNullValue());
+        assertThat("Json parsed value is null", intrinsicMetaT, notNullValue());
+
+        // fields size
+        assertThat("Incorrect size for header type fields",
+                   stdMetaT.fields(), hasSize(8));
+        assertThat("Incorrect size for header type fields",
+                   ethernetT.fields(), hasSize(3));
+        assertThat("Incorrect size for header type fields",
+                   intrinsicMetaT.fields(), hasSize(4));
+
+        // check that fields are in order
+        assertThat("Incorrect order for header type fields",
+                   stdMetaT.fields(), IsIterableContainingInOrder.contains(
+                        stdMetaT.field("ingress_port"),
+                        stdMetaT.field("packet_length"),
+                        stdMetaT.field("egress_spec"),
+                        stdMetaT.field("egress_port"),
+                        stdMetaT.field("egress_instance"),
+                        stdMetaT.field("instance_type"),
+                        stdMetaT.field("clone_spec"),
+                        stdMetaT.field("_padding")));
+
+        /* Check actions */
+        Bmv2ActionModel floodAction = config.action("flood");
+        Bmv2ActionModel dropAction = config.action("_drop");
+        Bmv2ActionModel fwdAction = config.action("set_egress_port");
+
+        Bmv2ActionModel floodAction2 = config2.action("flood");
+        Bmv2ActionModel dropAction2 = config2.action("_drop");
+        Bmv2ActionModel fwdAction2 = config2.action("set_egress_port");
+
+        new EqualsTester()
+                .addEqualityGroup(floodAction, floodAction2)
+                .addEqualityGroup(dropAction, dropAction2)
+                .addEqualityGroup(fwdAction, fwdAction2)
+                .testEquals();
+
+        // existence
+        assertThat("Json parsed value is null", floodAction, notNullValue());
+        assertThat("Json parsed value is null", dropAction, notNullValue());
+        assertThat("Json parsed value is null", fwdAction, notNullValue());
+
+        // runtime data size
+        assertThat("Incorrect size for action runtime data",
+                   floodAction.runtimeDatas().size(), is(equalTo(0)));
+        assertThat("Incorrect size for action runtime data",
+                   dropAction.runtimeDatas().size(), is(equalTo(0)));
+        assertThat("Incorrect size for action runtime data",
+                   fwdAction.runtimeDatas().size(), is(equalTo(1)));
+
+        // runtime data existence and parsing
+        assertThat("Parsed Json value is null",
+                   fwdAction.runtimeData("port"), notNullValue());
+        assertThat("Incorrect value for action runtime data bitwidth",
+                   fwdAction.runtimeData("port").bitWidth(), is(equalTo(9)));
+
+        /* Check tables */
+        Bmv2TableModel table0 = config.table(0);
+        Bmv2TableModel table02 = config2.table(0);
+
+        new EqualsTester()
+                .addEqualityGroup(table0, table02)
+                .testEquals();
+
+        // existence
+        assertThat("Parsed Json value is null", table0, notNullValue());
+
+        // id and name correspondence
+        assertThat("Incorrect value for table name",
+                   table0.name(), is(equalTo("table0")));
+
+        // keys size
+        assertThat("Incorrect size for table keys",
+                   table0.keys().size(), is(equalTo(4)));
+
+        // key match type
+        assertThat("Incorrect value for table key match type",
+                   table0.keys().get(0).matchType(), is(equalTo(Bmv2MatchParam.Type.TERNARY)));
+
+        // header type
+        assertThat("Incorrect value for table key header type",
+                   table0.keys().get(0).field().header().type(), is(equalTo(stdMetaT)));
+    }
+}
\ No newline at end of file
diff --git a/protocols/bmv2/api/src/test/resources/simple.json b/protocols/bmv2/api/src/test/resources/simple.json
new file mode 100644
index 0000000..9be7b89
--- /dev/null
+++ b/protocols/bmv2/api/src/test/resources/simple.json
@@ -0,0 +1,397 @@
+{
+    "header_types": [
+        {
+            "name": "standard_metadata_t",
+            "id": 0,
+            "fields": [
+                [
+                    "ingress_port",
+                    9
+                ],
+                [
+                    "packet_length",
+                    32
+                ],
+                [
+                    "egress_spec",
+                    9
+                ],
+                [
+                    "egress_port",
+                    9
+                ],
+                [
+                    "egress_instance",
+                    32
+                ],
+                [
+                    "instance_type",
+                    32
+                ],
+                [
+                    "clone_spec",
+                    32
+                ],
+                [
+                    "_padding",
+                    5
+                ]
+            ],
+            "length_exp": null,
+            "max_length": null
+        },
+        {
+            "name": "ethernet_t",
+            "id": 1,
+            "fields": [
+                [
+                    "dstAddr",
+                    48
+                ],
+                [
+                    "srcAddr",
+                    48
+                ],
+                [
+                    "etherType",
+                    16
+                ]
+            ],
+            "length_exp": null,
+            "max_length": null
+        },
+        {
+            "name": "intrinsic_metadata_t",
+            "id": 2,
+            "fields": [
+                [
+                    "ingress_global_timestamp",
+                    32
+                ],
+                [
+                    "lf_field_list",
+                    32
+                ],
+                [
+                    "mcast_grp",
+                    16
+                ],
+                [
+                    "egress_rid",
+                    16
+                ]
+            ],
+            "length_exp": null,
+            "max_length": null
+        }
+    ],
+    "headers": [
+        {
+            "name": "standard_metadata",
+            "id": 0,
+            "header_type": "standard_metadata_t",
+            "metadata": true
+        },
+        {
+            "name": "ethernet",
+            "id": 1,
+            "header_type": "ethernet_t",
+            "metadata": false
+        },
+        {
+            "name": "intrinsic_metadata",
+            "id": 2,
+            "header_type": "intrinsic_metadata_t",
+            "metadata": true
+        }
+    ],
+    "header_stacks": [],
+    "parsers": [
+        {
+            "name": "parser",
+            "id": 0,
+            "init_state": "start",
+            "parse_states": [
+                {
+                    "name": "start",
+                    "id": 0,
+                    "parser_ops": [],
+                    "transition_key": [],
+                    "transitions": [
+                        {
+                            "value": "default",
+                            "mask": null,
+                            "next_state": "parse_ethernet"
+                        }
+                    ]
+                },
+                {
+                    "name": "parse_ethernet",
+                    "id": 1,
+                    "parser_ops": [
+                        {
+                            "op": "extract",
+                            "parameters": [
+                                {
+                                    "type": "regular",
+                                    "value": "ethernet"
+                                }
+                            ]
+                        }
+                    ],
+                    "transition_key": [],
+                    "transitions": [
+                        {
+                            "value": "default",
+                            "mask": null,
+                            "next_state": null
+                        }
+                    ]
+                }
+            ]
+        }
+    ],
+    "deparsers": [
+        {
+            "name": "deparser",
+            "id": 0,
+            "order": [
+                "ethernet"
+            ]
+        }
+    ],
+    "meter_arrays": [],
+    "actions": [
+        {
+            "name": "set_egress_port",
+            "id": 0,
+            "runtime_data": [
+                {
+                    "name": "port",
+                    "bitwidth": 9
+                }
+            ],
+            "primitives": [
+                {
+                    "op": "modify_field",
+                    "parameters": [
+                        {
+                            "type": "field",
+                            "value": [
+                                "standard_metadata",
+                                "egress_spec"
+                            ]
+                        },
+                        {
+                            "type": "runtime_data",
+                            "value": 0
+                        }
+                    ]
+                }
+            ]
+        },
+        {
+            "name": "_drop",
+            "id": 1,
+            "runtime_data": [],
+            "primitives": [
+                {
+                    "op": "modify_field",
+                    "parameters": [
+                        {
+                            "type": "field",
+                            "value": [
+                                "standard_metadata",
+                                "egress_spec"
+                            ]
+                        },
+                        {
+                            "type": "hexstr",
+                            "value": "0x1ff"
+                        }
+                    ]
+                }
+            ]
+        },
+        {
+            "name": "flood",
+            "id": 2,
+            "runtime_data": [],
+            "primitives": [
+                {
+                    "op": "modify_field",
+                    "parameters": [
+                        {
+                            "type": "field",
+                            "value": [
+                                "intrinsic_metadata",
+                                "mcast_grp"
+                            ]
+                        },
+                        {
+                            "type": "field",
+                            "value": [
+                                "standard_metadata",
+                                "ingress_port"
+                            ]
+                        }
+                    ]
+                }
+            ]
+        },
+        {
+            "name": "send_to_cpu",
+            "id": 3,
+            "runtime_data": [],
+            "primitives": [
+                {
+                    "op": "modify_field",
+                    "parameters": [
+                        {
+                            "type": "field",
+                            "value": [
+                                "standard_metadata",
+                                "egress_spec"
+                            ]
+                        },
+                        {
+                            "type": "hexstr",
+                            "value": "0xff"
+                        }
+                    ]
+                }
+            ]
+        }
+    ],
+    "pipelines": [
+        {
+            "name": "ingress",
+            "id": 0,
+            "init_table": "table0",
+            "tables": [
+                {
+                    "name": "table0",
+                    "id": 0,
+                    "match_type": "ternary",
+                    "type": "simple",
+                    "max_size": 16384,
+                    "with_counters": false,
+                    "direct_meters": null,
+                    "support_timeout": false,
+                    "key": [
+                        {
+                            "match_type": "ternary",
+                            "target": [
+                                "standard_metadata",
+                                "ingress_port"
+                            ],
+                            "mask": null
+                        },
+                        {
+                            "match_type": "ternary",
+                            "target": [
+                                "ethernet",
+                                "dstAddr"
+                            ],
+                            "mask": null
+                        },
+                        {
+                            "match_type": "ternary",
+                            "target": [
+                                "ethernet",
+                                "srcAddr"
+                            ],
+                            "mask": null
+                        },
+                        {
+                            "match_type": "ternary",
+                            "target": [
+                                "ethernet",
+                                "etherType"
+                            ],
+                            "mask": null
+                        }
+                    ],
+                    "actions": [
+                        "set_egress_port",
+                        "flood",
+                        "send_to_cpu",
+                        "_drop"
+                    ],
+                    "next_tables": {
+                        "set_egress_port": null,
+                        "flood": null,
+                        "send_to_cpu": null,
+                        "_drop": null
+                    },
+                    "default_action": null,
+                    "base_default_next": null
+                }
+            ],
+            "conditionals": []
+        },
+        {
+            "name": "egress",
+            "id": 1,
+            "init_table": null,
+            "tables": [],
+            "conditionals": []
+        }
+    ],
+    "calculations": [],
+    "checksums": [],
+    "learn_lists": [],
+    "field_lists": [],
+    "counter_arrays": [],
+    "register_arrays": [],
+    "force_arith": [
+        [
+            "standard_metadata",
+            "ingress_port"
+        ],
+        [
+            "standard_metadata",
+            "packet_length"
+        ],
+        [
+            "standard_metadata",
+            "egress_spec"
+        ],
+        [
+            "standard_metadata",
+            "egress_port"
+        ],
+        [
+            "standard_metadata",
+            "egress_instance"
+        ],
+        [
+            "standard_metadata",
+            "instance_type"
+        ],
+        [
+            "standard_metadata",
+            "clone_spec"
+        ],
+        [
+            "standard_metadata",
+            "_padding"
+        ],
+        [
+            "intrinsic_metadata",
+            "ingress_global_timestamp"
+        ],
+        [
+            "intrinsic_metadata",
+            "lf_field_list"
+        ],
+        [
+            "intrinsic_metadata",
+            "mcast_grp"
+        ],
+        [
+            "intrinsic_metadata",
+            "egress_rid"
+        ]
+    ]
+}
\ No newline at end of file