[ONOS-6616] Revise VirtualPacketContext and DefaultVirtualPacketProvider

The constructor of VirtualPacketContext needs a parameter of DefaultVirtualPacketProvider type.
It is not flexible for us to use another packet provider to replace the default virtual packet provider.

To improve the code flexibility, I think it is better for us to use an interface type parameter in a method.

It alse seems redundant to use emit() method of DefaultVirtualPacketProvider in devirtualizeContext().
Thus, I think it will be more efficient to use core PacketService in VirtualPacketContext
when triger send() method.

Some other bugs are fixed.

Change-Id: I161a8929dc4e5a1d2ad716bc5da8b0b6f84340a9
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualPacketContext.java b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualPacketContext.java
new file mode 100644
index 0000000..54239e4
--- /dev/null
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualPacketContext.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2015-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.virtual;
+
+import org.onosproject.net.packet.PacketContext;
+
+/**
+ * Represents context for processing an inbound packet for a virtual network,
+ * and (optionally) emitting a corresponding outbound packet.
+ */
+public interface VirtualPacketContext extends PacketContext {
+    /**
+     * Returns the network identifier.
+     *
+     * @return network id
+     */
+    NetworkId networkId();
+}
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/VirtualPacketContext.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualPacketContext.java
similarity index 69%
rename from incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/VirtualPacketContext.java
rename to incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualPacketContext.java
index 9d03b0dc..1d59a10 100644
--- a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/VirtualPacketContext.java
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualPacketContext.java
@@ -17,18 +17,19 @@
 package org.onosproject.incubator.net.virtual.impl.provider;
 
 import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualPacketContext;
 import org.onosproject.net.packet.DefaultPacketContext;
 import org.onosproject.net.packet.InboundPacket;
 import org.onosproject.net.packet.OutboundPacket;
 
 /**
- * Represents context for processing an inbound packet for a virtual network
- * and (optionally) emitting a corresponding outbound packet.
- * The translation of context is handled by VirtualPacketProvider.
+ *Default implementation of a virtual packet context.
  */
