[OpenTAM_DPIS] DPI Statistics Manager & dpis CLI
 - Fixed Thomas Vachuska's comment
   .Removed DpiStatisticsJson.java and related codes for avoiding confusion within package because this does not need anymore.
   .Simplified execute() code and fixed comments in DpiListCommand
   .Added javadocs for public methods and fixed typos
 - Rebased from master 1.8.0-SNAPSHOT, 2016.10.14

 - Upgraded the function and performance.
   .changed List<DpiStatistics> to SortedMap<DpiStatistics>
   .added DpiStatisticsService interfaces to get DpiStatistics by receivedTime
   .added more options of dpis CLI: displays indivisual category by topn
   .and some bug fix

 - Fix javadocs and ONOS style method name.
 - Fix javadocs error.

Change-Id: I07a058e04f63bd9c547a5d605975b214eec0ce1f
diff --git a/cli/src/main/java/org/onosproject/cli/net/DpisListCommand.java b/cli/src/main/java/org/onosproject/cli/net/DpisListCommand.java
new file mode 100644
index 0000000..85c2dda
--- /dev/null
+++ b/cli/src/main/java/org/onosproject/cli/net/DpisListCommand.java
@@ -0,0 +1,368 @@
+/*
+ * 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.cli.net;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.incubator.net.dpi.DpiStatInfo;
+import org.onosproject.incubator.net.dpi.DpiStatistics;
+import org.onosproject.incubator.net.dpi.DpiStatisticsManagerService;
+import org.onosproject.incubator.net.dpi.FlowStatInfo;
+import org.onosproject.incubator.net.dpi.ProtocolStatInfo;
+import org.onosproject.incubator.net.dpi.TrafficStatInfo;
+
+import java.util.List;
+
+import static java.lang.Thread.sleep;
+
+/**
+ * Fetches DPI statistics list.
+ */
+@Command(scope = "onos", name = "dpis",
+        description = "Fetches the DPI result entries that is received from DPI engine server")
+public class DpisListCommand extends AbstractShellCommand {
+    @Argument(index = 0, name = "receivedTime", description = "received time: format 'yyyy-MM-dd HH:mm:ss', "
+            + "ex:'2016-08-30 10:31:20', default = null(latest time)",
+            required = false, multiValued = false)
+    String receivedTime = null; // default is latest time
+
+    @Option(name = "-l", aliases = "--latest",
+            description = "Show the latest dpi stats result entry",
+            required = false, multiValued = false)
+    boolean latest = true; // default
+
+    @Option(name = "-d", aliases = "--detectedProtocols",
+            description = "Show the detected protocols only for each statistic entry",
+            required = false, multiValued = false)
+    boolean dProtocols = false; // default
+
+    @Option(name = "-k", aliases = "--knownFlows",
+            description = "Show the known flows only for each statistic entry",
+            required = false, multiValued = false)
+    boolean kFlows = false; // default
+
+    @Option(name = "-u", aliases = "--unknownFlows",
+            description = "Show the unknown flows only for each statistic entry",
+            required = false, multiValued = false)
+    boolean uFlows = false; // default
+
+    @Option(name = "-a", aliases = "--all",
+            description = "Show the all statistics information in detail for each statistic entry",
+            required = false, multiValued = false)
+    boolean all = false; // default is traffic statistics only display
+
+    @Option(name = "-p", aliases = "--permanent",
+            description = "Show the latest dpi stats result entry permanently at 5 second, use Ctrl+C for quitting",
+            required = false, multiValued = false)
+    boolean permanent = false;
+
+    @Option(name = "-n", aliases = "--lastn",
+            description = "Show the last N Dpi stats result entries, MAX_REQUEST_ENTRY = 100",
+            required = false, multiValued = false)
+    String lastn = null;
+
+    @Option(name = "-P", aliases = "--topnProtocols",
+            description = "Show the topn detected Protocol result entries, MAX_PROTOCOLS_TOPN = 100",
+            required = false, multiValued = false)
+    String topnProtocols = null;
+
+    @Option(name = "-F", aliases = "--topnFlows",
+            description = "Show the topn known and unknown Flows result entries, MAX_FLOWS_TOPN = 100",
+            required = false, multiValued = false)
+    String topnFlows = null;
+
+    private static final int DEFAULT_LASTN = 100;
+    private static final int DEFAULT_TOPNP = -1;
+    private static final int DEFAULT_TOPNF = -1;
+    private static final String NO_DPI_ENTRY_ERROR_MSG = "No DPI statistic entry,"
+                                                        + " please check remote DPI engine is running";
+    private static final String RECEIVED_TIME_ERROR_MSG = NO_DPI_ENTRY_ERROR_MSG + "\n"
+                    + " or correct receivedTime format: 'yyyy-MM-dd HH:mm:ss', ex:'2016-08-30 10:31:20'";
+
+    @Override
+    protected void execute() {
+        DpiStatisticsManagerService dsms = get(DpiStatisticsManagerService.class);
+
+        DpiStatistics ds;
+
+        int topnP = DEFAULT_TOPNP;
+        int topnF = DEFAULT_TOPNF;
+
+        if (topnProtocols != null) {
+            topnP = parseIntWithDefault(topnProtocols, DEFAULT_TOPNP);
+            if (topnP <= 0) {
+                print("Invalid detected protocol topn number: 0 < valid number <= 100");
+                return;
+            }
+        }
+
+        if (topnFlows != null) {
+            topnF = parseIntWithDefault(topnFlows, DEFAULT_TOPNF);
+            if (topnF <= 0) {
+                print("Invalid known or unknown flows topn number: 0 < valid number <= 100");
+                return;
+            }
+        }
+
+        boolean isTopn = (topnP > 0 || topnF > 0);
+
+        if (all) {
+            dProtocols = true;
+            kFlows = true;
+            uFlows = true;
+        }
+
+        if (receivedTime != null) {
+            if (isTopn) {
+                ds = dsms.getDpiStatistics(receivedTime, topnP, topnF);
+            } else {
+                ds = dsms.getDpiStatistics(receivedTime);
+            }
+            if (ds == null) {
+                print(RECEIVED_TIME_ERROR_MSG);
+                return;
+            }
+
+            printDpiStatistics(0, ds);
+        } else if (lastn != null) {
+            int lastN = parseIntWithDefault(lastn, DEFAULT_LASTN);
+
+            List<DpiStatistics> dsList;
+            if (isTopn) {
+                dsList = dsms.getDpiStatistics(lastN, topnP, topnF);
+
+            } else {
+                dsList = dsms.getDpiStatistics(lastN);
+            }
+
+            printDpiStatisticsList(dsList);
+        } else if (permanent) {
+            int i = 0;
+            while (true) {
+                try {
+                    if (isTopn) {
+                        ds = dsms.getDpiStatisticsLatest(topnP, topnF);
+                    } else {
+                        ds = dsms.getDpiStatisticsLatest();
+                    }
+                    if (ds == null) {
+                        print(NO_DPI_ENTRY_ERROR_MSG);
+                        return;
+                    }
+
+                    printDpiStatistics(i++, ds);
+                    sleep(5000);
+                } catch (Exception e) {
+                    return;
+                }
+            }
+        } else { // latest == true
+            if (isTopn) {
+                ds = dsms.getDpiStatisticsLatest(topnP, topnF);
+            } else {
+                ds = dsms.getDpiStatisticsLatest();
+            }
+            if (ds == null) {
+                print(NO_DPI_ENTRY_ERROR_MSG);
+                return;
+            }
+
+            printDpiStatistics(0, ds);
+        }
+    }
+
+
+    /**
+     * Parse unsigned integer from input lastn string.
+     *
+     * @param lastN string lastn number
+     * @param defaultN integer default lastn number = 100
+     * @return integer lastN number, defaultN if input format is not a number
+     */
+    private int parseIntWithDefault(String lastN, int defaultN) {
+        try {
+            lastN = lastN.trim();
+            return Integer.parseUnsignedInt(lastN);
+        } catch (NumberFormatException e) {
+            return defaultN;
+        }
+    }
+
+    private void printDpiStatistics(int number, DpiStatistics ds) {
+        if (outputJson()) {
+            printDpiStatisticsJson(number, ds);
+        } else {
+            printDpiStatisticsClass(number, ds);
+        }
+    }
+
+    private void printDpiStatisticsJson(int number, DpiStatistics ds) {
+        String index = number < 0 ? String.format("  -  ") : String.format("%5d", number);
+        if (ds.receivedTime().equals("")) {
+            print("ReceivedTime is null, No valid DPI Statistics!");
+            return;
+        }
+
+        print("<--- (%s) DPI Statistics Time [%s] --->", index, ds.receivedTime());
+        print("      %s", ds.toString());
+        print("<--------------------------------------------------------->");
+    }
+
+    private void printDpiStatisticsClass(int number, DpiStatistics ds) {
+        String printLine = "";
+        String index = number < 0 ? String.format("  -  ") : String.format("%5d", number);
+
+        DpiStatInfo dsi = ds.dpiStatInfo();
+        if (dsi == null) {
+            return;
+        }
+
+        if (ds.receivedTime().equals("")) {
+            print("ReceivedTime is null, No valid DPI Statistics!");
+            return;
+        }
+
+        print("<--- (%s) DPI Statistics Time [%s] --->", index, ds.receivedTime());
+
+        print("Traffic Statistics:");
+        TrafficStatInfo tsi = dsi.trafficStatistics();
+
+        printLine = String.format("        %-30s %-30s", "ethernet.bytes:" + ":", tsi.ethernetBytes());
+        print("%s", printLine);
+        printLine = String.format("        %-30s %-30s", "discarded.bytes" + ":", tsi.discardedBytes());
+        print("%s", printLine);
+        printLine = String.format("        %-30s %-30s", "ip.packets" + ":", tsi.ipPackets());
+        print("%s", printLine);
+        printLine = String.format("        %-30s %-30s", "total.packets" + ":", tsi.totalPackets());
+        print("%s", printLine);
+        printLine = String.format("        %-30s %-30s", "ip.bytes" + ":", tsi.ipBytes());
+        print("%s", printLine);
+        printLine = String.format("        %-30s %-30s", "avg.pkt.size" + ":", tsi.avgPktSize());
+        print("%s", printLine);
+        printLine = String.format("        %-30s %-30s", "unique.flows" + ":", tsi.uniqueFlows());
+        print("%s", printLine);
+        printLine = String.format("        %-30s %-30s", "tcp.packets" + ":", tsi.tcpPackets());
+        print("%s", printLine);
+        printLine = String.format("        %-30s %-30s", "udp.packets" + ":", tsi.tcpPackets());
+        print("%s", printLine);
+        printLine = String.format("        %-30s %-30s", "dpi.throughput.pps" + ":",
+                                  tsi.dpiThroughputPps() + " pps");
+        print("%s", printLine);
+        printLine = String.format("        %-30s %-30s", "dpi.throughput.bps" + ":",
+                                  tsi.dpiThroughputBps() + " bps");
+        print("%s", printLine);
+        printLine = String.format("        %-30s %-30s", "traffic.throughput.pps" + ":",
+                                  tsi.trafficThroughputPps() + " pps");
+        print("%s", printLine);
+        printLine = String.format("        %-30s %-30s", "traffic.throughput.bps" + ":",
+                                  tsi.trafficThroughputBps() + " bps");
+        print("%s", printLine);
+        printLine = String.format("        %-30s %-30s", "traffic.duration.sec" + ":",
+                                  tsi.trafficDurationSec() + " sec");
+        print("%s", printLine);
+        printLine = String.format("        %-30s %-30s", "guessed.flow.protos" + ":", tsi.guessedFlowProtos());
+        print("%s", printLine);
+
+        if (dProtocols || topnProtocols != null) {
+            print("");
+            print("Detected Protocols:");
+            List<ProtocolStatInfo> psiList = dsi.detectedProtos();
+            if (psiList != null) {
+                psiList.forEach(psi -> print(makeProtocolString(psi)));
+            }
+        }
+
+        List<FlowStatInfo> fsiList;
+        if (kFlows || topnFlows != null) {
+            print("");
+            print("Known Flows:");
+            fsiList = dsi.knownFlows();
+            if (fsiList != null) {
+                for (int i = 0; i < fsiList.size(); i++) {
+                    print(makeFlowString(fsiList.get(i), i));
+                }
+            }
+        }
+
+        if (uFlows || topnFlows != null) {
+            print("");
+            print("Unknown Flows:");
+            fsiList = dsi.unknownFlows();
+            if (fsiList != null) {
+                for (int i = 0; i < fsiList.size(); i++) {
+                    print(makeFlowString(fsiList.get(i), i));
+                }
+            }
+        }
+
+        print("<--------------------------------------------------------->");
+    }
+
+    private void printDpiStatisticsList(List<DpiStatistics> dsList) {
+        if (outputJson()) {
+            printDpiStatisticsListJson(dsList);
+        } else {
+            printDpiStatisticsListClass(dsList);
+        }
+    }
+
+    private void printDpiStatisticsListJson(List<DpiStatistics> dsList) {
+        for (int i = 0; i < dsList.size(); i++) {
+            printDpiStatisticsJson(i, dsList.get(i));
+        }
+    }
+
+    private void printDpiStatisticsListClass(List<DpiStatistics> dsList) {
+        for (int i = 0; i < dsList.size(); i++) {
+            printDpiStatisticsClass(i, dsList.get(i));
+        }
+    }
+
+    private String makeProtocolString(ProtocolStatInfo psi) {
+        StringBuffer sb = new StringBuffer("        ");
+
+        sb.append(String.format("%-20s", psi.name()));
+        sb.append(String.format(" %s: %-20s", "packets", psi.packets()));
+        sb.append(String.format(" %s: %-20s", "bytes", psi.bytes()));
+        sb.append(String.format(" %s: %-20s", "flows", psi.flows()));
+
+        return sb.toString();
+    }
+
+    private String makeFlowString(FlowStatInfo fsi, int index) {
+        StringBuffer sb = new StringBuffer("        ");
+
+        sb.append(String.format("%-8d ", index));
+        sb.append(String.format("%s ", fsi.protocol()));
+        sb.append(String.format("%s", fsi.hostAName()));
+        sb.append(String.format(":%s", fsi.hostAPort()));
+        sb.append(String.format(" <-> %s", fsi.hostBName()));
+        sb.append(String.format(":%s", fsi.hostBPort()));
+        sb.append(String.format(" [proto: %d", fsi.detectedProtocol()));
+        sb.append(String.format("/%s]", fsi.detectedProtocolName()));
+        sb.append(String.format(" [%s pkts/", fsi.packets()));
+        sb.append(String.format("%s bytes]", fsi.bytes()));
+        String serverHostName = fsi.hostServerName();
+        if (serverHostName != null && !serverHostName.equals("")) {
+            sb.append(String.format("[Host: %s]", serverHostName));
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 69c52fe..f3c660f 100644
--- a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -714,6 +714,9 @@
         <command>
             <action class="org.onosproject.cli.net.vnet.VirtualNetworkIntentRemoveCommand"/>
         </command>
+        <command>
+            <action class="org.onosproject.cli.net.DpisListCommand"/>
+        </command>
     </command-bundle>
 
     <bean id="reviewAppNameCompleter" class="org.onosproject.cli.security.ReviewApplicationNameCompleter"/>
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/CodecManager.java b/core/common/src/main/java/org/onosproject/codec/impl/CodecManager.java
index 090727f..3f305b6 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/CodecManager.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/CodecManager.java
@@ -28,6 +28,16 @@
 import org.onosproject.codec.JsonCodec;
 import org.onosproject.core.Application;
 import org.onosproject.core.ApplicationId;
+import org.onosproject.incubator.net.dpi.DpiStatInfo;
+import org.onosproject.incubator.net.dpi.DpiStatInfoCodec;
+import org.onosproject.incubator.net.dpi.DpiStatistics;
+import org.onosproject.incubator.net.dpi.DpiStatisticsCodec;
+import org.onosproject.incubator.net.dpi.FlowStatInfo;
+import org.onosproject.incubator.net.dpi.FlowStatInfoCodec;
+import org.onosproject.incubator.net.dpi.ProtocolStatInfo;
+import org.onosproject.incubator.net.dpi.ProtocolStatInfoCodec;
+import org.onosproject.incubator.net.dpi.TrafficStatInfo;
+import org.onosproject.incubator.net.dpi.TrafficStatInfoCodec;
 import org.onosproject.incubator.net.virtual.TenantId;
 import org.onosproject.incubator.net.virtual.VirtualDevice;
 import org.onosproject.incubator.net.virtual.VirtualHost;
@@ -150,6 +160,11 @@
         registerCodec(MastershipTerm.class, new MastershipTermCodec());
         registerCodec(MastershipRole.class, new MastershipRoleCodec());
         registerCodec(RoleInfo.class, new RoleInfoCodec());
+        registerCodec(DpiStatistics.class, new DpiStatisticsCodec());
+        registerCodec(DpiStatInfo.class, new DpiStatInfoCodec());
+        registerCodec(TrafficStatInfo.class, new TrafficStatInfoCodec());
+        registerCodec(ProtocolStatInfo.class, new ProtocolStatInfoCodec());
+        registerCodec(FlowStatInfo.class, new FlowStatInfoCodec());
         log.info("Started");
     }
 
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/DpiStatInfo.java b/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/DpiStatInfo.java
new file mode 100644
index 0000000..6d99c0c
--- /dev/null
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/DpiStatInfo.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.incubator.net.dpi;
+
+import java.util.List;
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * DPI statistic information.
+ */
+public class DpiStatInfo {
+    TrafficStatInfo trafficStatistics;
+    List<ProtocolStatInfo> detectedProtos;
+    List<FlowStatInfo> knownFlows;
+    List<FlowStatInfo> unknownFlows;
+
+    /**
+     * Constructor for default DpiStatInfo class.
+     */
+    public DpiStatInfo() {
+        this.trafficStatistics = null;
+        this.detectedProtos = null;
+        this.knownFlows = null;
+        this.unknownFlows = null;
+    }
+
+    /**
+     * Constructor for DpiStatistics class specified with trafficStatInfo.
+     *
+     * @param trafficStatistics traffic statistic information
+     */
+    public DpiStatInfo(TrafficStatInfo trafficStatistics) {
+        this.trafficStatistics = trafficStatistics;
+        this.detectedProtos = null;
+        this.knownFlows = null;
+        this.unknownFlows = null;
+    }
+
+    /**
+     * Constructor for DpiStatistics class specified with trafficStatInfo and detectedProtos.
+     *
+     * @param trafficStatistics traffic statistic information
+     * @param detectedProtos detected protocols statistic information
+     */
+    public DpiStatInfo(TrafficStatInfo trafficStatistics,
+                       List<ProtocolStatInfo> detectedProtos) {
+        this.trafficStatistics = trafficStatistics;
+        this.detectedProtos = detectedProtos;
+        this.knownFlows = null;
+        this.unknownFlows = null;
+    }
+
+    /**
+     * Constructor for DpiStatistics class specified with trafficStatInfo, detectedProtos and knownFlows.
+     *
+     * @param trafficStatistics traffic statistic information
+     * @param detectedProtos detected protocols statistic information
+     * @param knownFlows known flows
+     */
+    public DpiStatInfo(TrafficStatInfo trafficStatistics,
+                       List<ProtocolStatInfo> detectedProtos,
+                       List<FlowStatInfo> knownFlows) {
+        this.trafficStatistics = trafficStatistics;
+        this.detectedProtos = detectedProtos;
+        this.knownFlows = knownFlows;
+        this.unknownFlows = null;
+    }
+
+    /**
+     * Constructor for DpiStatistics class specified with trafficStatInfo, detectedProtos, knownFlows and unknownFlows.
+     *
+     * @param trafficStatistics traffic statistic information
+     * @param detectedProtos detected protocols statistic information
+     * @param knownFlows known flows
+     * @param unknownFlows unknown flows
+     */
+    public DpiStatInfo(TrafficStatInfo trafficStatistics,
+                       List<ProtocolStatInfo> detectedProtos,
+                       List<FlowStatInfo> knownFlows,
+                       List<FlowStatInfo> unknownFlows) {
+        this.trafficStatistics = trafficStatistics;
+        this.detectedProtos = detectedProtos;
+        this.knownFlows = knownFlows;
+        this.unknownFlows = unknownFlows;
+    }
+
+    /**
+     * Returns DPI traffic statistic information.
+     *
+     * @return trafficStatistics
+     */
+    public TrafficStatInfo trafficStatistics() {
+        return trafficStatistics;
+    }
+
+    /**
+     * Returns DPI detected protocols statistic information.
+     *
+     * @return detectedProtos
+     */
+    public List<ProtocolStatInfo> detectedProtos() {
+        return detectedProtos;
+    }
+
+    /**
+     * Returns DPI known flows.
+     *
+     * @return knownFlows
+     */
+    public List<FlowStatInfo> knownFlows() {
+        return knownFlows;
+    }
+
+    /**
+     * Returns DPI unknown flows.
+     *
+     * @return unknownFlows
+     */
+    public List<FlowStatInfo> unknownFlows() {
+        return unknownFlows;
+    }
+
+    /**
+     * Sets the traffic statistic information.
+     */
+    public void setTrafficStatistics(TrafficStatInfo trafficStatistics) {
+        this.trafficStatistics = trafficStatistics;
+    }
+
+    /**
+     * Sets the detected protocols statistic information.
+     */
+    public void setDetectedProtos(List<ProtocolStatInfo> detectedProtos) {
+        this.detectedProtos = detectedProtos;
+    }
+
+    /**
+     * Sets the known flows information.
+     */
+    public void setKnownFlows(List<FlowStatInfo> knownFlows) {
+        this.knownFlows = knownFlows;
+    }
+
+    /**
+     * Sets the unknown flows information.
+     */
+    public void setUnknownFlows(List<FlowStatInfo> unknownFlows) {
+        this.unknownFlows = unknownFlows;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("trafficStatistics", trafficStatistics)
+                .add("detectedProtos", detectedProtos)
+                .add("knownFlows", knownFlows)
+                .add("unknownFlows", unknownFlows)
+                .toString();
+    }
+}
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/DpiStatInfoCodec.java b/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/DpiStatInfoCodec.java
new file mode 100644
index 0000000..d7d3473
--- /dev/null
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/DpiStatInfoCodec.java
@@ -0,0 +1,151 @@
+/*
+ * 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.incubator.net.dpi;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.IntStream;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of encoder for DpiStatInfo codec.
+ */
+public final class DpiStatInfoCodec extends JsonCodec<DpiStatInfo> {
+
+    private static final String TRAFFIC_STATISTICS = "trafficStatistics";
+    private static final String DETECTED_PROTOS = "detectedProtos";
+    private static final String KNOWN_FLOWS = "knownFlows";
+    private static final String UNKNOWN_FLOWS = "unknownFlows";
+
+    private final Logger log = getLogger(getClass());
+
+    @Override
+    public ObjectNode encode(DpiStatInfo dsi, CodecContext context) {
+        checkNotNull(dsi, "DpiStatInfo cannot be null");
+
+        final ObjectNode result = context.mapper().createObjectNode();
+
+        final JsonCodec<TrafficStatInfo> trafficStatInfoCodec =
+                context.codec(TrafficStatInfo.class);
+
+
+        final TrafficStatInfo tsi = dsi.trafficStatistics();
+        if (tsi != null) {
+            final ObjectNode jsonTrafficStatistics = trafficStatInfoCodec.encode(tsi, context);
+            result.set(TRAFFIC_STATISTICS, jsonTrafficStatistics);
+        }
+
+
+        final List<ProtocolStatInfo> psi = dsi.detectedProtos();
+        if (psi != null) {
+            final ArrayNode jsonDetectedProtos = result.putArray(DETECTED_PROTOS);
+            final JsonCodec<ProtocolStatInfo> protocolStatInfoCodec =
+                    context.codec(ProtocolStatInfo.class);
+
+            for (final ProtocolStatInfo protocolStatInfo : psi) {
+                jsonDetectedProtos.add(protocolStatInfoCodec.encode(protocolStatInfo, context));
+            }
+        }
+
+        List<FlowStatInfo> fsi = dsi.knownFlows();
+        if (fsi != null) {
+            final ArrayNode jsonKnownFlows = result.putArray(KNOWN_FLOWS);
+            final JsonCodec<FlowStatInfo> flowStatInfoCodec =
+                    context.codec(FlowStatInfo.class);
+
+            for (final FlowStatInfo flowStatInfo : fsi) {
+                jsonKnownFlows.add(flowStatInfoCodec.encode(flowStatInfo, context));
+            }
+        }
+
+        fsi = dsi.unknownFlows();
+        if (fsi != null) {
+            final ArrayNode jsonUnknownFlows = result.putArray(UNKNOWN_FLOWS);
+            final JsonCodec<FlowStatInfo> flowStatInfoCodec =
+                    context.codec(FlowStatInfo.class);
+
+            for (final FlowStatInfo flowStatInfo : fsi) {
+                jsonUnknownFlows.add(flowStatInfoCodec.encode(flowStatInfo, context));
+            }
+        }
+
+        return result;
+    }
+
+    @Override
+    public DpiStatInfo decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        log.debug("trafficStatistics={}, full json={} ", json.get("trafficStatistics"), json);
+        TrafficStatInfo trafficStatInfo = null;
+        ObjectNode tsJson = get(json, TRAFFIC_STATISTICS);
+        if (tsJson != null) {
+            JsonCodec<TrafficStatInfo> trafficStatInfoJsonCodec =
+                    context.codec(TrafficStatInfo.class);
+            trafficStatInfo = trafficStatInfoJsonCodec.decode(tsJson, context);
+        }
+
+        final JsonCodec<ProtocolStatInfo> protocolStatInfoCodec =
+                context.codec(ProtocolStatInfo.class);
+
+        List<ProtocolStatInfo> detectedProtos = new ArrayList<>();
+        JsonNode dpJson = json.get(DETECTED_PROTOS);
+        if (dpJson != null) {
+            IntStream.range(0, dpJson.size())
+                    .forEach(i -> detectedProtos.add(
+                            protocolStatInfoCodec.decode(get(dpJson, i),
+                                                     context)));
+        }
+
+        final JsonCodec<FlowStatInfo> flowStatInfoCodec =
+                context.codec(FlowStatInfo.class);
+
+        List<FlowStatInfo> knownFlows = new ArrayList<>();
+        JsonNode kfJson = json.get(KNOWN_FLOWS);
+        if (kfJson != null) {
+            IntStream.range(0, kfJson.size())
+                    .forEach(i -> knownFlows.add(
+                            flowStatInfoCodec.decode(get(kfJson, i),
+                                                         context)));
+        }
+
+        List<FlowStatInfo> unknownFlows = new ArrayList<>();
+        JsonNode ufJson = json.get(UNKNOWN_FLOWS);
+        if (ufJson != null) {
+            IntStream.range(0, ufJson.size())
+                    .forEach(i -> unknownFlows.add(
+                            flowStatInfoCodec.decode(get(ufJson, i),
+                                                     context)));
+        }
+
+        return new DpiStatInfo(trafficStatInfo,
+                               detectedProtos,
+                               knownFlows,
+                               unknownFlows);
+    }
+}
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/DpiStatistics.java b/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/DpiStatistics.java
new file mode 100644
index 0000000..6e7d199
--- /dev/null
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/DpiStatistics.java
@@ -0,0 +1,94 @@
+/*
+ * 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.incubator.net.dpi;
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * DPI statistics with received time.
+ */
+public class DpiStatistics {
+    private final String receivedTime;
+    private final DpiStatInfo dpiStatInfo;
+
+    /**
+     * Constructor for DpiStatistics class.
+     *
+     * @param receivedTime dpiStatInfo received time
+     * @param dpiStatInfo the dpi statistics info
+     */
+    public DpiStatistics(final String receivedTime, final DpiStatInfo dpiStatInfo) {
+        checkNotNull(receivedTime, "Must specify receivedTime");
+        checkNotNull(dpiStatInfo, "Must specify DpiStatInfo");
+
+        this.receivedTime = receivedTime;
+        this.dpiStatInfo = dpiStatInfo;
+    }
+
+    /**
+     * Returns DPI statistics received time.
+     *
+     * @return receivedTime
+     */
+    public String receivedTime() {
+        return receivedTime;
+    }
+
+    /**
+     * Returns DPI statistics information.
+     *
+     * @return dpiStatInfo
+     */
+    public DpiStatInfo dpiStatInfo() {
+        return dpiStatInfo;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(receivedTime, dpiStatInfo);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final DpiStatistics other = (DpiStatistics) obj;
+        if (!Objects.equals(this.receivedTime, other.receivedTime)) {
+            return false;
+        }
+        if (!Objects.equals(this.dpiStatInfo, other.dpiStatInfo)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("receivedTime", receivedTime)
+                .add("dpiStatInfo", dpiStatInfo)
+                .toString();
+    }
+}
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/DpiStatisticsCodec.java b/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/DpiStatisticsCodec.java
new file mode 100644
index 0000000..1e1d1f7
--- /dev/null
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/DpiStatisticsCodec.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.incubator.net.dpi;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.slf4j.Logger;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of encoder for DpiStatistics codec.
+ */
+public final class DpiStatisticsCodec extends JsonCodec<DpiStatistics> {
+
+    private static final String RECEIVED_TIME = "receivedTime";
+    private static final String DPI_STATISTICS = "dpiStatistics";
+
+    private final Logger log = getLogger(getClass());
+
+    @Override
+    public ObjectNode encode(DpiStatistics ds, CodecContext context) {
+        checkNotNull(ds, "DpiStatistics cannot be null");
+
+        final ObjectNode result = context.mapper().createObjectNode();
+
+        result.put(RECEIVED_TIME, ds.receivedTime());
+
+        final JsonCodec<DpiStatInfo> dpiStatInfoCodec =
+                context.codec(DpiStatInfo.class);
+
+        final ObjectNode jsonDpiStatInfo = dpiStatInfoCodec.encode(ds.dpiStatInfo(), context);
+        result.set(DPI_STATISTICS, jsonDpiStatInfo);
+
+        return result;
+    }
+
+    @Override
+    public DpiStatistics decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        log.debug("receivedTime={}, full json={} ", json.get("receivedTime"), json);
+        JsonNode receivedTimeJson = json.get(RECEIVED_TIME);
+        String receivedTime = receivedTimeJson == null ? "" : receivedTimeJson.asText();
+
+        final JsonCodec<DpiStatInfo> dpiStatInfoCodec =
+                context.codec(DpiStatInfo.class);
+
+        DpiStatInfo dpiStatInfo =
+                dpiStatInfoCodec.decode(get(json, DPI_STATISTICS), context);
+
+        return new DpiStatistics(receivedTime, dpiStatInfo);
+    }
+}
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/DpiStatisticsManagerService.java b/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/DpiStatisticsManagerService.java
new file mode 100644
index 0000000..792c293
--- /dev/null
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/DpiStatisticsManagerService.java
@@ -0,0 +1,84 @@
+/*
+ * 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.incubator.net.dpi;
+
+import java.util.List;
+
+/**
+ * Service for DPI Statistics Service Manager.
+ */
+public interface DpiStatisticsManagerService {
+    /**
+     * Get the latest DpiStatistics in the Store list.
+     *
+     * @return the DpiStatistics object class or null if not exist
+     */
+    DpiStatistics getDpiStatisticsLatest();
+
+    /**
+     * Get the latest DpiStatistics in the Store list.
+     *
+     * @param topnProtocols detected topn protocols, default = 100
+     * @param topnFlows detected topn known and unknown flows , default = 100
+     *
+     * @return the DpiStatistics object class or null if not exist
+     */
+    DpiStatistics getDpiStatisticsLatest(int topnProtocols, int topnFlows);
+
+    /**
+     * Gets the last N(Max = 100) DpiStatistics in the Store list.
+     *
+     * @return the List of DpiStatistics object class
+     */
+    List<DpiStatistics> getDpiStatistics(int lastN);
+
+    /**
+     * Gets the last N(Max = 100) DpiStatistics in the Store list.
+     *
+     * @param lastN latest N entries
+     * @param topnProtocols detected topn protocols, default = 100
+     * @param topnFlows detected topn known and unknown flows , default = 100
+     * @return the List of DpiStatistics object class
+     */
+    List<DpiStatistics> getDpiStatistics(int lastN, int topnProtocols, int topnFlows);
+
+    /**
+     * Get the specified receivedTime DpiStatistics in the Store list.
+     *
+     * @param receivedTime receivedTime string with format "yyyy-MM-dd HH:mm:ss"
+     * @return the DpiStatistics object class or null if not exist
+     */
+    DpiStatistics getDpiStatistics(String receivedTime);
+
+    /**
+     * Get the specified receivedTime DpiStatistics in the Store list.
+     *
+     * @param receivedTime receivedTime string with format "yyyy-MM-dd HH:mm:ss"
+     * @param topnProtocols detected topn protocols, default = 100
+     * @param topnFlows detected topn known and unknown flows , default = 100
+     * @return the DpiStatistics object class or null if not exist
+     */
+    DpiStatistics getDpiStatistics(String receivedTime, int topnProtocols, int topnFlows);
+
+    /**
+     * Adds DpiStatistics at the end of the Store list.
+     *
+     * @return the added DpiStatistics object class
+     */
+    DpiStatistics addDpiStatistics(DpiStatistics ds);
+
+}
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/FlowStatInfo.java b/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/FlowStatInfo.java
new file mode 100644
index 0000000..591df78
--- /dev/null
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/FlowStatInfo.java
@@ -0,0 +1,230 @@
+/*
+ * 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.incubator.net.dpi;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Flow statistic information.
+ */
+public class FlowStatInfo {
+    String protocol;
+    String hostAName;
+    int hostAPort;
+    String hostBName;
+    int hostBPort;
+    int detectedProtocol;
+    String detectedProtocolName;
+    long packets;
+    long bytes;
+    String hostServerName;
+
+    /**
+     * Constructor for default FlowStatInfo class.
+     */
+    public FlowStatInfo() {
+        protocol = "";
+        hostAName = "::";
+        hostAPort = 0;
+        hostBName = "";
+        hostBPort = 0;
+        detectedProtocol = 0;
+        detectedProtocolName = "";
+        packets = 0;
+        bytes = 0;
+
+        hostServerName = "";
+    }
+
+    /**
+     * Constructor for FlowStatInfo class specified with flow statistic parameters.
+     */
+    public FlowStatInfo(String protocol, String hostAName, int hostAPort, String hostBName, int hostBPort,
+                        int detectedProtocol, String detectedProtocolName, long packets, long bytes) {
+        this.protocol = protocol;
+        this.hostAName = hostAName;
+        this.hostAPort = hostAPort;
+        this.hostBName = hostBName;
+        this.hostBPort = hostBPort;
+        this.detectedProtocol = detectedProtocol;
+        this.detectedProtocolName = detectedProtocolName;
+        this.packets = packets;
+        this.bytes = bytes;
+
+        hostServerName = "";
+    }
+
+    /**
+     * Constructor for FlowStatInfo class specified with flow statistic parameters and hostServerName.
+     */
+    public FlowStatInfo(String protocol, String hostAName, int hostAPort, String hostBName, int hostBPort,
+                        int detectedProtocol, String detectedProtocolName, long packets, long bytes,
+                        String hostServerName) {
+        this(protocol, hostAName, hostAPort, hostBName, hostBPort, detectedProtocol, detectedProtocolName,
+             packets, bytes);
+
+        this.hostServerName = hostServerName;
+    }
+
+    /**
+     * Returns DPI flow protocol.
+     *
+     * @return protocol
+     */
+    public String protocol() {
+        return protocol;
+    }
+
+    /**
+     * Returns DPI flow host A name.
+     *
+     * @return hostAName
+     */
+    public String hostAName() {
+        return hostAName;
+    }
+
+    /**
+     * Returns DPI flow host A port.
+     *
+     * @return hostAPort
+     */
+    public int hostAPort() {
+        return hostAPort;
+    }
+
+
+    /**
+     * Returns DPI flow host B name.
+     *
+     * @return hostBName
+     */
+    public String hostBName() {
+        return hostBName;
+    }
+
+    /**
+     * Returns DPI flow host B Port.
+     *
+     * @return hostBPort
+     */
+    public int hostBPort() {
+        return hostBPort;
+    }
+
+    /**
+     * Returns DPI flow detected protocol.
+     *
+     * @return detectedProtocol
+     */
+    public int detectedProtocol() {
+        return detectedProtocol;
+    }
+
+    /**
+     * Returns DPI flow detected protocol name.
+     *
+     * @return detectedProtocolName
+     */
+    public String detectedProtocolName() {
+        return detectedProtocolName;
+    }
+
+    /**
+     * Returns DPI flow packets.
+     *
+     * @return packets
+     */
+    public long packets() {
+        return packets;
+    }
+
+    /**
+     * Returns DPI flow bytes.
+     *
+     * @return bytes
+     */
+    public long bytes() {
+        return bytes;
+    }
+
+    /**
+     * Returns DPI flow host server name.
+     *
+     * @return hostServerName
+     */
+    public String hostServerName() {
+        return hostServerName;
+    }
+
+
+    public void setProtocol(String protocol) {
+        this.protocol = protocol;
+    }
+
+    public void setHostAName(String hostAName) {
+        this.hostAName = hostAName;
+    }
+
+    public void setHostAPort(int hostAPort) {
+        this.hostAPort = hostAPort;
+    }
+
+    public void setHostBName(String hostBName) {
+        this.hostBName = hostBName;
+    }
+
+    public void setHostBPort(int hostBPort) {
+        this.hostBPort = hostBPort;
+    }
+
+    public void setDetectedProtocol(int detectedProtocol) {
+        this.detectedProtocol = detectedProtocol;
+    }
+
+    public void setDetectedProtocolName(String detectedProtocolName) {
+        this.detectedProtocolName = detectedProtocolName;
+    }
+
+    public void setPackets(long packets) {
+        this.packets = packets;
+    }
+
+    public void setBytes(long bytes) {
+        this.bytes = bytes;
+    }
+
+    public void setHostServerName(String hostServerName) {
+        this.hostServerName = hostServerName;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("protocol", protocol)
+                .add("hostAName", hostAName)
+                .add("hostAPort", hostAPort)
+                .add("hostBName", hostBName)
+                .add("hostBPort", hostBPort)
+                .add("detectedProtocol", detectedProtocol)
+                .add("detectedProtocolName", detectedProtocolName)
+                .add("packets", packets)
+                .add("bytes", bytes)
+                .add("hostServerName", hostServerName)
+                .toString();
+    }
+}
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/FlowStatInfoCodec.java b/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/FlowStatInfoCodec.java
new file mode 100644
index 0000000..d32ff6c
--- /dev/null
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/FlowStatInfoCodec.java
@@ -0,0 +1,74 @@
+/*
+ * 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.incubator.net.dpi;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.slf4j.Logger;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of encoder for FlowStatInfo codec.
+ */
+public final class FlowStatInfoCodec extends JsonCodec<FlowStatInfo> {
+
+    private final Logger log = getLogger(getClass());
+
+    @Override
+    public ObjectNode encode(FlowStatInfo fsi, CodecContext context) {
+        checkNotNull(fsi, "FlowStatInfo cannot be null");
+
+        return context.mapper().createObjectNode()
+                .put("protocol", fsi.protocol())
+                .put("hostAName", fsi.hostAName())
+                .put("hostAPort", fsi.hostAPort())
+                .put("hostBName", fsi.hostBName())
+                .put("hostBPort", fsi.hostBPort())
+                .put("detectedProtocol", fsi.detectedProtocol())
+                .put("detectedProtocolName", fsi.detectedProtocolName())
+                .put("packets", fsi.packets())
+                .put("bytes", fsi.bytes())
+                .put("hostServerName", fsi.hostServerName());
+    }
+
+    @Override
+    public FlowStatInfo decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        log.debug("protocol={}, full json={} ", json.get("protocol"), json);
+        final String protocol = json.get("protocol").asText();
+        final String hostAName = json.get("hostAName").asText();
+        final int hostAPort = json.get("hostAPort").asInt();
+        final String hostBName = json.get("hostBName").asText();
+        final int hostBPort = json.get("hostBPort").asInt();
+        final int detectedProtocol = json.get("detectedProtocol").asInt();
+        final String detectedProtocolName = json.get("detectedProtocolName").asText();
+        final long packets = json.get("packets").asLong();
+        final long bytes = json.get("bytes").asLong();
+        final String hostServerName = json.get("hostServerName").asText();
+
+        return new FlowStatInfo(protocol,
+                                hostAName, hostAPort, hostBName, hostBPort,
+                                detectedProtocol, detectedProtocolName,
+                                packets, bytes, hostServerName);
+    }
+}
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/ProtocolStatInfo.java b/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/ProtocolStatInfo.java
new file mode 100644
index 0000000..372deff
--- /dev/null
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/ProtocolStatInfo.java
@@ -0,0 +1,135 @@
+/*
+ * 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.incubator.net.dpi;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Protocol statistic information.
+ */
+public class ProtocolStatInfo {
+    String name;
+    String breed;
+    long packets;
+    long bytes;
+    int flows;
+
+    /**
+     * Constructor for default ProtocolStatInfo class.
+     */
+    public ProtocolStatInfo() {
+        name = "";
+        breed = "";
+        packets = 0;
+        bytes = 0;
+        flows = 0;
+    }
+
+    /**
+     * Constructor for ProtocolStatInfo class specified with protocol statistic parameters.
+     *
+     * @param name protocol name
+     * @param breed protocol breed
+     * @param packets protocol packets
+     * @param bytes protocol bytes
+     * @param flows protocol flows
+     */
+    public ProtocolStatInfo(String name, String breed, long packets, long bytes, int flows) {
+        this.name = name;
+        this.breed = breed;
+        this.packets = packets;
+        this.bytes = bytes;
+        this.flows = flows;
+    }
+
+    /**
+     * Returns DPI protocol name.
+     *
+     * @return name
+     */
+    public String name() {
+        return name;
+    }
+
+    /**
+     * Returns DPI protocol breed.
+     *
+     * @return breed
+     */
+    public String breed() {
+        return breed;
+    }
+
+    /**
+     * Returns DPI protocol packets.
+     *
+     * @return packets
+     */
+    public long packets() {
+        return packets;
+    }
+
+    /**
+     * Returns DPI protocol bytes.
+     *
+     * @return bytes
+     */
+    public long bytes() {
+        return bytes;
+    }
+
+    /**
+     * Returns DPI protocol flows.
+     *
+     * @return flows
+     */
+    public int flows() {
+        return flows;
+    }
+
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public void setBreed(String breed) {
+        this.breed = breed;
+    }
+
+    public void setPackets(long packets) {
+        this.packets = packets;
+    }
+
+    public void setBytes(long bytes) {
+        this.bytes = bytes;
+    }
+
+    public void setFlows(int flows) {
+        this.flows = flows;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("name", name)
+                .add("breed", breed)
+                .add("packets", packets)
+                .add("bytes", bytes)
+                .add("flows", flows)
+                .toString();
+    }
+}
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/ProtocolStatInfoCodec.java b/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/ProtocolStatInfoCodec.java
new file mode 100644
index 0000000..24ef9fc
--- /dev/null
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/ProtocolStatInfoCodec.java
@@ -0,0 +1,61 @@
+/*
+ * 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.incubator.net.dpi;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.slf4j.Logger;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of encoder for ProtocolStatInfo codec.
+ */
+public final class ProtocolStatInfoCodec extends JsonCodec<ProtocolStatInfo> {
+
+    private final Logger log = getLogger(getClass());
+
+    @Override
+    public ObjectNode encode(ProtocolStatInfo psi, CodecContext context) {
+        checkNotNull(psi, "ProtocolStatInfo cannot be null");
+
+        return context.mapper().createObjectNode()
+                .put("name", psi.name())
+                .put("breed", psi.breed())
+                .put("packets", psi.packets())
+                .put("bytes", psi.bytes())
+                .put("flows", psi.flows());
+    }
+
+    @Override
+    public ProtocolStatInfo decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        log.debug("name={}, full json={} ", json.get("name"), json);
+        final String name = json.get("name").asText();
+        final String breed = json.get("breed").asText();
+        final long packets = json.get("packets").asLong();
+        final long bytes = json.get("bytes").asLong();
+        final int flows = json.get("flows").asInt();
+
+        return new ProtocolStatInfo(name, breed, packets, bytes, flows);
+    }
+}
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/TrafficStatInfo.java b/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/TrafficStatInfo.java
new file mode 100644
index 0000000..e7e4ece
--- /dev/null
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/TrafficStatInfo.java
@@ -0,0 +1,308 @@
+/*
+ * 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.incubator.net.dpi;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Traffic statistic information.
+ */
+public class TrafficStatInfo {
+    long ethernetBytes;
+    long discardedBytes;
+    long ipPackets;
+    long totalPackets;
+    long ipBytes;
+    int avgPktSize;
+    int uniqueFlows;
+    long tcpPackets;
+    long udpPackets;
+
+    double dpiThroughputPps;
+    double dpiThroughputBps;
+    double trafficThroughputPps;
+    double trafficThroughputBps;
+    double trafficDurationSec;
+    int guessedFlowProtos;
+
+    static final String PPS_STRING = "pps";
+    static final String BPS_STRING = "bps";
+    static final String SEC_STRING = "sec";
+
+    /**
+     * Constructor for default TrafficStatInfo class.
+     */
+    public TrafficStatInfo() {
+        ethernetBytes = 0;
+        discardedBytes = 0;
+        ipPackets = 0;
+        totalPackets = 0;
+        ipBytes = 0;
+        avgPktSize = 0;
+        uniqueFlows = 0;
+        tcpPackets = 0;
+        udpPackets = 0;
+        dpiThroughputPps = 0;
+        dpiThroughputBps = 0;
+        trafficThroughputPps = 0;
+        trafficThroughputBps = 0;
+        trafficDurationSec = 0;
+        guessedFlowProtos = 0;
+    }
+
+    /**
+     * Constructor for TrafficStatInfo class specified with traffic statistic parameters.
+     */
+    public TrafficStatInfo(long ethernetBytes, long discardedBytes, long ipPackets, long totalPackets,
+                           long ipBytes, int avgPktSize, int uniqueFlows, long tcpPackets, long udpPackets,
+                           double dpiThroughputPps, double dpiThroughputBps,
+                           double trafficThroughputPps, double trafficThroughputBps,
+                           double trafficDurationSec, int guessedFlowProtos) {
+        this.ethernetBytes = ethernetBytes;
+        this.discardedBytes = discardedBytes;
+        this.ipPackets = ipPackets;
+        this.totalPackets = totalPackets;
+        this.ipBytes = ipBytes;
+        this.avgPktSize = avgPktSize;
+        this.uniqueFlows = uniqueFlows;
+        this.tcpPackets = tcpPackets;
+        this.udpPackets = udpPackets;
+        this.dpiThroughputPps = dpiThroughputPps;
+        this.dpiThroughputBps = dpiThroughputBps;
+        this.trafficThroughputPps = trafficThroughputPps;
+        this.trafficThroughputBps = trafficThroughputBps;
+        this.trafficDurationSec = trafficDurationSec;
+        this.guessedFlowProtos = guessedFlowProtos;
+    }
+
+    /**
+     * Returns DPI traffic ethernet bytes.
+     *
+     * @return ethernetBytes
+     */
+    public long ethernetBytes() {
+        return ethernetBytes;
+    }
+
+    /**
+     * Returns DPI traffic discarded bytes.
+     *
+     * @return discardedBytes
+     */
+    public long discardedBytes() {
+        return discardedBytes;
+    }
+
+    /**
+     * Returns DPI traffic ip packets.
+     *
+     * @return ipPackets
+     */
+    public long ipPackets() {
+        return ipPackets;
+    }
+
+    /**
+     * Returns DPI traffic total packets.
+     *
+     * @return totalPackets
+     */
+    public long totalPackets() {
+        return totalPackets;
+    }
+
+    /**
+     * Returns DPI traffic ip bytes.
+     *
+     * @return ipBytes
+     */
+    public long ipBytes() {
+        return ipBytes;
+    }
+
+    /**
+     * Returns DPI traffic average packet size.
+     *
+     * @return avgPktSize
+     */
+    public int avgPktSize() {
+        return avgPktSize;
+    }
+
+    /**
+     * Returns DPI traffic the number of unique flows.
+     *
+     * @return uniqueFlows
+     */
+    public int uniqueFlows() {
+        return uniqueFlows;
+    }
+
+    /**
+     * Returns DPI traffic TCP packets.
+     *
+     * @return tcpPackets
+     */
+    public long tcpPackets() {
+        return tcpPackets;
+    }
+
+    /**
+     * Returns DPI traffic UDP packets.
+     *
+     * @return udpPackets
+     */
+    public long udpPackets() {
+        return udpPackets;
+    }
+
+    /**
+     * Returns DPI traffic throughput Pps(Packet per second).
+     *
+     * @return dpiThroughputPps
+     */
+    public double dpiThroughputPps() {
+        return dpiThroughputPps;
+    }
+
+    /**
+     * Returns DPI traffic throughput Bps(Byte per second).
+     *
+     * @return dpiThroughputBps
+     */
+    public double dpiThroughputBps() {
+        return dpiThroughputBps;
+    }
+
+    /**
+     * Returns total traffic throughput Pps(Packet per second).
+     *
+     * @return trafficThroughputPps
+     */
+    public double trafficThroughputPps() {
+        return trafficThroughputPps;
+    }
+
+    /**
+     * Returns total traffic throughput Bps(Byte per second).
+     *
+     * @return trafficThroughputBps
+     */
+    public double trafficThroughputBps() {
+        return trafficThroughputBps;
+    }
+
+    /**
+     * Returns DPI traffic duration second.
+     *
+     * @return trafficDurationSec
+     */
+    public double trafficDurationSec() {
+        return trafficDurationSec;
+    }
+
+    /**
+     * Returns DPI traffic the number of guessed flow protocols.
+     *
+     * @return guessedFlowProtos
+     */
+    public int guessedFlowProtos() {
+        return guessedFlowProtos;
+    }
+
+
+    public void setEthernetBytes(long ethernetBytes) {
+        this.ethernetBytes = ethernetBytes;
+    }
+
+    public void setDiscardedBytes(long discardedBytes) {
+        this.discardedBytes = discardedBytes;
+    }
+
+    public void setIpPackets(long ipPackets) {
+        this.ipPackets = ipPackets;
+    }
+
+    public void setTotalPackets(long totalPackets) {
+        this.totalPackets = totalPackets;
+    }
+
+    public void setIpBytes(long ipBytes) {
+        this.ipBytes = ipBytes;
+    }
+
+    public void setAvgPktSize(int avgPktSize) {
+        this.avgPktSize = avgPktSize;
+    }
+
+    public void setUniqueFlows(int uniqueFlows) {
+        this.uniqueFlows = uniqueFlows;
+    }
+
+    public void setTcpPackets(long tcpPackets) {
+        this.tcpPackets = tcpPackets;
+    }
+
+    public void setUdpPackets(long udpPackets) {
+        this.udpPackets = udpPackets;
+    }
+
+    public void setDpiThroughputPps(double dpiThroughputPps) {
+        this.dpiThroughputPps = dpiThroughputPps;
+    }
+
+    public void setDpiThroughputBps(double dpiThroughputBps) {
+        this.dpiThroughputBps = dpiThroughputBps;
+    }
+
+    public void setTrafficThroughputPps(double trafficThroughputPps) {
+        this.trafficThroughputPps = trafficThroughputPps;
+    }
+
+    public void setTrafficThroughputBps(double trafficThroughputBps) {
+        this.trafficThroughputBps = trafficThroughputBps;
+    }
+
+    public void setTrafficDurationSec(double trafficDurationSec) {
+        this.trafficDurationSec = trafficDurationSec;
+    }
+
+    public void setGuessedFlowProtos(int guessedFlowProtos) {
+        this.guessedFlowProtos = guessedFlowProtos;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("ethernetBytes", ethernetBytes)
+                .add("discardedBytes", discardedBytes)
+                .add("ipPackets", ipPackets)
+                .add("totalPackets", totalPackets)
+                .add("ipBytes", ipBytes)
+                .add("avgPktSize", avgPktSize)
+                .add("uniqueFlows", uniqueFlows)
+                .add("tcpPackets", tcpPackets)
+                .add("udpPackets", udpPackets)
+                .add("dpiThroughputPps", dpiThroughputPps + " " + PPS_STRING)
+                .add("dpiThroughputBps", dpiThroughputBps + " " + BPS_STRING)
+                .add("trafficThroughputPps", trafficThroughputPps + " " + PPS_STRING)
+                .add("trafficThroughputBps", trafficThroughputBps + " " + BPS_STRING)
+                .add("trafficDurationSec", trafficDurationSec + " " + SEC_STRING)
+                .add("guessedFlowProtos", guessedFlowProtos)
+                .toString();
+    }
+}
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/TrafficStatInfoCodec.java b/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/TrafficStatInfoCodec.java
new file mode 100644
index 0000000..7f9ace8
--- /dev/null
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/TrafficStatInfoCodec.java
@@ -0,0 +1,90 @@
+/*
+ * 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.incubator.net.dpi;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.slf4j.Logger;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of encoder for TrafficStatInfo codec.
+ */
+public final class TrafficStatInfoCodec extends JsonCodec<TrafficStatInfo> {
+
+    private final Logger log = getLogger(getClass());
+
+    @Override
+    public ObjectNode encode(TrafficStatInfo tsi, CodecContext context) {
+        checkNotNull(tsi, "TrafficStatInfo cannot be null");
+
+        return context.mapper().createObjectNode()
+                .put("ethernetBytes", tsi.ethernetBytes())
+                .put("discardedBytes", tsi.discardedBytes())
+                .put("ipPackets", tsi.ipPackets())
+                .put("totalPackets", tsi.totalPackets())
+                .put("ipBytes", tsi.ipBytes())
+                .put("avgPktSize", tsi.avgPktSize())
+                .put("uniqueFlows", tsi.uniqueFlows())
+                .put("tcpPackets", tsi.tcpPackets())
+                .put("udpPackets", tsi.udpPackets())
+                .put("dpiThroughputPps", tsi.dpiThroughputPps())
+                .put("dpiThroughputBps", tsi.dpiThroughputBps())
+                .put("trafficThroughputPps", tsi.trafficThroughputPps())
+                .put("trafficThroughputBps", tsi.trafficThroughputBps())
+                .put("trafficDurationSec", tsi.trafficDurationSec())
+                .put("guessedFlowProtos", tsi.guessedFlowProtos());
+    }
+
+    @Override
+    public TrafficStatInfo decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        log.debug("ethernetBytes={}, full json={} ", json.get("ethernetBytes"), json);
+        final Long ethernetBytes = json.get("ethernetBytes").asLong();
+        final Long discardedBytes = json.get("discardedBytes").asLong();
+        final Long ipPackets = json.get("ipPackets").asLong();
+        final Long totalPackets = json.get("totalPackets").asLong();
+        final Long ipBytes = json.get("ipBytes").asLong();
+        final int avgPktSize = json.get("avgPktSize").asInt();
+        final int uniqueFlows = json.get("uniqueFlows").asInt();
+        final Long tcpPackets = json.get("tcpPackets").asLong();
+        final Long udpPackets = json.get("udpPackets").asLong();
+        final double dpiThroughputPps = json.get("dpiThroughputPps").asDouble();
+        final double dpiThroughputBps = json.get("dpiThroughputBps").asDouble();
+        final double trafficThroughputPps = json.get("trafficThroughputPps").asDouble();
+        final double trafficThroughputBps = json.get("trafficThroughputBps").asDouble();
+        final double trafficDurationSec = json.get("trafficDurationSec").asDouble();
+        final int guessedFlowProtos = json.get("guessedFlowProtos").asInt();
+
+        return new TrafficStatInfo(ethernetBytes,
+                                   discardedBytes,
+                                   ipPackets, totalPackets,
+                                   ipBytes, avgPktSize,
+                                   uniqueFlows,
+                                   tcpPackets, udpPackets,
+                                   dpiThroughputPps, dpiThroughputBps,
+                                   trafficThroughputPps, trafficThroughputBps,
+                                   trafficDurationSec,
+                                   guessedFlowProtos);
+    }
+}
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/package-info.java b/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/package-info.java
new file mode 100644
index 0000000..6ee300c
--- /dev/null
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/dpi/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.
+ */
+
+/**
+ * Subsystem for dpi statistics service.
+ */
+package org.onosproject.incubator.net.dpi;
\ No newline at end of file
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/dpi/impl/DpiStatisticsManager.java b/incubator/net/src/main/java/org/onosproject/incubator/net/dpi/impl/DpiStatisticsManager.java
new file mode 100644
index 0000000..23cb8b8
--- /dev/null
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/dpi/impl/DpiStatisticsManager.java
@@ -0,0 +1,478 @@
+/*
+ * 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.incubator.net.dpi.impl;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.incubator.net.dpi.DpiStatInfo;
+import org.onosproject.incubator.net.dpi.DpiStatistics;
+import org.onosproject.incubator.net.dpi.DpiStatisticsManagerService;
+import org.onosproject.incubator.net.dpi.FlowStatInfo;
+import org.onosproject.incubator.net.dpi.ProtocolStatInfo;
+import org.onosproject.incubator.net.dpi.TrafficStatInfo;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.SortedMap;
+import java.util.TimeZone;
+import java.util.TreeMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static java.lang.Thread.sleep;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * DPI Statistics Manager.
+ */
+@Component(immediate = true)
+@Service
+public class DpiStatisticsManager implements DpiStatisticsManagerService {
+
+    private static ServerSocket serverSocket;
+    private static int port = 11990; // socket server listening port
+
+    private final Logger log = getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    private ApplicationId appId;
+
+    private final ExecutorService dpiListenerThread =
+            Executors.newSingleThreadExecutor(groupedThreads("onos/apps/dpi", "dpi-listener"));
+
+    DpiStatisticsListener dpiStatisticsListener = null;
+
+    // 31*2(month)*24(hour)*3600(second)/5(second)
+    private static final int MAX_DPI_STATISTICS_ENTRY = 1071360;
+
+    private SortedMap<String, DpiStatistics> dpiStatisticsMap =
+            new TreeMap<>(new MapComparator());
+
+    private long convertTimeToLong(String timeString) {
+        long timeLong = 0;
+
+        try {
+            // Time format: yyyy-MM-dd HH:mm:ss, Time Zone: GMT
+            SimpleDateFormat df = new SimpleDateFormat(DATE_FMT, Locale.KOREA);
+            df.setTimeZone(TimeZone.getTimeZone(TIME_ZONE));
+
+             timeLong = df.parse(timeString).getTime();
+        } catch (ParseException e) {
+            log.error("Time parse error! Exception={}", e.toString());
+        }
+
+        return timeLong;
+    }
+
+    private static final String DATE_FMT = "yyyy-MM-dd HH:mm:ss";
+    private static final String TIME_ZONE = "GMT";
+
+    public static final int MAX_DPI_STATISTICS_REQUEST = 100;
+    public static final int MAX_DPI_STATISTICS_TOPN = 100;
+
+    @Activate
+    public void activate(ComponentContext context) {
+        appId = coreService.registerApplication("org.onosproject.dpi");
+
+        dpiStatisticsListener = new DpiStatisticsListener();
+        dpiListenerThread.execute(dpiStatisticsListener);
+
+        log.info("Started", appId.id());
+    }
+
+    @Deactivate
+    public void deactivate() {
+        log.info("Deactivated...");
+        dpiStatisticsListener.stop();
+        dpiListenerThread.shutdown();
+        log.info("Stopped");
+    }
+
+    @Override
+    public DpiStatistics getDpiStatisticsLatest() {
+        if (dpiStatisticsMap.size() > 0) {
+            return dpiStatisticsMap.get(dpiStatisticsMap.firstKey());
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public DpiStatistics getDpiStatisticsLatest(int topnProtocols, int topnFlows) {
+        DpiStatistics ds, topnDs;
+
+        ds = getDpiStatisticsLatest();
+        topnDs = processTopn(ds, topnProtocols, topnFlows);
+
+        return topnDs;
+    }
+
+    @Override
+    public List<DpiStatistics> getDpiStatistics(int lastN) {
+        List<DpiStatistics> dsList = new ArrayList<>();
+        DpiStatistics ds;
+
+        if (lastN > MAX_DPI_STATISTICS_REQUEST) {
+            lastN = MAX_DPI_STATISTICS_REQUEST;
+        }
+
+        SortedMap tempMap = new TreeMap(new MapComparator());
+        tempMap.putAll(dpiStatisticsMap);
+
+        for (int i = 0; i < lastN && i < tempMap.size(); i++) {
+            ds = (DpiStatistics) tempMap.get(tempMap.firstKey());
+            dsList.add(i, new DpiStatistics(ds.receivedTime(), ds.dpiStatInfo()));
+
+            tempMap.remove(tempMap.firstKey());
+        }
+
+        return dsList;
+    }
+
+    @Override
+    public List<DpiStatistics> getDpiStatistics(int lastN, int topnProtocols, int topnFlows) {
+        List<DpiStatistics> dsList;
+        List<DpiStatistics> topnDsList = new ArrayList<>();
+        DpiStatistics ds, topnDs;
+
+        dsList = getDpiStatistics(lastN);
+        for (int i = 0; i < dsList.size(); i++) {
+            ds = dsList.get(i);
+            topnDs = processTopn(ds, topnProtocols, topnFlows);
+            topnDsList.add(i, topnDs);
+        }
+
+        return topnDsList;
+    }
+
+    @Override
+    public DpiStatistics getDpiStatistics(String receivedTime) {
+        DpiStatistics ds;
+
+        if (receivedTime == null) {
+            return null;
+        }
+
+        if (!dpiStatisticsMap.containsKey(receivedTime)) {
+            return null;
+        }
+
+        ds = dpiStatisticsMap.get(receivedTime);
+
+        return ds;
+    }
+
+    @Override
+    public DpiStatistics getDpiStatistics(String receivedTime, int topnProtocols, int topnFlows) {
+        DpiStatistics ds, topnDs;
+
+        ds = getDpiStatistics(receivedTime);
+
+        topnDs = processTopn(ds, topnProtocols, topnFlows);
+
+        return topnDs;
+    }
+
+    @Override
+    public DpiStatistics addDpiStatistics(DpiStatistics ds) {
+        if (ds == null) {
+            return ds;
+        }
+
+        // check the time. The firstKey is lastTime because of descending sorted order
+        if (dpiStatisticsMap.size() > 0) {
+            String lastTime = dpiStatisticsMap.get(dpiStatisticsMap.firstKey()).receivedTime();
+            String inputTime = ds.receivedTime();
+
+            long lastTimeLong = convertTimeToLong(lastTime);
+            long inputTimeLong = convertTimeToLong(inputTime);
+
+            if (lastTimeLong >= inputTimeLong) {
+                return null;
+            }
+        }
+
+        if (dpiStatisticsMap.size() >= MAX_DPI_STATISTICS_ENTRY) {
+            // remove the last (oldest) entry
+            dpiStatisticsMap.remove(dpiStatisticsMap.lastKey());
+        }
+
+        if (dpiStatisticsMap.containsKey(ds.receivedTime())) {
+            log.warn("addDpiStatistics(), {} dpiStatistics is already existing!",
+                      ds.receivedTime());
+            return null;
+        }
+
+        dpiStatisticsMap.put(ds.receivedTime(), ds);
+        log.debug("addDpiStatistics: dpiResultJson data[time={}] is added " +
+                          "into DpiStatisticsMap size={}.",
+                  ds.receivedTime(), dpiStatisticsMap.size());
+
+        return ds;
+    }
+
+    private class MapComparator implements Comparator<String> {
+        @Override
+        public int compare(String rt1, String rt2) {
+            long rt1Long = convertTimeToLong(rt1);
+            long rt2Long = convertTimeToLong(rt2);
+
+            // Descending order
+            if (rt1Long > rt2Long) {
+                return -1;
+            } else if (rt1Long < rt2Long) {
+                return 1;
+            } else {
+                return 0;
+            }
+        }
+    }
+
+    private class ProtocolComparator implements Comparator<ProtocolStatInfo> {
+        @Override
+        public int compare(ProtocolStatInfo p1, ProtocolStatInfo p2) {
+            //Descending order
+            if (p1.bytes() > p2.bytes()) {
+                return -1;
+            } else if (p1.bytes() < p2.bytes()) {
+                return 1;
+            } else {
+                return 0;
+            }
+        }
+    }
+
+    private class FlowComparator implements Comparator<FlowStatInfo> {
+        @Override
+        public int compare(FlowStatInfo f1, FlowStatInfo f2) {
+            // Descending order
+            if (f1.bytes() > f2.bytes()) {
+                return -1;
+            } else if (f1.bytes() < f2.bytes()) {
+                return 1;
+            } else {
+                return 0;
+            }
+        }
+    }
+    private DpiStatistics processTopn(DpiStatistics ds, int topnProtocols, int topnFlows) {
+        if (ds == null) {
+            return null;
+        }
+
+        if (topnProtocols <= 0) {
+            // displays all entries
+            topnProtocols = 0;
+        } else if (topnProtocols > MAX_DPI_STATISTICS_TOPN) {
+            topnProtocols = MAX_DPI_STATISTICS_TOPN;
+        }
+
+        if (topnFlows <= 0) {
+            // displays all entries
+            topnFlows = 0;
+        } else if (topnFlows > MAX_DPI_STATISTICS_TOPN) {
+            topnFlows = MAX_DPI_STATISTICS_TOPN;
+        }
+
+        if (topnProtocols == 0 && topnFlows == 0) {
+            return ds;
+        }
+
+        TrafficStatInfo tsi = ds.dpiStatInfo().trafficStatistics();
+        List<ProtocolStatInfo> psiList;
+        List<FlowStatInfo> kfList;
+        List<FlowStatInfo> ufList;
+
+        List<ProtocolStatInfo> pList = ds.dpiStatInfo().detectedProtos();
+        Collections.sort(pList, new ProtocolComparator());
+        if (topnProtocols > 0 && topnProtocols < pList.size()) {
+            psiList = pList.subList(0, topnProtocols);
+        } else {
+            psiList = pList;
+        }
+
+
+        List<FlowStatInfo> fList = ds.dpiStatInfo().knownFlows();
+        Collections.sort(fList, new FlowComparator());
+        if (topnFlows > 0 && topnFlows < fList.size()) {
+            kfList = fList.subList(0, topnFlows);
+        } else {
+            kfList = fList;
+        }
+
+        fList = ds.dpiStatInfo().unknownFlows();
+        Collections.sort(fList, new FlowComparator());
+        if (topnFlows > 0 && topnFlows < fList.size()) {
+            ufList = fList.subList(0, topnFlows);
+        } else {
+            ufList = fList;
+        }
+
+        DpiStatInfo dsi = new DpiStatInfo();
+        dsi.setTrafficStatistics(tsi);
+        dsi.setDetectedProtos(psiList);
+        dsi.setKnownFlows(kfList);
+        dsi.setUnknownFlows(ufList);
+
+        DpiStatistics retDs = new DpiStatistics(ds.receivedTime(), dsi);
+        return retDs;
+    }
+
+    /**
+     * Receiving DPI Statistics result thread.
+     */
+    private class DpiStatisticsListener implements Runnable {
+        Socket clientSocket = null;
+        BufferedReader in = null;
+        PrintWriter out = null;
+
+        String resultJsonString = null;
+
+        static final int MAX_SLEEP_COUNT = 10;
+        int sleepCount = 0;
+
+        @Override
+        public void run() {
+            log.info("DpiStatisticsListener: Receiving thread started...");
+            receiveDpiResult();
+        }
+
+        public void stop() {
+            try {
+                if (serverSocket != null) {
+                    if (clientSocket != null) {
+                        if (in != null) {
+                            in.close();
+                        }
+                        if (out != null) {
+                            out.close();
+                        }
+                        clientSocket.close();
+                        //log.debug("DpiResultListener: stop(): Socket close() is done...");
+                    }
+                    serverSocket.close();
+                    //log.debug("DpiResultListener: stop(): Server close() is done...");
+                }
+            } catch (Exception e) {
+                log.error("DpiStatisticsListener: stop(): Server Socket closing error, exception={}",
+                          e.toString());
+            }
+            log.debug("DpiStatisticsListener: stop(): stopped...");
+        }
+
+        private void receiveDpiResult() {
+            try {
+                serverSocket = new ServerSocket(port);
+            } catch (Exception e) {
+                log.error("DpiStatisticsListener: ServerSocket listening error from port={} in localhost, exception={}",
+                          port, e.toString());
+                return;
+            }
+
+            try {
+                while (true) {
+                    if (clientSocket == null) {
+                        log.info("DpiStatisticsListener: Waiting for accepting from dpi client...");
+                        clientSocket = serverSocket.accept();
+                        log.info("DpiStatisticsListener: Accepted from dpi client={}",
+                                 clientSocket.getRemoteSocketAddress().toString());
+
+                        in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
+                        out = new PrintWriter(clientSocket.getOutputStream(), true); // For disconnecting check!
+
+                        resultJsonString = null;
+                    }
+
+                    sleepCount = 0;
+                    while (!in.ready()) {
+                        sleep(1000); // sleep one second.
+                        if (out.checkError() || ++sleepCount >= MAX_SLEEP_COUNT) {
+                            log.debug("DpiStatisticsListener: server and socket connect is lost...");
+                            in.close();
+                            in = null;
+                            out.close();
+                            out = null;
+                            clientSocket.close();
+                            clientSocket = null;
+
+                            break;
+                        }
+                    }
+
+                    if (in != null) {
+                        resultJsonString = in.readLine();
+
+                        // process the result
+                        log.trace("DpiStatisticsListener: resultJsonString={}", resultJsonString);
+                        processResultJson(resultJsonString);
+                    }
+                }
+            } catch (Exception e) {
+                log.error("DpiStatisticsListener: Exception = {}", e.toString());
+                return;
+            }
+        }
+
+        private void processResultJson(String resultJsonString) {
+            Date tr = new Date(System.currentTimeMillis());
+            SimpleDateFormat df = new SimpleDateFormat(DATE_FMT, Locale.KOREA);
+            df.setTimeZone(TimeZone.getTimeZone(TIME_ZONE));
+
+            String curReceivedTime = new String(df.format(tr));
+            String curResultJson = new String(resultJsonString);
+
+            DpiStatInfo dpiStatInfo;
+            ObjectMapper mapper = new ObjectMapper();
+            try {
+                dpiStatInfo = mapper.readValue(curResultJson, DpiStatInfo.class);
+            } catch (IOException e) {
+                log.error("DpiStatisticsListener: ObjectMapper Exception = {}", e.toString());
+                return;
+            }
+
+            DpiStatistics dpiStatistics = new DpiStatistics(curReceivedTime, dpiStatInfo);
+
+            addDpiStatistics(dpiStatistics);
+        }
+    }
+}
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/dpi/impl/package-info.java b/incubator/net/src/main/java/org/onosproject/incubator/net/dpi/impl/package-info.java
new file mode 100644
index 0000000..afc6313
--- /dev/null
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/dpi/impl/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.
+ */
+
+/**
+ * Implementation of the dpi statistics service.
+ */
+package org.onosproject.incubator.net.dpi.impl;
\ No newline at end of file
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/CoreWebApplication.java b/web/api/src/main/java/org/onosproject/rest/resources/CoreWebApplication.java
index b8597fa..b92038b 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/CoreWebApplication.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/CoreWebApplication.java
@@ -51,7 +51,8 @@
                 TenantWebResource.class,
                 VirtualNetworkWebResource.class,
                 MastershipWebResource.class,
-                InvalidConfigExceptionMapper.class
+                InvalidConfigExceptionMapper.class,
+                DpisWebResource.class
         );
     }
 }
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/DpisWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/DpisWebResource.java
new file mode 100644
index 0000000..6b01b13
--- /dev/null
+++ b/web/api/src/main/java/org/onosproject/rest/resources/DpisWebResource.java
@@ -0,0 +1,308 @@
+/*
+ * 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.rest.resources;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.incubator.net.dpi.DpiStatInfo;
+import org.onosproject.incubator.net.dpi.DpiStatistics;
+import org.onosproject.incubator.net.dpi.DpiStatisticsManagerService;
+import org.onosproject.incubator.net.dpi.FlowStatInfo;
+import org.onosproject.incubator.net.dpi.ProtocolStatInfo;
+import org.onosproject.rest.AbstractWebResource;
+import org.slf4j.Logger;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Query the latest DPI statistics info.
+ */
+
+@Path("dpis")
+public class DpisWebResource extends AbstractWebResource {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final int MAX_TOPN = 100;
+
+    private final DpiStatisticsManagerService service = get(DpiStatisticsManagerService.class);
+
+    public static final Comparator<ProtocolStatInfo> PROTOCOL_STAT_INFO_COMPARATOR =
+        new Comparator<ProtocolStatInfo>() {
+            @Override
+            public int compare(ProtocolStatInfo psi1, ProtocolStatInfo psi2) {
+                long delta = psi1.bytes() - psi2.bytes();
+                return delta == 0 ? 0 : (delta > 0 ? -1 : +1);
+            }
+        };
+
+    public static final Comparator<FlowStatInfo> FLOW_STAT_INFO_COMPARATOR =
+            new Comparator<FlowStatInfo>() {
+                @Override
+                public int compare(FlowStatInfo fsi1, FlowStatInfo fsi2) {
+                    long delta = fsi1.bytes() - fsi2.bytes();
+                    return delta == 0 ? 0 : (delta > 0 ? -1 : +1);
+                }
+            };
+
+    /**
+     * Gets the latest dpi statistics.
+     *
+     * @return 200 OK with a dpi statistics
+     * @onos.rsModel DpiStatistics
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getDpisLatest(@QueryParam("topn") int topn) {
+        log.debug("getDpisLatest request with topn={}", topn);
+
+        DpiStatistics ds = service.getDpiStatisticsLatest();
+        DpiStatistics retDs;
+
+        if (ds == null) {
+            retDs = new DpiStatistics("", new DpiStatInfo());
+        } else if (topn <= 0) {
+            retDs = ds;
+        } else {
+            if (topn > MAX_TOPN) {
+                topn = MAX_TOPN;
+            }
+            retDs = new DpiStatistics(ds.receivedTime(),
+                                      new DpiStatInfo(ds.dpiStatInfo().trafficStatistics()));
+            List<ProtocolStatInfo> psiList = ds.dpiStatInfo().detectedProtos();
+            if (psiList != null) {
+                // sorts protocol list with descending order based on bytes within topn
+                List<ProtocolStatInfo> psiListSorted =
+                        psiList.stream().sorted(PROTOCOL_STAT_INFO_COMPARATOR).
+                        limit(topn).collect(Collectors.toList());
+                retDs.dpiStatInfo().setDetectedProtos(psiListSorted);
+            }
+            List<FlowStatInfo> fsiList = ds.dpiStatInfo().knownFlows();
+            if (fsiList != null) {
+                // sorts known flow list with descending order based on bytes within topn
+                List<FlowStatInfo> fsiListSorted =
+                        fsiList.stream().sorted(FLOW_STAT_INFO_COMPARATOR).
+                                limit(topn).collect(Collectors.toList());
+                retDs.dpiStatInfo().setKnownFlows(fsiListSorted);
+            }
+            fsiList = ds.dpiStatInfo().unknownFlows();
+            if (fsiList != null) {
+                // sorts unknown flow list with descending order based on bytes within topn
+                List<FlowStatInfo> fsiListSorted =
+                        fsiList.stream().sorted(FLOW_STAT_INFO_COMPARATOR).
+                                limit(topn).collect(Collectors.toList());
+                retDs.dpiStatInfo().setUnknownFlows(fsiListSorted);
+            }
+        }
+
+        ObjectNode result = codec(DpiStatistics.class).encode(retDs, this);
+        return ok(result).build();
+
+    }
+
+    /**
+     * Gets the latest traffic statistics only.
+     *
+     * @return 200 OK with a traffic statistics
+     * @onos.rsModel TrafficStatistics
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("traffic")
+    public Response getTrafficStatistics() {
+        log.debug("getTrafficStatistics request");
+
+        DpiStatistics ds = service.getDpiStatisticsLatest();
+        if (ds == null) {
+            ds = new DpiStatistics("", new DpiStatInfo());
+        }
+
+        DpiStatInfo dsi = new DpiStatInfo();
+        dsi.setTrafficStatistics(ds.dpiStatInfo().trafficStatistics());
+        DpiStatistics dsTraffic = new DpiStatistics(ds.receivedTime(), dsi);
+
+        ObjectNode result = codec(DpiStatistics.class).encode(dsTraffic, this);
+        return ok(result).build();
+    }
+
+    /**
+     * Gets the latest detected protocol statistics only.
+     *
+     * @return 200 OK with a protocol statistics
+     * @onos.rsModel ProtocolStatistics
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("protocols")
+    public Response getDetectedProtocols(@QueryParam("topn") int topn) {
+        log.debug("getDetectedProtocols request with topn={}", topn);
+
+        DpiStatistics ds = service.getDpiStatisticsLatest();
+        DpiStatistics dsProtocol;
+
+        if (ds == null) {
+            dsProtocol = new DpiStatistics("", new DpiStatInfo());
+        } else if (topn <= 0) {
+            DpiStatInfo dsi = new DpiStatInfo();
+            dsi.setDetectedProtos(ds.dpiStatInfo().detectedProtos());
+            dsProtocol = new DpiStatistics(ds.receivedTime(), dsi);
+        } else {
+            if (topn > MAX_TOPN) {
+                topn = MAX_TOPN;
+            }
+            dsProtocol = new DpiStatistics(ds.receivedTime(), new DpiStatInfo());
+            List<ProtocolStatInfo> psiList = ds.dpiStatInfo().detectedProtos();
+            if (psiList != null) {
+                // sorts protocol list with descending order based on bytes within topn
+                List<ProtocolStatInfo> psiListSorted =
+                        psiList.stream().sorted(PROTOCOL_STAT_INFO_COMPARATOR).
+                                limit(topn).collect(Collectors.toList());
+                dsProtocol.dpiStatInfo().setDetectedProtos(psiListSorted);
+            }
+        }
+
+        ObjectNode result = codec(DpiStatistics.class).encode(dsProtocol, this);
+        return ok(result).build();
+    }
+
+    /**
+     * Gets the latest known flows statistics only.
+     *
+     * @return 200 OK with a known flow statistics
+     * @onos.rsModel KnownFlowStatistics
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("knownFlows")
+    public Response getKnownFlows(@QueryParam("topn") int topn) {
+        log.debug("getKnownFlows request with topn={}", topn);
+
+        DpiStatistics ds = service.getDpiStatisticsLatest();
+        DpiStatistics dsKnownFlows;
+
+        if (ds == null) {
+            dsKnownFlows = new DpiStatistics("", new DpiStatInfo());
+        } else if (topn <= 0) {
+            DpiStatInfo dsi = new DpiStatInfo();
+            dsi.setKnownFlows(ds.dpiStatInfo().knownFlows());
+            dsKnownFlows = new DpiStatistics(ds.receivedTime(), dsi);
+        } else {
+            if (topn > MAX_TOPN) {
+                topn = MAX_TOPN;
+            }
+            dsKnownFlows = new DpiStatistics(ds.receivedTime(), new DpiStatInfo());
+            List<FlowStatInfo> fsiList = ds.dpiStatInfo().knownFlows();
+            if (fsiList != null) {
+                // sorts known flow list with descending order based on bytes within topn
+                List<FlowStatInfo> fsiListSorted =
+                        fsiList.stream().sorted(FLOW_STAT_INFO_COMPARATOR).
+                                limit(topn).collect(Collectors.toList());
+                dsKnownFlows.dpiStatInfo().setKnownFlows(fsiListSorted);
+            }
+        }
+
+        ObjectNode result = codec(DpiStatistics.class).encode(dsKnownFlows, this);
+        return ok(result).build();
+    }
+
+    /**
+     * Gets the latest unknown flows statistics only.
+     *
+     * @return 200 OK with an unknown flow statistics
+     * @onos.rsModel UnknownFlowStatistics
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("unknownFlows")
+    public Response getUnknownFlows(@QueryParam("topn") int topn) {
+        log.debug("getUnknownFlows request with topn={}", topn);
+
+        DpiStatistics ds = service.getDpiStatisticsLatest();
+        DpiStatistics dsUnknownFlows;
+
+        if (ds == null) {
+            dsUnknownFlows = new DpiStatistics("", new DpiStatInfo());
+        } else if (topn <= 0) {
+            DpiStatInfo dsi = new DpiStatInfo();
+            dsi.setUnknownFlows(ds.dpiStatInfo().unknownFlows());
+            dsUnknownFlows = new DpiStatistics(ds.receivedTime(), dsi);
+        } else {
+            if (topn > 100) {
+                topn = 100;
+            }
+            dsUnknownFlows = new DpiStatistics(ds.receivedTime(), new DpiStatInfo());
+            List<FlowStatInfo> fsiList = ds.dpiStatInfo().unknownFlows();
+            if (fsiList != null) {
+                // sorts unknown flow list with descending order based on bytes within topn
+                List<FlowStatInfo> fsiListSorted =
+                        fsiList.stream().sorted(FLOW_STAT_INFO_COMPARATOR).
+                                limit(topn).collect(Collectors.toList());
+                dsUnknownFlows.dpiStatInfo().setUnknownFlows(fsiListSorted);
+            }
+        }
+
+        ObjectNode result = codec(DpiStatistics.class).encode(dsUnknownFlows, this);
+        return ok(result).build();
+    }
+
+    /**
+     * Add new dpi statistics entry at the end of list.
+     *
+     * @param stream dpi statistics JSON
+     * @return status of the request - CREATED if the JSON is correct,
+     * BAD_REQUEST if the JSON is invalid
+     * @onos.rsModel DpiStatisticsPost
+     */
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response addDpiStatistics(InputStream stream) {
+        ObjectNode result;
+
+        try {
+            ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
+            log.debug("jsonTree={}", jsonTree);
+
+            DpiStatistics ds = codec(DpiStatistics.class).decode(jsonTree, this);
+            if (ds == null) {
+                log.error("Wrong DpiStatistics json format error");
+            }
+
+            // TODO: check the validity of dpi statistics values, specially receivedTime format
+            DpiStatistics added = service.addDpiStatistics(ds);
+
+            result = codec(DpiStatistics.class).encode(added, this);
+        } catch (IOException ex) {
+            throw new IllegalArgumentException(ex);
+        }
+        return ok(result).build();
+    }
+}
diff --git a/web/api/src/main/resources/definitions/DpiStatistics.json b/web/api/src/main/resources/definitions/DpiStatistics.json
new file mode 100644
index 0000000..43f4679
--- /dev/null
+++ b/web/api/src/main/resources/definitions/DpiStatistics.json
@@ -0,0 +1,307 @@
+{
+  "type": "object",
+  "title": "dpiStatistics",
+  "required": [
+    "receivedTime",
+    "dpiStatInfo"
+  ],
+  "properties": {
+    "receivedTime": {
+      "type": "string",
+      "example": "2016-06-12 04:05:05"
+    },
+    "dpiStatInfo": {
+      "type": "object",
+      "title": "dpiStatInfo",
+      "required": [
+        "trafficStatistics",
+        "detectedProtos",
+        "knownFlows",
+        "unknownFlow"
+      ],
+      "properties": {
+        "trafficStatistics": {
+          "type": "object",
+          "title": "trafficStatistics",
+          "required": [
+            "ethernetBytes",
+            "discardedBytes",
+            "ipPackets",
+            "totalPackets",
+            "ipBytes",
+            "avgPktSize",
+            "uniqueFlows",
+            "tcpPackets",
+            "udpPackets",
+            "dpiThroughputPps",
+            "dpiThroughputBps",
+            "trafficThroughputPps",
+            "trafficThroughputBps",
+            "trafficDurationSec",
+            "guessedFlowProtos"
+          ],
+          "properties": {
+            "ethernetBytes": {
+              "type": "integer",
+              "format": "int64",
+              "example": 69889
+            },
+            "discardedBytes": {
+              "type": "integer",
+              "format": "int64",
+              "example": 69889
+            },
+            "ipPackets": {
+              "type": "integer",
+              "format": "int64",
+              "example": 69889
+            },
+            "totalPackets": {
+              "type": "integer",
+              "format": "int64",
+              "example": 69889
+            },
+            "ipBytes": {
+              "type": "integer",
+              "format": "int64",
+              "example": 69889
+            },
+            "avgPktSize": {
+              "type": "integer",
+              "format": "int32",
+              "example": 9889
+            },
+            "uniqueFlows": {
+              "type": "integer",
+              "format": "int32",
+              "example": 9889
+            },
+            "tcpPackets": {
+              "type": "integer",
+              "format": "int64",
+              "example": 69889
+            },
+            "udpPackets": {
+              "type": "integer",
+              "format": "int64",
+              "example": 69889
+            },
+            "dpiThroughputPps": {
+              "type": "number",
+              "format": "double",
+              "example": 69889.12
+            },
+            "dpiThroughputBps": {
+              "type": "number",
+              "format": "double",
+              "example": 69889.12
+            },
+            "trafficThroughputPps": {
+              "type": "number",
+              "format": "double",
+              "example": 69889.12
+            },
+            "trafficThroughputBps": {
+              "type": "number",
+              "format": "double",
+              "example": 69889.12
+            },
+            "trafficDurationSec": {
+              "type": "number",
+              "format": "double",
+              "example": 69889.12
+            },
+            "guessedFlowProtos": {
+              "type": "integer",
+              "format": "int32",
+              "example": 9889
+            }
+          },
+          "detectedProtos": {
+            "type": "array",
+            "xml": {
+              "name": "detectedProtos",
+              "wrapped": true
+            },
+            "items": {
+              "type": "object",
+              "title": "protos",
+              "required": [
+                "name",
+                "breed",
+                "packets",
+                "bytes",
+                "flows"
+              ],
+              "properties": {
+                "name": {
+                  "type": "string",
+                  "example": "TCP"
+                },
+                "breed": {
+                  "type": "string",
+                  "example": "Acceptable"
+                },
+                "packets": {
+                  "type": "integer",
+                  "format": "int64",
+                  "example": 69889
+                },
+                "bytes": {
+                  "type": "integer",
+                  "format": "int64",
+                  "example": 69889
+                },
+                "flows": {
+                  "type": "integer",
+                  "format": "int32",
+                  "example": 9889
+                }
+              }
+            }
+          },
+          "knownFlows": {
+            "type": "array",
+            "xml": {
+              "name": "knownFlows",
+              "wrapped": true
+            },
+            "items": {
+              "type": "object",
+              "title": "knownFlows",
+              "required": [
+                "protocol",
+                "hostAName",
+                "hostAPort",
+                "hostBName",
+                "hostBPort",
+                "detectedProtocol",
+                "detectedProtocolName",
+                "packets",
+                "bytes",
+                "hostServerName"
+              ],
+              "properties": {
+                "protocol": {
+                  "type": "string",
+                  "example": "TCP"
+                },
+                "hostAName": {
+                  "type": "string",
+                  "example": "10.0.20.50"
+                },
+                "hostAPort": {
+                  "type": "integer",
+                  "format": "int32",
+                  "example": 9889
+                },
+                "hostBName": {
+                  "type": "string",
+                  "example": "10.0.20.10"
+                },
+                "hostBPort": {
+                  "type": "integer",
+                  "format": "int32",
+                  "example": 8181
+                },
+                "detectedProtocol": {
+                  "type": "integer",
+                  "format": "int32",
+                  "example": 80
+                },
+                "detectedProtocolName": {
+                  "type": "string",
+                  "example": "HTTP"
+                },
+                "packets": {
+                  "type": "integer",
+                  "format": "int64",
+                  "example": 69889
+                },
+                "bytes": {
+                  "type": "integer",
+                  "format": "int64",
+                  "example": 69889
+                },
+                "hostSeverName": {
+                  "type": "string",
+                  "example": "raptor"
+                }
+              }
+            }
+          },
+          "unknownFlows": {
+            "type": "array",
+            "xml": {
+              "name": "unknownFlows",
+              "wrapped": true
+            },
+            "items": {
+              "type": "object",
+              "title": "unknownFlows",
+              "required": [
+                "protocol",
+                "hostAName",
+                "hostAPort",
+                "hostBName",
+                "hostBPort",
+                "detectedProtocol",
+                "detectedProtocolName",
+                "packets",
+                "bytes",
+                "hostServerName"
+              ],
+              "properties": {
+                "protocol": {
+                  "type": "string",
+                  "example": "TCP"
+                },
+                "hostAName": {
+                  "type": "string",
+                  "example": "10.0.20.50"
+                },
+                "hostAPort": {
+                  "type": "integer",
+                  "format": "int32",
+                  "example": 9889
+                },
+                "hostBName": {
+                  "type": "string",
+                  "example": "10.0.20.10"
+                },
+                "hostBPort": {
+                  "type": "integer",
+                  "format": "int32",
+                  "example": 8181
+                },
+                "detectedProtocol": {
+                  "type": "integer",
+                  "format": "int32",
+                  "example": 80
+                },
+                "detectedProtocolName": {
+                  "type": "string",
+                  "example": "HTTP"
+                },
+                "packets": {
+                  "type": "integer",
+                  "format": "int64",
+                  "example": 69889
+                },
+                "bytes": {
+                  "type": "integer",
+                  "format": "int64",
+                  "example": 69889
+                },
+                "hostSeverName": {
+                  "type": "string",
+                  "example": "raptor"
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/web/api/src/main/resources/definitions/DpiStatisticsPost.json b/web/api/src/main/resources/definitions/DpiStatisticsPost.json
new file mode 100644
index 0000000..43f4679
--- /dev/null
+++ b/web/api/src/main/resources/definitions/DpiStatisticsPost.json
@@ -0,0 +1,307 @@
+{
+  "type": "object",
+  "title": "dpiStatistics",
+  "required": [
+    "receivedTime",
+    "dpiStatInfo"
+  ],
+  "properties": {
+    "receivedTime": {
+      "type": "string",
+      "example": "2016-06-12 04:05:05"
+    },
+    "dpiStatInfo": {
+      "type": "object",
+      "title": "dpiStatInfo",
+      "required": [
+        "trafficStatistics",
+        "detectedProtos",
+        "knownFlows",
+        "unknownFlow"
+      ],
+      "properties": {
+        "trafficStatistics": {
+          "type": "object",
+          "title": "trafficStatistics",
+          "required": [
+            "ethernetBytes",
+            "discardedBytes",
+            "ipPackets",
+            "totalPackets",
+            "ipBytes",
+            "avgPktSize",
+            "uniqueFlows",
+            "tcpPackets",
+            "udpPackets",
+            "dpiThroughputPps",
+            "dpiThroughputBps",
+            "trafficThroughputPps",
+            "trafficThroughputBps",
+            "trafficDurationSec",
+            "guessedFlowProtos"
+          ],
+          "properties": {
+            "ethernetBytes": {
+              "type": "integer",
+              "format": "int64",
+              "example": 69889
+            },
+            "discardedBytes": {
+              "type": "integer",
+              "format": "int64",
+              "example": 69889
+            },
+            "ipPackets": {
+              "type": "integer",
+              "format": "int64",
+              "example": 69889
+            },
+            "totalPackets": {
+              "type": "integer",
+              "format": "int64",
+              "example": 69889
+            },
+            "ipBytes": {
+              "type": "integer",
+              "format": "int64",
+              "example": 69889
+            },
+            "avgPktSize": {
+              "type": "integer",
+              "format": "int32",
+              "example": 9889
+            },
+            "uniqueFlows": {
+              "type": "integer",
+              "format": "int32",
+              "example": 9889
+            },
+            "tcpPackets": {
+              "type": "integer",
+              "format": "int64",
+              "example": 69889
+            },
+            "udpPackets": {
+              "type": "integer",
+              "format": "int64",
+              "example": 69889
+            },
+            "dpiThroughputPps": {
+              "type": "number",
+              "format": "double",
+              "example": 69889.12
+            },
+            "dpiThroughputBps": {
+              "type": "number",
+              "format": "double",
+              "example": 69889.12
+            },
+            "trafficThroughputPps": {
+              "type": "number",
+              "format": "double",
+              "example": 69889.12
+            },
+            "trafficThroughputBps": {
+              "type": "number",
+              "format": "double",
+              "example": 69889.12
+            },
+            "trafficDurationSec": {
+              "type": "number",
+              "format": "double",
+              "example": 69889.12
+            },
+            "guessedFlowProtos": {
+              "type": "integer",
+              "format": "int32",
+              "example": 9889
+            }
+          },
+          "detectedProtos": {
+            "type": "array",
+            "xml": {
+              "name": "detectedProtos",
+              "wrapped": true
+            },
+            "items": {
+              "type": "object",
+              "title": "protos",
+              "required": [
+                "name",
+                "breed",
+                "packets",
+                "bytes",
+                "flows"
+              ],
+              "properties": {
+                "name": {
+                  "type": "string",
+                  "example": "TCP"
+                },
+                "breed": {
+                  "type": "string",
+                  "example": "Acceptable"
+                },
+                "packets": {
+                  "type": "integer",
+                  "format": "int64",
+                  "example": 69889
+                },
+                "bytes": {
+                  "type": "integer",
+                  "format": "int64",
+                  "example": 69889
+                },
+                "flows": {
+                  "type": "integer",
+                  "format": "int32",
+                  "example": 9889
+                }
+              }
+            }
+          },
+          "knownFlows": {
+            "type": "array",
+            "xml": {
+              "name": "knownFlows",
+              "wrapped": true
+            },
+            "items": {
+              "type": "object",
+              "title": "knownFlows",
+              "required": [
+                "protocol",
+                "hostAName",
+                "hostAPort",
+                "hostBName",
+                "hostBPort",
+                "detectedProtocol",
+                "detectedProtocolName",
+                "packets",
+                "bytes",
+                "hostServerName"
+              ],
+              "properties": {
+                "protocol": {
+                  "type": "string",
+                  "example": "TCP"
+                },
+                "hostAName": {
+                  "type": "string",
+                  "example": "10.0.20.50"
+                },
+                "hostAPort": {
+                  "type": "integer",
+                  "format": "int32",
+                  "example": 9889
+                },
+                "hostBName": {
+                  "type": "string",
+                  "example": "10.0.20.10"
+                },
+                "hostBPort": {
+                  "type": "integer",
+                  "format": "int32",
+                  "example": 8181
+                },
+                "detectedProtocol": {
+                  "type": "integer",
+                  "format": "int32",
+                  "example": 80
+                },
+                "detectedProtocolName": {
+                  "type": "string",
+                  "example": "HTTP"
+                },
+                "packets": {
+                  "type": "integer",
+                  "format": "int64",
+                  "example": 69889
+                },
+                "bytes": {
+                  "type": "integer",
+                  "format": "int64",
+                  "example": 69889
+                },
+                "hostSeverName": {
+                  "type": "string",
+                  "example": "raptor"
+                }
+              }
+            }
+          },
+          "unknownFlows": {
+            "type": "array",
+            "xml": {
+              "name": "unknownFlows",
+              "wrapped": true
+            },
+            "items": {
+              "type": "object",
+              "title": "unknownFlows",
+              "required": [
+                "protocol",
+                "hostAName",
+                "hostAPort",
+                "hostBName",
+                "hostBPort",
+                "detectedProtocol",
+                "detectedProtocolName",
+                "packets",
+                "bytes",
+                "hostServerName"
+              ],
+              "properties": {
+                "protocol": {
+                  "type": "string",
+                  "example": "TCP"
+                },
+                "hostAName": {
+                  "type": "string",
+                  "example": "10.0.20.50"
+                },
+                "hostAPort": {
+                  "type": "integer",
+                  "format": "int32",
+                  "example": 9889
+                },
+                "hostBName": {
+                  "type": "string",
+                  "example": "10.0.20.10"
+                },
+                "hostBPort": {
+                  "type": "integer",
+                  "format": "int32",
+                  "example": 8181
+                },
+                "detectedProtocol": {
+                  "type": "integer",
+                  "format": "int32",
+                  "example": 80
+                },
+                "detectedProtocolName": {
+                  "type": "string",
+                  "example": "HTTP"
+                },
+                "packets": {
+                  "type": "integer",
+                  "format": "int64",
+                  "example": 69889
+                },
+                "bytes": {
+                  "type": "integer",
+                  "format": "int64",
+                  "example": 69889
+                },
+                "hostSeverName": {
+                  "type": "string",
+                  "example": "raptor"
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/web/api/src/main/resources/definitions/KnownFlowStatistics.json b/web/api/src/main/resources/definitions/KnownFlowStatistics.json
new file mode 100644
index 0000000..0342b91
--- /dev/null
+++ b/web/api/src/main/resources/definitions/KnownFlowStatistics.json
@@ -0,0 +1,93 @@
+{
+  "type": "object",
+  "title": "dpiStatistics",
+  "required": [
+    "receivedTime",
+    "dpiStatInfo"
+  ],
+  "properties": {
+    "receivedTime": {
+      "type": "string",
+      "example": "2016-06-12 04:05:05"
+    },
+    "dpiStatInfo": {
+      "type": "object",
+      "title": "dpiStatInfo",
+      "required": [
+        "knownFlows"
+      ],
+      "properties": {
+        "knownFlows": {
+          "type": "array",
+          "xml": {
+            "name": "knownFlows",
+            "wrapped": true
+          },
+          "items": {
+            "type": "object",
+            "title": "knownFlows",
+            "required": [
+              "protocol",
+              "hostAName",
+              "hostAPort",
+              "hostBName",
+              "hostBPort",
+              "detectedProtocol",
+              "detectedProtocolName",
+              "packets",
+              "bytes",
+              "hostServerName"
+            ],
+            "properties": {
+              "protocol": {
+                "type": "string",
+                "example": "TCP"
+              },
+              "hostAName": {
+                "type": "string",
+                "example": "10.0.20.50"
+              },
+              "hostAPort": {
+                "type": "integer",
+                "format": "int32",
+                "example": 9889
+              },
+              "hostBName": {
+                "type": "string",
+                "example": "10.0.20.10"
+              },
+              "hostBPort": {
+                "type": "integer",
+                "format": "int32",
+                "example": 8181
+              },
+              "detectedProtocol": {
+                "type": "integer",
+                "format": "int32",
+                "example": 80
+              },
+              "detectedProtocolName": {
+                "type": "string",
+                "example": "HTTP"
+              },
+              "packets": {
+                "type": "integer",
+                "format": "int64",
+                "example": 69889
+              },
+              "bytes": {
+                "type": "integer",
+                "format": "int64",
+                "example": 69889
+              },
+              "hostSeverName": {
+                "type": "string",
+                "example": "raptor"
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/web/api/src/main/resources/definitions/ProtocolStatistics.json b/web/api/src/main/resources/definitions/ProtocolStatistics.json
new file mode 100644
index 0000000..9eb010e
--- /dev/null
+++ b/web/api/src/main/resources/definitions/ProtocolStatistics.json
@@ -0,0 +1,66 @@
+{
+  "type": "object",
+  "title": "protocolStatistics",
+  "required": [
+    "receivedTime",
+    "dpiStatInfo"
+  ],
+  "properties": {
+    "receivedTime": {
+      "type": "string",
+      "example": "2016-06-12 04:05:05"
+    },
+    "dpiStatInfo": {
+      "type": "object",
+      "title": "dpiStatInfo",
+      "required": [
+        "detectedProtos"
+      ],
+      "properties": {
+        "detectedProtos": {
+          "type": "array",
+          "xml": {
+            "name": "detectedProtos",
+            "wrapped": true
+          },
+          "items": {
+            "type": "object",
+            "title": "protos",
+            "required": [
+              "name",
+              "breed",
+              "packets",
+              "bytes",
+              "flows"
+            ],
+            "properties": {
+              "name": {
+                "type": "string",
+                "example": "TCP"
+              },
+              "breed": {
+                "type": "string",
+                "example": "Acceptable"
+              },
+              "packets": {
+                "type": "integer",
+                "format": "int64",
+                "example": 69889
+              },
+              "bytes": {
+                "type": "integer",
+                "format": "int64",
+                "example": 69889
+              },
+              "flows": {
+                "type": "integer",
+                "format": "int32",
+                "example": 9889
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/web/api/src/main/resources/definitions/TrafficStatistics.json b/web/api/src/main/resources/definitions/TrafficStatistics.json
new file mode 100644
index 0000000..4749be8
--- /dev/null
+++ b/web/api/src/main/resources/definitions/TrafficStatistics.json
@@ -0,0 +1,121 @@
+{
+  "type": "object",
+  "title": "TrafficStatistics",
+  "required": [
+    "receivedTime",
+    "dpiStatInfo"
+  ],
+  "properties": {
+    "receivedTime": {
+      "type": "string",
+      "example": "2016-06-12 04:05:05"
+    },
+    "dpiStatInfo": {
+      "type": "object",
+      "title": "dpiStatInfo",
+      "required": [
+        "trafficStatistics"
+      ],
+      "properties": {
+        "trafficStatistics": {
+          "type": "object",
+          "title": "trafficStatistics",
+          "required": [
+            "ethernetBytes",
+            "discardedBytes",
+            "ipPackets",
+            "totalPackets",
+            "ipBytes",
+            "avgPktSize",
+            "uniqueFlows",
+            "tcpPackets",
+            "udpPackets",
+            "dpiThroughputPps",
+            "dpiThroughputBps",
+            "trafficThroughputPps",
+            "trafficThroughputBps",
+            "trafficDurationSec",
+            "guessedFlowProtos"
+          ],
+          "properties": {
+            "ethernetBytes": {
+              "type": "integer",
+              "format": "int64",
+              "example": 69889
+            },
+            "discardedBytes": {
+              "type": "integer",
+              "format": "int64",
+              "example": 69889
+            },
+            "ipPackets": {
+              "type": "integer",
+              "format": "int64",
+              "example": 69889
+            },
+            "totalPackets": {
+              "type": "integer",
+              "format": "int64",
+              "example": 69889
+            },
+            "ipBytes": {
+              "type": "integer",
+              "format": "int64",
+              "example": 69889
+            },
+            "avgPktSize": {
+              "type": "integer",
+              "format": "int32",
+              "example": 9889
+            },
+            "uniqueFlows": {
+              "type": "integer",
+              "format": "int32",
+              "example": 9889
+            },
+            "tcpPackets": {
+              "type": "integer",
+              "format": "int64",
+              "example": 69889
+            },
+            "udpPackets": {
+              "type": "integer",
+              "format": "int64",
+              "example": 69889
+            },
+            "dpiThroughputPps": {
+              "type": "number",
+              "format": "double",
+              "example": 69889.12
+            },
+            "dpiThroughputBps": {
+              "type": "number",
+              "format": "double",
+              "example": 69889.12
+            },
+            "trafficThroughputPps": {
+              "type": "number",
+              "format": "double",
+              "example": 69889.12
+            },
+            "trafficThroughputBps": {
+              "type": "number",
+              "format": "double",
+              "example": 69889.12
+            },
+            "trafficDurationSec": {
+              "type": "number",
+              "format": "double",
+              "example": 69889.12
+            },
+            "guessedFlowProtos": {
+              "type": "integer",
+              "format": "int32",
+              "example": 9889
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/web/api/src/main/resources/definitions/UnknownFlowStatistics.json b/web/api/src/main/resources/definitions/UnknownFlowStatistics.json
new file mode 100644
index 0000000..0d9dc53
--- /dev/null
+++ b/web/api/src/main/resources/definitions/UnknownFlowStatistics.json
@@ -0,0 +1,93 @@
+{
+  "type": "object",
+  "title": "unknownFlowStatistics",
+  "required": [
+    "receivedTime",
+    "dpiStatInfo"
+  ],
+  "properties": {
+    "receivedTime": {
+      "type": "string",
+      "example": "2016-06-12 04:05:05"
+    },
+    "dpiStatInfo": {
+      "type": "object",
+      "title": "dpiStatInfo",
+      "required": [
+        "unknownFlow"
+      ],
+      "properties": {
+        "unknownFlows": {
+          "type": "array",
+          "xml": {
+            "name": "unknownFlows",
+            "wrapped": true
+          },
+          "items": {
+            "type": "object",
+            "title": "unknownFlows",
+            "required": [
+              "protocol",
+              "hostAName",
+              "hostAPort",
+              "hostBName",
+              "hostBPort",
+              "detectedProtocol",
+              "detectedProtocolName",
+              "packets",
+              "bytes",
+              "hostServerName"
+            ],
+            "properties": {
+              "protocol": {
+                "type": "string",
+                "example": "TCP"
+              },
+              "hostAName": {
+                "type": "string",
+                "example": "10.0.20.50"
+              },
+              "hostAPort": {
+                "type": "integer",
+                "format": "int32",
+                "example": 9889
+              },
+              "hostBName": {
+                "type": "string",
+                "example": "10.0.20.10"
+              },
+              "hostBPort": {
+                "type": "integer",
+                "format": "int32",
+                "example": 8181
+              },
+              "detectedProtocol": {
+                "type": "integer",
+                "format": "int32",
+                "example": 80
+              },
+              "detectedProtocolName": {
+                "type": "string",
+                "example": "HTTP"
+              },
+              "packets": {
+                "type": "integer",
+                "format": "int64",
+                "example": 69889
+              },
+              "bytes": {
+                "type": "integer",
+                "format": "int64",
+                "example": 69889
+              },
+              "hostSeverName": {
+                "type": "string",
+                "example": "raptor"
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}