-public class VirtualPacketContext extends DefaultPacketContext {
-    private DefaultVirtualPacketProvider dvpp;
+public class DefaultVirtualPacketContext extends DefaultPacketContext
+        implements VirtualPacketContext {
+
     private NetworkId networkId;
+    private DefaultVirtualPacketProvider dvpp;
 
     /**
      * Creates a new packet context.
@@ -38,13 +39,13 @@
      * @param outPkt outbound packet
      * @param block  whether the context is blocked or not
      * @param networkId virtual network ID where this context is handled
-     * @param dvpp The pointer for DefaultVirtualPacketProvider
+     * @param dvpp  pointer to default virtual packet provider
      */
 
-    protected VirtualPacketContext(long time, InboundPacket inPkt,
-                                   OutboundPacket outPkt, boolean block,
-                                   NetworkId networkId,
-                                   DefaultVirtualPacketProvider dvpp) {
+    protected DefaultVirtualPacketContext(long time, InboundPacket inPkt,
+                                          OutboundPacket outPkt, boolean block,
+                                          NetworkId networkId,
+                                          DefaultVirtualPacketProvider dvpp) {
         super(time, inPkt, outPkt, block);
 
         this.networkId = networkId;
@@ -54,11 +55,12 @@
     @Override
     public void send() {
         if (!this.block()) {
-            dvpp.devirtualizeContext(this);
+            dvpp.send(this);
         }
     }
 
-    public NetworkId getNetworkId() {
+    @Override
+    public NetworkId networkId() {
         return networkId;
     }
 }
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualPacketProvider.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualPacketProvider.java
index 5eaa3b0..ad81063 100644
--- a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualPacketProvider.java
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualPacketProvider.java
@@ -16,7 +16,6 @@
 
 package org.onosproject.incubator.net.virtual.impl.provider;
 
-import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -32,6 +31,7 @@
 import org.onosproject.incubator.net.virtual.VirtualDevice;
 import org.onosproject.incubator.net.virtual.VirtualNetwork;
 import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
+import org.onosproject.incubator.net.virtual.VirtualPacketContext;
 import org.onosproject.incubator.net.virtual.VirtualPort;
 import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProvider;
 import org.onosproject.incubator.net.virtual.provider.VirtualPacketProvider;
@@ -59,7 +59,6 @@
 import java.nio.ByteBuffer;
 import java.util.Dictionary;
 import java.util.HashSet;
-import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -87,10 +86,8 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected VirtualProviderRegistryService providerRegistryService;
 
-    ApplicationId appId;
-    InternalPacketProcessor processor;
-
-    private Map<VirtualPacketContext, PacketContext> contextMap;
+    private ApplicationId appId;
+    private InternalPacketProcessor processor;
 
     private Set<NetworkId> requestsSet = Sets.newHashSet();
 
@@ -106,8 +103,6 @@
         appId = coreService.registerApplication("org.onosproject.virtual.virtual-packet");
         providerRegistryService.registerProvider(this);
 
-        contextMap = Maps.newConcurrentMap();
-
         log.info("Started");
     }
 
@@ -128,7 +123,8 @@
 
     @Override
     public void emit(NetworkId networkId, OutboundPacket packet) {
-       packetService.emit(devirtualize(networkId, packet));
+       devirtualize(networkId, packet)
+               .forEach(outboundPacket -> packetService.emit(outboundPacket));
     }
 
     @Override
@@ -150,8 +146,21 @@
     }
 
     /**
+     * Send the outbound packet of a virtual context.
+     * This method is designed to support Context's send() method that invoked
+     * by applications.
+     * See {@link org.onosproject.net.packet.PacketContext}
+     *
+     * @param virtualPacketContext virtual packet context
+     */
+    protected void send(VirtualPacketContext virtualPacketContext) {
+        devirtualizeContext(virtualPacketContext)
+                .forEach(outboundPacket -> packetService.emit(outboundPacket));
+    }
+
+    /**
      * Translate the requested physical PacketContext into a virtual PacketContext.
-     * See {@link org.onosproject.net.packet.OutboundPacket}
+     * See {@link org.onosproject.net.packet.PacketContext}
      *
      * @param context A physical PacketContext be translated
      * @return A translated virtual PacketContext
@@ -177,12 +186,10 @@
                                               ByteBuffer.wrap(eth.serialize()));
 
             VirtualPacketContext vContext =
-                    new VirtualPacketContext(context.time(), inPacket, outPkt,
-                                             false, vPort.networkId(),
+                    new DefaultVirtualPacketContext(context.time(), inPacket, outPkt,
+                                             context.isHandled(), vPort.networkId(),
                                              this);
 
-            contextMap.put(vContext, context);
-
             return vContext;
         } else {
             return null;
@@ -222,33 +229,18 @@
     }
 
     /**
-     * Translate the requested a virtual outbound packet into
-     * a physical OutboundPacket.
-     * See {@link org.onosproject.net.packet.PacketContext}
+     * Translate the requested virtual outbound packet into
+     * a set of physical OutboundPacket.
+     * See {@link org.onosproject.net.packet.OutboundPacket}
      *
-     * @param packet A OutboundPacket to be translated
-     * @return de-virtualized (physical) OutboundPacket
+     * @param packet an OutboundPacket to be translated
+     * @return a set of de-virtualized (physical) OutboundPacket
      */
-    private OutboundPacket devirtualize(NetworkId networkId, OutboundPacket packet) {
+    private Set<OutboundPacket> devirtualize(NetworkId networkId, OutboundPacket packet) {
+        Set<OutboundPacket> outboundPackets = new HashSet<>();
         Set<VirtualPort> vPorts = vnaService
                 .getVirtualPorts(networkId, packet.sendThrough());
 
-        PortNumber vOutPortNum = packet.treatment().allInstructions().stream()
-                .filter(i -> i.type() == Instruction.Type.OUTPUT)
-                .map(i -> ((Instructions.OutputInstruction) i).port())
-                .findFirst().get();
-
-        Optional<ConnectPoint> optionalCpOut = vPorts.stream()
-                .filter(v -> v.number().equals(vOutPortNum))
-                .map(v -> v.realizedBy())
-                .findFirst();
-        if (!optionalCpOut.isPresent()) {
-            log.warn("Port {} is not realized yet, in Network {}, Device {}",
-                     vOutPortNum, networkId, packet.sendThrough());
-            return null;
-        }
-        ConnectPoint egressPoint = optionalCpOut.get();
-
         TrafficTreatment.Builder commonTreatmentBuilder
                 = DefaultTrafficTreatment.builder();
         packet.treatment().allInstructions().stream()
@@ -256,31 +248,69 @@
                 .forEach(i -> commonTreatmentBuilder.add(i));
         TrafficTreatment commonTreatment = commonTreatmentBuilder.build();
 
-        TrafficTreatment treatment = DefaultTrafficTreatment
-                .builder(commonTreatment)
-                .setOutput(egressPoint.port()).build();
+        PortNumber vOutPortNum = packet.treatment().allInstructions().stream()
+                .filter(i -> i.type() == Instruction.Type.OUTPUT)
+                .map(i -> ((Instructions.OutputInstruction) i).port())
+                .findFirst().get();
 
-        OutboundPacket outboundPacket = new DefaultOutboundPacket(
-                egressPoint.deviceId(), treatment, packet.data());
-        return outboundPacket;
+        if (!vOutPortNum.isLogical()) {
+            Optional<ConnectPoint> optionalCpOut = vPorts.stream()
+                    .filter(v -> v.number().equals(vOutPortNum))
+                    .map(v -> v.realizedBy())
+                    .findFirst();
+            if (!optionalCpOut.isPresent()) {
+                log.warn("Port {} is not realized yet, in Network {}, Device {}",
+                        vOutPortNum, networkId, packet.sendThrough());
+                return outboundPackets;
+            }
+            ConnectPoint egressPoint = optionalCpOut.get();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment
+                    .builder(commonTreatment)
+                    .setOutput(egressPoint.port()).build();
+
+            OutboundPacket outboundPacket = new DefaultOutboundPacket(
+                    egressPoint.deviceId(), treatment, packet.data());
+            outboundPackets.add(outboundPacket);
+        } else {
+            if (vOutPortNum == PortNumber.FLOOD) {
+                for (VirtualPort outPort : vPorts) {
+                    ConnectPoint cpOut = outPort.realizedBy();
+                    if (cpOut != null) {
+                        TrafficTreatment treatment = DefaultTrafficTreatment
+                                .builder(commonTreatment)
+                                .setOutput(cpOut.port()).build();
+                        OutboundPacket outboundPacket = new DefaultOutboundPacket(
+                                cpOut.deviceId(), treatment, packet.data());
+                        outboundPackets.add(outboundPacket);
+                    } else {
+                        log.warn("Port {} is not realized yet, in Network {}, Device {}",
+                                outPort.number(), networkId, packet.sendThrough());
+                    }
+                }
+            }
+        }
+
+        return outboundPackets;
     }
 
     /**
-     * Translate the requested a virtual Packet Context into
-     * a physical Packet Context.
-     * This method is designed to support Context's send() method that invoked
-     * by applications.
-     * See {@link org.onosproject.net.packet.PacketContext}
+     * Translate the requested virtual packet context into
+     * a set of physical outbound packets.
      *
-     * @param context A handled packet context
+     * @param context A handled virtual packet context
      */
-    public void devirtualizeContext(VirtualPacketContext context) {
-        NetworkId networkId = context.getNetworkId();
+    private Set<OutboundPacket> devirtualizeContext(VirtualPacketContext context) {
 
+        Set<OutboundPacket> outboundPackets = new HashSet<>();
+
+        NetworkId networkId = context.networkId();
         TrafficTreatment vTreatment = context.treatmentBuilder().build();
-
         DeviceId sendThrough = context.outPacket().sendThrough();
 
+        Set<VirtualPort> vPorts = vnaService
+                .getVirtualPorts(networkId, sendThrough);
+
         PortNumber vOutPortNum = vTreatment.allInstructions().stream()
                 .filter(i -> i.type() == Instruction.Type.OUTPUT)
                 .map(i -> ((Instructions.OutputInstruction) i).port())
@@ -294,20 +324,26 @@
         TrafficTreatment commonTreatment = commonTreatmentBuilder.build();
 
         if (!vOutPortNum.isLogical()) {
-            TrafficTreatment treatment = DefaultTrafficTreatment
-                    .builder()
-                    .addTreatment(commonTreatment)
-                    .setOutput(vOutPortNum)
-                    .build();
+            Optional<ConnectPoint> optionalCpOut = vPorts.stream()
+                    .filter(v -> v.number().equals(vOutPortNum))
+                    .map(v -> v.realizedBy())
+                    .findFirst();
+            if (!optionalCpOut.isPresent()) {
+                log.warn("Port {} is not realized yet, in Network {}, Device {}",
+                        vOutPortNum, networkId, sendThrough);
+                return outboundPackets;
+            }
+            ConnectPoint egressPoint = optionalCpOut.get();
 
-            emit(networkId, new DefaultOutboundPacket(sendThrough,
-                                                      treatment,
-                                                      context.outPacket().data()));
+            TrafficTreatment treatment = DefaultTrafficTreatment
+                    .builder(commonTreatment)
+                    .setOutput(egressPoint.port()).build();
+
+            OutboundPacket outboundPacket = new DefaultOutboundPacket(
+                    egressPoint.deviceId(), treatment, context.outPacket().data());
+            outboundPackets.add(outboundPacket);
         } else {
             if (vOutPortNum == PortNumber.FLOOD) {
-                Set<VirtualPort> vPorts = vnaService
-                        .getVirtualPorts(networkId, sendThrough);
-
                 Set<VirtualPort> outPorts = vPorts.stream()
                         .filter(vp -> !vp.number().isLogical())
                         .filter(vp -> vp.number() !=
@@ -315,18 +351,22 @@
                         .collect(Collectors.toSet());
 
                 for (VirtualPort outPort : outPorts) {
-                    TrafficTreatment treatment = DefaultTrafficTreatment
-                            .builder()
-                            .addTreatment(commonTreatment)
-                            .setOutput(outPort.number())
-                            .build();
-
-                    emit(networkId, new DefaultOutboundPacket(sendThrough,
-                                                   treatment,
-                                                   context.outPacket().data()));
+                    ConnectPoint cpOut = outPort.realizedBy();
+                    if (cpOut != null) {
+                        TrafficTreatment treatment = DefaultTrafficTreatment
+                                .builder(commonTreatment)
+                                .setOutput(cpOut.port()).build();
+                        OutboundPacket outboundPacket = new DefaultOutboundPacket(
+                                cpOut.deviceId(), treatment, context.outPacket().data());
+                        outboundPackets.add(outboundPacket);
+                    } else {
+                        log.warn("Port {} is not realized yet, in Network {}, Device {}",
+                                outPort.number(), networkId, sendThrough);
+                    }
                 }
             }
         }
+        return outboundPackets;
     }
 
     private final class InternalPacketProcessor implements PacketProcessor {
@@ -341,7 +381,7 @@
 
             VirtualPacketProviderService service =
                     (VirtualPacketProviderService) providerRegistryService
-                            .getProviderService(vContexts.getNetworkId(),
+                            .getProviderService(vContexts.networkId(),
                                                 VirtualPacketProvider.class);
             if (service != null) {
                 service.processPacket(vContexts);