REST API to create flows

Change-Id: I5d001782249c0eab249d7aa857ae465da95b5955
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 50a35c3..4b8c474 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
@@ -35,6 +35,7 @@
 import org.onosproject.net.Port;
 import org.onosproject.net.driver.Driver;
 import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.criteria.Criterion;
@@ -83,6 +84,7 @@
         registerCodec(Intent.class, new IntentCodec());
         registerCodec(ConnectivityIntent.class, new ConnectivityIntentCodec());
         registerCodec(FlowEntry.class, new FlowEntryCodec());
+        registerCodec(FlowRule.class, new FlowRuleCodec());
         registerCodec(TrafficTreatment.class, new TrafficTreatmentCodec());
         registerCodec(TrafficSelector.class, new TrafficSelectorCodec());
         registerCodec(Instruction.class, new InstructionCodec());
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/CriterionCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/CriterionCodec.java
index 1670361..98cd3bd 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/CriterionCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/CriterionCodec.java
@@ -15,43 +15,14 @@
  */
 package org.onosproject.codec.impl;
 
-import java.util.EnumMap;
-
 import org.onosproject.codec.CodecContext;
 import org.onosproject.codec.JsonCodec;
-import org.onosproject.net.OchSignal;
 import org.onosproject.net.flow.criteria.Criterion;
-import org.onosproject.net.flow.criteria.EthCriterion;
-import org.onosproject.net.flow.criteria.EthTypeCriterion;
-import org.onosproject.net.flow.criteria.IPCriterion;
-import org.onosproject.net.flow.criteria.IPDscpCriterion;
-import org.onosproject.net.flow.criteria.IPEcnCriterion;
-import org.onosproject.net.flow.criteria.IPProtocolCriterion;
-import org.onosproject.net.flow.criteria.IPv6ExthdrFlagsCriterion;
-import org.onosproject.net.flow.criteria.IPv6FlowLabelCriterion;
-import org.onosproject.net.flow.criteria.IPv6NDLinkLayerAddressCriterion;
-import org.onosproject.net.flow.criteria.IPv6NDTargetAddressCriterion;
-import org.onosproject.net.flow.criteria.IcmpCodeCriterion;
-import org.onosproject.net.flow.criteria.IcmpTypeCriterion;
-import org.onosproject.net.flow.criteria.Icmpv6CodeCriterion;
-import org.onosproject.net.flow.criteria.Icmpv6TypeCriterion;
-import org.onosproject.net.flow.criteria.MetadataCriterion;
-import org.onosproject.net.flow.criteria.MplsCriterion;
-import org.onosproject.net.flow.criteria.OchSignalCriterion;
-import org.onosproject.net.flow.criteria.OchSignalTypeCriterion;
-import org.onosproject.net.flow.criteria.PortCriterion;
-import org.onosproject.net.flow.criteria.SctpPortCriterion;
-import org.onosproject.net.flow.criteria.TcpPortCriterion;
-import org.onosproject.net.flow.criteria.UdpPortCriterion;
-import org.onosproject.net.flow.criteria.VlanIdCriterion;
-import org.onosproject.net.flow.criteria.VlanPcpCriterion;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-
 /**
  * Criterion codec.
  */
@@ -60,318 +31,46 @@
     protected static final Logger log =
             LoggerFactory.getLogger(CriterionCodec.class);
 
-    private final EnumMap<Criterion.Type, CriterionTypeFormatter> formatMap;
+    protected static final String TYPE = "type";
+    protected static final String ETH_TYPE = "ethType";
+    protected static final String MAC = "mac";
+    protected static final String PORT = "port";
+    protected static final String METADATA = "metadata";
 
-    public CriterionCodec() {
-        formatMap = new EnumMap<>(Criterion.Type.class);
-
-        formatMap.put(Criterion.Type.IN_PORT, new FormatInPort());
-        formatMap.put(Criterion.Type.IN_PHY_PORT, new FormatInPort());
-        formatMap.put(Criterion.Type.METADATA, new FormatMetadata());
-        formatMap.put(Criterion.Type.ETH_DST, new FormatEth());
-        formatMap.put(Criterion.Type.ETH_SRC, new FormatEth());
-        formatMap.put(Criterion.Type.ETH_TYPE, new FormatEthType());
-        formatMap.put(Criterion.Type.VLAN_VID, new FormatVlanVid());
-        formatMap.put(Criterion.Type.VLAN_PCP, new FormatVlanPcp());
-        formatMap.put(Criterion.Type.IP_DSCP, new FormatIpDscp());
-        formatMap.put(Criterion.Type.IP_ECN, new FormatIpEcn());
-        formatMap.put(Criterion.Type.IP_PROTO, new FormatIpProto());
-        formatMap.put(Criterion.Type.IPV4_SRC, new FormatIp());
-        formatMap.put(Criterion.Type.IPV4_DST, new FormatIp());
-        formatMap.put(Criterion.Type.TCP_SRC, new FormatTcp());
-        formatMap.put(Criterion.Type.TCP_DST, new FormatTcp());
-        formatMap.put(Criterion.Type.UDP_SRC, new FormatUdp());
-        formatMap.put(Criterion.Type.UDP_DST, new FormatUdp());
-        formatMap.put(Criterion.Type.SCTP_SRC, new FormatSctp());
-        formatMap.put(Criterion.Type.SCTP_DST, new FormatSctp());
-        formatMap.put(Criterion.Type.ICMPV4_TYPE, new FormatIcmpV4Type());
-        formatMap.put(Criterion.Type.ICMPV4_CODE, new FormatIcmpV4Code());
-        formatMap.put(Criterion.Type.IPV6_SRC, new FormatIp());
-        formatMap.put(Criterion.Type.IPV6_DST, new FormatIp());
-        formatMap.put(Criterion.Type.IPV6_FLABEL, new FormatIpV6FLabel());
-        formatMap.put(Criterion.Type.ICMPV6_TYPE, new FormatIcmpV6Type());
-        formatMap.put(Criterion.Type.ICMPV6_CODE, new FormatIcmpV6Code());
-        formatMap.put(Criterion.Type.IPV6_ND_TARGET, new FormatV6NDTarget());
-        formatMap.put(Criterion.Type.IPV6_ND_SLL, new FormatV6NDTll());
-        formatMap.put(Criterion.Type.IPV6_ND_TLL, new FormatV6NDTll());
-        formatMap.put(Criterion.Type.MPLS_LABEL, new FormatMplsLabel());
-        formatMap.put(Criterion.Type.IPV6_EXTHDR, new FormatIpV6Exthdr());
-        formatMap.put(Criterion.Type.OCH_SIGID, new FormatOchSigId());
-        formatMap.put(Criterion.Type.OCH_SIGTYPE, new FormatOchSigType());
-        formatMap.put(Criterion.Type.DUMMY, new FormatDummyType());
-
-        // Currently unimplemented
-        formatMap.put(Criterion.Type.ARP_OP, new FormatUnknown());
-        formatMap.put(Criterion.Type.ARP_SPA, new FormatUnknown());
-        formatMap.put(Criterion.Type.ARP_TPA, new FormatUnknown());
-        formatMap.put(Criterion.Type.ARP_SHA, new FormatUnknown());
-        formatMap.put(Criterion.Type.ARP_THA, new FormatUnknown());
-        formatMap.put(Criterion.Type.MPLS_TC, new FormatUnknown());
-        formatMap.put(Criterion.Type.MPLS_BOS, new FormatUnknown());
-        formatMap.put(Criterion.Type.PBB_ISID, new FormatUnknown());
-        formatMap.put(Criterion.Type.TUNNEL_ID, new FormatUnknown());
-        formatMap.put(Criterion.Type.UNASSIGNED_40, new FormatUnknown());
-        formatMap.put(Criterion.Type.PBB_UCA, new FormatUnknown());
-        formatMap.put(Criterion.Type.TCP_FLAGS, new FormatUnknown());
-        formatMap.put(Criterion.Type.ACTSET_OUTPUT, new FormatUnknown());
-        formatMap.put(Criterion.Type.PACKET_TYPE, new FormatUnknown());
-    }
-
-    private interface CriterionTypeFormatter {
-        ObjectNode encodeCriterion(ObjectNode root, Criterion criterion);
-    }
-
-    private static class FormatUnknown implements CriterionTypeFormatter {
-        @Override
-        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-            return root;
-        }
-    }
-
-    private static class FormatInPort implements CriterionTypeFormatter {
-        @Override
-        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-            final PortCriterion portCriterion = (PortCriterion) criterion;
-            return root.put("port", portCriterion.port().toLong());
-        }
-    }
-
-    private static class FormatMetadata implements CriterionTypeFormatter {
-        @Override
-        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-            final MetadataCriterion metadataCriterion =
-                    (MetadataCriterion) criterion;
-            return root.put("metadata", metadataCriterion.metadata());
-        }
-    }
-
-    private static class FormatEth implements CriterionTypeFormatter {
-        @Override
-        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-            final EthCriterion ethCriterion = (EthCriterion) criterion;
-            return root.put("mac", ethCriterion.mac().toString());
-        }
-    }
-
-    private static class FormatEthType implements CriterionTypeFormatter {
-        @Override
-        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-            final EthTypeCriterion ethTypeCriterion =
-                    (EthTypeCriterion) criterion;
-            return root.put("ethType", ethTypeCriterion.ethType());
-        }
-    }
-
-    private static class FormatVlanVid implements CriterionTypeFormatter {
-        @Override
-        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-            final VlanIdCriterion vlanIdCriterion =
-                    (VlanIdCriterion) criterion;
-            return root.put("vlanId", vlanIdCriterion.vlanId().toShort());
-        }
-    }
-
-    private static class FormatVlanPcp implements CriterionTypeFormatter {
-            @Override
-            public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-                final VlanPcpCriterion vlanPcpCriterion =
-                        (VlanPcpCriterion) criterion;
-                return root.put("priority", vlanPcpCriterion.priority());
-            }
-    }
-
-    private static class FormatIpDscp implements CriterionTypeFormatter {
-            @Override
-            public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-                final IPDscpCriterion ipDscpCriterion =
-                        (IPDscpCriterion) criterion;
-                return root.put("ipDscp", ipDscpCriterion.ipDscp());
-            }
-    }
-
-    private static class FormatIpEcn implements CriterionTypeFormatter {
-            @Override
-            public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-                final IPEcnCriterion ipEcnCriterion =
-                        (IPEcnCriterion) criterion;
-                return root.put("ipEcn", ipEcnCriterion.ipEcn());
-            }
-    }
-
-    private static class FormatIpProto implements CriterionTypeFormatter {
-        @Override
-        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-            final IPProtocolCriterion iPProtocolCriterion =
-                    (IPProtocolCriterion) criterion;
-            return root.put("protocol", iPProtocolCriterion.protocol());
-        }
-    }
-
-    private static class FormatIp implements CriterionTypeFormatter {
-            @Override
-            public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-                final IPCriterion iPCriterion = (IPCriterion) criterion;
-                return root.put("ip", iPCriterion.ip().toString());
-        }
-    }
-
-    private static class FormatTcp implements CriterionTypeFormatter {
-            @Override
-            public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-                final TcpPortCriterion tcpPortCriterion =
-                        (TcpPortCriterion) criterion;
-                return root.put("tcpPort", tcpPortCriterion.tcpPort());
-            }
-    }
-
-    private static class FormatUdp implements CriterionTypeFormatter {
-            @Override
-            public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-                final UdpPortCriterion udpPortCriterion =
-                        (UdpPortCriterion) criterion;
-                return root.put("udpPort", udpPortCriterion.udpPort());
-            }
-    }
-
-    private static class FormatSctp implements CriterionTypeFormatter {
-        @Override
-        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-            final SctpPortCriterion sctpPortCriterion =
-                    (SctpPortCriterion) criterion;
-            return root.put("sctpPort", sctpPortCriterion.sctpPort());
-        }
-    }
-
-    private static class FormatIcmpV4Type implements CriterionTypeFormatter {
-        @Override
-        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-            final IcmpTypeCriterion icmpTypeCriterion =
-                    (IcmpTypeCriterion) criterion;
-            return root.put("icmpType", icmpTypeCriterion.icmpType());
-        }
-    }
-
-    private static class FormatIcmpV4Code implements CriterionTypeFormatter {
-        @Override
-        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-            final IcmpCodeCriterion icmpCodeCriterion =
-                    (IcmpCodeCriterion) criterion;
-            return root.put("icmpCode", icmpCodeCriterion.icmpCode());
-        }
-    }
-
-    private static class FormatIpV6FLabel implements CriterionTypeFormatter {
-        @Override
-        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-            final IPv6FlowLabelCriterion ipv6FlowLabelCriterion =
-                    (IPv6FlowLabelCriterion) criterion;
-            return root.put("flowLabel", ipv6FlowLabelCriterion.flowLabel());
-        }
-    }
-
-    private static class FormatIcmpV6Type implements CriterionTypeFormatter {
-        @Override
-        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-            final Icmpv6TypeCriterion icmpv6TypeCriterion =
-                    (Icmpv6TypeCriterion) criterion;
-            return root.put("icmpv6Type", icmpv6TypeCriterion.icmpv6Type());
-        }
-    }
-
-    private static class FormatIcmpV6Code implements CriterionTypeFormatter {
-        @Override
-        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-            final Icmpv6CodeCriterion icmpv6CodeCriterion =
-                    (Icmpv6CodeCriterion) criterion;
-            return root.put("icmpv6Code", icmpv6CodeCriterion.icmpv6Code());
-        }
-    }
-
-    private static class FormatV6NDTarget implements CriterionTypeFormatter {
-        @Override
-        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-            final IPv6NDTargetAddressCriterion ipv6NDTargetAddressCriterion
-                = (IPv6NDTargetAddressCriterion) criterion;
-            return root.put("targetAddress", ipv6NDTargetAddressCriterion.targetAddress().toString());
-        }
-    }
-
-    private static class FormatV6NDTll implements CriterionTypeFormatter {
-        @Override
-        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-            final IPv6NDLinkLayerAddressCriterion ipv6NDLinkLayerAddressCriterion
-                = (IPv6NDLinkLayerAddressCriterion) criterion;
-            return root.put("mac", ipv6NDLinkLayerAddressCriterion.mac().toString());
-        }
-    }
-
-    private static class FormatMplsLabel implements CriterionTypeFormatter {
-        @Override
-        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-            final MplsCriterion mplsCriterion =
-                    (MplsCriterion) criterion;
-            return root.put("label", mplsCriterion.label().toInt());
-        }
-    }
-
-    private static class FormatIpV6Exthdr implements CriterionTypeFormatter {
-        @Override
-        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-            final IPv6ExthdrFlagsCriterion exthdrCriterion =
-                    (IPv6ExthdrFlagsCriterion) criterion;
-            return root.put("exthdrFlags", exthdrCriterion.exthdrFlags());
-        }
-    }
-
-    private static class FormatOchSigId implements CriterionTypeFormatter {
-        @Override
-        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-            OchSignal ochSignal = ((OchSignalCriterion) criterion).lambda();
-            ObjectNode child = root.putObject("ochSignalId");
-
-            child.put("gridType", ochSignal.gridType().name());
-            child.put("channelSpacing", ochSignal.channelSpacing().name());
-            child.put("spacingMultiplier", ochSignal.spacingMultiplier());
-            child.put("slotGranularity", ochSignal.slotGranularity());
-
-            return root;
-        }
-    }
-
-    private static class FormatOchSigType implements CriterionTypeFormatter {
-        @Override
-        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-            final OchSignalTypeCriterion ochSignalTypeCriterion =
-                    (OchSignalTypeCriterion) criterion;
-            return root.put("ochSignalType", ochSignalTypeCriterion.signalType().name());
-        }
-    }
-
-    private class FormatDummyType implements CriterionTypeFormatter {
-
-        @Override
-        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
-            checkNotNull(criterion, "Criterion cannot be null");
-
-            return root.put("type", criterion.type().toString());
-
-        }
-    }
+    protected static final String VLAN_ID = "vlanId";
+    protected static final String PRIORITY = "priority";
+    protected static final String IP_DSCP = "ipDscp";
+    protected static final String IP_ECN = "ipEcn";
+    protected static final String PROTOCOL = "protocol";
+    protected static final String IP = "ip";
+    protected static final String TCP_PORT = "tcpPort";
+    protected static final String UDP_PORT = "udpPort";
+    protected static final String SCTP_PORT = "sctpPort";
+    protected static final String ICMP_TYPE = "icmpType";
+    protected static final String ICMP_CODE = "icmpCode";
+    protected static final String FLOW_LABEL = "flowLabel";
+    protected static final String ICMPV6_TYPE = "icmpv6Type";
+    protected static final String ICMPV6_CODE = "icmpv6Code";
+    protected static final String TARGET_ADDRESS = "targetAddress";
+    protected static final String LABEL = "label";
+    protected static final String EXT_HDR_FLAGS = "exthdrFlags";
+    protected static final String LAMBDA = "lambda";
+    protected static final String GRID_TYPE = "gridType";
+    protected static final String CHANNEL_SPACING = "channelSpacing";
+    protected static final String SPACING_MULIPLIER = "spacingMultiplier";
+    protected static final String SLOT_GRANULARITY = "slotGranularity";
+    protected static final String OCH_SIGNAL_ID = "ochSignalId";
 
     @Override
     public ObjectNode encode(Criterion criterion, CodecContext context) {
-        checkNotNull(criterion, "Criterion cannot be null");
+        EncodeCriterionCodec encoder = new EncodeCriterionCodec(criterion, context);
+        return encoder.encode();
+    }
 
-        final ObjectNode result = context.mapper().createObjectNode()
-                .put("type", criterion.type().toString());
-
-        CriterionTypeFormatter formatter =
-                checkNotNull(
-                        formatMap.get(criterion.type()),
-                        "No formatter found for criterion type "
-                                + criterion.type().toString());
-
-        return formatter.encodeCriterion(result, criterion);
+    @Override
+    public Criterion decode(ObjectNode json, CodecContext context) {
+        DecodeCriterionCodec decoder = new DecodeCriterionCodec(json);
+        return decoder.decode();
     }
 
 
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/DecodeCriterionCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/DecodeCriterionCodec.java
new file mode 100644
index 0000000..27a0b40
--- /dev/null
+++ b/core/common/src/main/java/org/onosproject/codec/impl/DecodeCriterionCodec.java
@@ -0,0 +1,438 @@
+/*
+ * Copyright 2015 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.codec.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.MplsLabel;
+import org.onlab.packet.VlanId;
+import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.GridType;
+import org.onosproject.net.Lambda;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flow.criteria.Criterion;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import static org.onlab.util.Tools.nullIsIllegal;
+
+/**
+ * Decode portion of the criterion codec.
+ */
+public final class DecodeCriterionCodec {
+
+    private final ObjectNode json;
+
+    protected static final String MISSING_MEMBER_MESSAGE =
+            " member is required in Criterion";
+
+    private interface CriterionDecoder {
+        Criterion decodeCriterion(ObjectNode json);
+    }
+    private final Map<String, CriterionDecoder> decoderMap;
+
+    /**
+     * Creates a decode criterion codec object.
+     * Initializes the lookup map for criterion subclass decoders.
+     *
+     * @param json JSON object to decode
+     */
+    public DecodeCriterionCodec(ObjectNode json) {
+        this.json = json;
+        decoderMap = new HashMap<>();
+
+        decoderMap.put(Criterion.Type.IN_PORT.name(), new InPortDecoder());
+        decoderMap.put(Criterion.Type.IN_PHY_PORT.name(), new InPhyPortDecoder());
+        decoderMap.put(Criterion.Type.METADATA.name(), new MetadataDecoder());
+        decoderMap.put(Criterion.Type.ETH_DST.name(), new EthDstDecoder());
+        decoderMap.put(Criterion.Type.ETH_SRC.name(), new EthSrcDecoder());
+        decoderMap.put(Criterion.Type.ETH_TYPE.name(), new EthTypeDecoder());
+        decoderMap.put(Criterion.Type.VLAN_VID.name(), new VlanVidDecoder());
+        decoderMap.put(Criterion.Type.VLAN_PCP.name(), new VlanPcpDecoder());
+        decoderMap.put(Criterion.Type.IP_DSCP.name(), new IpDscpDecoder());
+        decoderMap.put(Criterion.Type.IP_ECN.name(), new IpEcnDecoder());
+        decoderMap.put(Criterion.Type.IP_PROTO.name(), new IpProtoDecoder());
+        decoderMap.put(Criterion.Type.IPV4_SRC.name(), new IpV4SrcDecoder());
+        decoderMap.put(Criterion.Type.IPV4_DST.name(), new IpV4DstDecoder());
+        decoderMap.put(Criterion.Type.TCP_SRC.name(), new TcpSrcDecoder());
+        decoderMap.put(Criterion.Type.TCP_DST.name(), new TcpDstDecoder());
+        decoderMap.put(Criterion.Type.UDP_SRC.name(), new UdpSrcDecoder());
+        decoderMap.put(Criterion.Type.UDP_DST.name(), new UdpDstDecoder());
+        decoderMap.put(Criterion.Type.SCTP_SRC.name(), new SctpSrcDecoder());
+        decoderMap.put(Criterion.Type.SCTP_DST.name(), new SctpDstDecoder());
+        decoderMap.put(Criterion.Type.ICMPV4_TYPE.name(), new IcmpV4TypeDecoder());
+        decoderMap.put(Criterion.Type.ICMPV4_CODE.name(), new IcmpV4CodeDecoder());
+        decoderMap.put(Criterion.Type.IPV6_SRC.name(), new IpV6SrcDecoder());
+        decoderMap.put(Criterion.Type.IPV6_DST.name(), new IpV6DstDecoder());
+        decoderMap.put(Criterion.Type.IPV6_FLABEL.name(), new IpV6FLabelDecoder());
+        decoderMap.put(Criterion.Type.ICMPV6_TYPE.name(), new IcmpV6TypeDecoder());
+        decoderMap.put(Criterion.Type.ICMPV6_CODE.name(), new IcmpV6CodeDecoder());
+        decoderMap.put(Criterion.Type.IPV6_ND_TARGET.name(), new V6NDTargetDecoder());
+        decoderMap.put(Criterion.Type.IPV6_ND_SLL.name(), new V6NDSllDecoder());
+        decoderMap.put(Criterion.Type.IPV6_ND_TLL.name(), new V6NDTllDecoder());
+        decoderMap.put(Criterion.Type.MPLS_LABEL.name(), new MplsLabelDecoder());
+        decoderMap.put(Criterion.Type.IPV6_EXTHDR.name(), new IpV6ExthdrDecoder());
+        decoderMap.put(Criterion.Type.OCH_SIGID.name(), new OchSigIdDecoder());
+        decoderMap.put(Criterion.Type.OCH_SIGTYPE.name(), new OchSigTypeDecoder());
+    }
+
+    private class EthTypeDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            int ethType = nullIsIllegal(json.get(CriterionCodec.ETH_TYPE),
+                    CriterionCodec.ETH_TYPE + MISSING_MEMBER_MESSAGE).asInt();
+            return Criteria.matchEthType(ethType);
+        }
+    }
+
+    private class EthDstDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            MacAddress mac = MacAddress.valueOf(nullIsIllegal(json.get(CriterionCodec.MAC),
+                    CriterionCodec.MAC + MISSING_MEMBER_MESSAGE).asText());
+
+            return Criteria.matchEthDst(mac);
+        }
+    }
+
+    private class EthSrcDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            MacAddress mac = MacAddress.valueOf(nullIsIllegal(json.get(CriterionCodec.MAC),
+                    CriterionCodec.MAC + MISSING_MEMBER_MESSAGE).asText());
+
+            return Criteria.matchEthSrc(mac);
+        }
+    }
+
+    private class InPortDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            PortNumber port = PortNumber.portNumber(nullIsIllegal(json.get(CriterionCodec.PORT),
+                    CriterionCodec.PORT + MISSING_MEMBER_MESSAGE).asLong());
+
+            return Criteria.matchInPort(port);
+        }
+    }
+
+    private class InPhyPortDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            PortNumber port = PortNumber.portNumber(nullIsIllegal(json.get(CriterionCodec.PORT),
+                    CriterionCodec.PORT + MISSING_MEMBER_MESSAGE).asLong());
+
+            return Criteria.matchInPhyPort(port);
+        }
+    }
+
+    private class MetadataDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            long metadata = nullIsIllegal(json.get(CriterionCodec.METADATA),
+                    CriterionCodec.METADATA + MISSING_MEMBER_MESSAGE).asLong();
+
+            return Criteria.matchMetadata(metadata);
+        }
+    }
+
+    private class VlanVidDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            short vlanId = (short) nullIsIllegal(json.get(CriterionCodec.VLAN_ID),
+                    CriterionCodec.VLAN_ID + MISSING_MEMBER_MESSAGE).asInt();
+
+            return Criteria.matchVlanId(VlanId.vlanId(vlanId));
+        }
+    }
+
+    private class VlanPcpDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            byte priority = (byte) nullIsIllegal(json.get(CriterionCodec.PRIORITY),
+                    CriterionCodec.VLAN_ID + MISSING_MEMBER_MESSAGE).asInt();
+
+            return Criteria.matchVlanPcp(priority);
+        }
+    }
+
+    private class IpDscpDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            byte ipDscp = (byte) nullIsIllegal(json.get(CriterionCodec.IP_DSCP),
+                    CriterionCodec.IP_DSCP + MISSING_MEMBER_MESSAGE).asInt();
+            return Criteria.matchIPDscp(ipDscp);
+        }
+    }
+
+    private class IpEcnDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            byte ipEcn = (byte) nullIsIllegal(json.get(CriterionCodec.IP_ECN),
+                    CriterionCodec.IP_ECN + MISSING_MEMBER_MESSAGE).asInt();
+            return Criteria.matchIPEcn(ipEcn);
+        }
+    }
+
+    private class IpProtoDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            short proto = (short) nullIsIllegal(json.get(CriterionCodec.PROTOCOL),
+                    CriterionCodec.PROTOCOL + MISSING_MEMBER_MESSAGE).asInt();
+            return Criteria.matchIPProtocol(proto);
+        }
+    }
+
+    private class IpV4SrcDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            String ip = nullIsIllegal(json.get(CriterionCodec.IP),
+                    CriterionCodec.IP + MISSING_MEMBER_MESSAGE).asText();
+            return Criteria.matchIPSrc(IpPrefix.valueOf(ip));
+        }
+    }
+
+    private class IpV4DstDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            String ip = nullIsIllegal(json.get(CriterionCodec.IP),
+                    CriterionCodec.IP + MISSING_MEMBER_MESSAGE).asText();
+            return Criteria.matchIPDst(IpPrefix.valueOf(ip));
+        }
+    }
+
+    private class IpV6SrcDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            String ip = nullIsIllegal(json.get(CriterionCodec.IP),
+                    CriterionCodec.IP + MISSING_MEMBER_MESSAGE).asText();
+            return Criteria.matchIPv6Src(IpPrefix.valueOf(ip));
+        }
+    }
+
+    private class IpV6DstDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            String ip = nullIsIllegal(json.get(CriterionCodec.IP),
+                    CriterionCodec.IP + MISSING_MEMBER_MESSAGE).asText();
+            return Criteria.matchIPv6Dst(IpPrefix.valueOf(ip));
+        }
+    }
+
+    private class TcpSrcDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            int tcpPort = nullIsIllegal(json.get(CriterionCodec.TCP_PORT),
+                    CriterionCodec.TCP_PORT + MISSING_MEMBER_MESSAGE).asInt();
+            return Criteria.matchTcpSrc(tcpPort);
+        }
+    }
+
+    private class TcpDstDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            int tcpPort = nullIsIllegal(json.get(CriterionCodec.TCP_PORT),
+                    CriterionCodec.TCP_PORT + MISSING_MEMBER_MESSAGE).asInt();
+            return Criteria.matchTcpDst(tcpPort);
+        }
+    }
+
+    private class UdpSrcDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            int udpPort = nullIsIllegal(json.get(CriterionCodec.UDP_PORT),
+                    CriterionCodec.UDP_PORT + MISSING_MEMBER_MESSAGE).asInt();
+            return Criteria.matchUdpSrc(udpPort);
+        }
+    }
+
+    private class UdpDstDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            int udpPort = nullIsIllegal(json.get(CriterionCodec.UDP_PORT),
+                    CriterionCodec.UDP_PORT + MISSING_MEMBER_MESSAGE).asInt();
+            return Criteria.matchUdpDst(udpPort);
+        }
+    }
+
+    private class SctpSrcDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            int sctpPort = nullIsIllegal(json.get(CriterionCodec.SCTP_PORT),
+                    CriterionCodec.SCTP_PORT + MISSING_MEMBER_MESSAGE).asInt();
+            return Criteria.matchSctpSrc(sctpPort);
+        }
+    }
+
+    private class SctpDstDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            int sctpPort = nullIsIllegal(json.get(CriterionCodec.SCTP_PORT),
+                    CriterionCodec.SCTP_PORT + MISSING_MEMBER_MESSAGE).asInt();
+            return Criteria.matchSctpDst(sctpPort);
+        }
+    }
+
+    private class IcmpV4TypeDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            short type = (short) nullIsIllegal(json.get(CriterionCodec.ICMP_TYPE),
+                    CriterionCodec.ICMP_TYPE + MISSING_MEMBER_MESSAGE).asInt();
+            return Criteria.matchIcmpType(type);
+        }
+    }
+
+    private class IcmpV4CodeDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            short code = (short) nullIsIllegal(json.get(CriterionCodec.ICMP_CODE),
+                    CriterionCodec.ICMP_CODE + MISSING_MEMBER_MESSAGE).asInt();
+            return Criteria.matchIcmpCode(code);
+        }
+    }
+
+    private class IpV6FLabelDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            int flowLabel = nullIsIllegal(json.get(CriterionCodec.FLOW_LABEL),
+                    CriterionCodec.FLOW_LABEL + MISSING_MEMBER_MESSAGE).asInt();
+            return Criteria.matchIPv6FlowLabel(flowLabel);
+        }
+    }
+
+    private class IcmpV6TypeDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            short type = (short) nullIsIllegal(json.get(CriterionCodec.ICMPV6_TYPE),
+                    CriterionCodec.ICMPV6_TYPE + MISSING_MEMBER_MESSAGE).asInt();
+            return Criteria.matchIcmpv6Type(type);
+        }
+    }
+
+    private class IcmpV6CodeDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            short code = (short) nullIsIllegal(json.get(CriterionCodec.ICMPV6_CODE),
+                    CriterionCodec.ICMPV6_CODE + MISSING_MEMBER_MESSAGE).asInt();
+            return Criteria.matchIcmpv6Code(code);
+        }
+    }
+
+    private class V6NDTargetDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            Ip6Address target = Ip6Address.valueOf(nullIsIllegal(json.get(CriterionCodec.TARGET_ADDRESS),
+                    CriterionCodec.TARGET_ADDRESS + MISSING_MEMBER_MESSAGE).asText());
+            return Criteria.matchIPv6NDTargetAddress(target);
+        }
+    }
+
+    private class V6NDSllDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            MacAddress mac = MacAddress.valueOf(nullIsIllegal(json.get(CriterionCodec.MAC),
+                    CriterionCodec.MAC + MISSING_MEMBER_MESSAGE).asText());
+            return Criteria.matchIPv6NDSourceLinkLayerAddress(mac);
+        }
+    }
+
+    private class V6NDTllDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            MacAddress mac = MacAddress.valueOf(nullIsIllegal(json.get(CriterionCodec.MAC),
+                    CriterionCodec.MAC + MISSING_MEMBER_MESSAGE).asText());
+            return Criteria.matchIPv6NDTargetLinkLayerAddress(mac);
+        }
+    }
+
+    private class MplsLabelDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            int label = nullIsIllegal(json.get(CriterionCodec.LABEL),
+                    CriterionCodec.LABEL + MISSING_MEMBER_MESSAGE).asInt();
+            return Criteria.matchMplsLabel(MplsLabel.mplsLabel(label));
+        }
+    }
+
+    private class IpV6ExthdrDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            int exthdrFlags = nullIsIllegal(json.get(CriterionCodec.EXT_HDR_FLAGS),
+                    CriterionCodec.EXT_HDR_FLAGS + MISSING_MEMBER_MESSAGE).asInt();
+            return Criteria.matchIPv6ExthdrFlags(exthdrFlags);
+        }
+    }
+
+    private class OchSigIdDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            if (json.get(CriterionCodec.LAMBDA) != null) {
+                Lambda lambda = Lambda.indexedLambda(nullIsIllegal(json.get(CriterionCodec.LAMBDA),
+                        CriterionCodec.LAMBDA + MISSING_MEMBER_MESSAGE).asInt());
+                return Criteria.matchLambda(lambda);
+            } else {
+                JsonNode ochSignalId = nullIsIllegal(json.get(CriterionCodec.OCH_SIGNAL_ID),
+                        CriterionCodec.GRID_TYPE + MISSING_MEMBER_MESSAGE);
+                GridType gridType =
+                        GridType.valueOf(
+                                nullIsIllegal(ochSignalId.get(CriterionCodec.GRID_TYPE),
+                                CriterionCodec.GRID_TYPE + MISSING_MEMBER_MESSAGE).asText());
+                ChannelSpacing channelSpacing =
+                        ChannelSpacing.valueOf(
+                                nullIsIllegal(ochSignalId.get(CriterionCodec.CHANNEL_SPACING),
+                                CriterionCodec.CHANNEL_SPACING + MISSING_MEMBER_MESSAGE).asText());
+                int spacingMultiplier = nullIsIllegal(ochSignalId.get(CriterionCodec.SPACING_MULIPLIER),
+                        CriterionCodec.SPACING_MULIPLIER + MISSING_MEMBER_MESSAGE).asInt();
+                int slotGranularity = nullIsIllegal(ochSignalId.get(CriterionCodec.SLOT_GRANULARITY),
+                        CriterionCodec.SLOT_GRANULARITY + MISSING_MEMBER_MESSAGE).asInt();
+                return Criteria.matchLambda(
+                        Lambda.ochSignal(gridType, channelSpacing,
+                                spacingMultiplier, slotGranularity));
+            }
+        }
+    }
+
+    private class OchSigTypeDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            return null;
+        }
+    }
+
+    /**
+     * Decodes the JSON into a criterion object.
+     *
+     * @return Criterion object
+     * @throws IllegalArgumentException if the JSON is invalid
+     */
+    public Criterion decode() {
+        String type = json.get(CriterionCodec.TYPE).asText();
+
+        CriterionDecoder decoder = decoderMap.get(type);
+        if (decoder != null) {
+            return decoder.decodeCriterion(json);
+        }
+
+        throw new IllegalArgumentException("Type " + type + " is unknown");
+    }
+
+
+}
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodec.java
new file mode 100644
index 0000000..48dfbdc
--- /dev/null
+++ b/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodec.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2015 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.codec.impl;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.MplsLabel;
+import org.onlab.packet.VlanId;
+import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.GridType;
+import org.onosproject.net.Lambda;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flow.instructions.L0ModificationInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import static org.onlab.util.Tools.nullIsIllegal;
+
+/**
+ * Decoding portion of the instruction codec.
+ */
+public final class DecodeInstructionCodec {
+    private final ObjectNode json;
+
+    /**
+     * Creates a decode instruction codec object.
+     *
+     * @param json JSON object to decode
+     */
+    public DecodeInstructionCodec(ObjectNode json) {
+        this.json = json;
+    }
+
+    /**
+     * Decodes a Layer 2 instruction.
+     *
+     * @return instruction object decoded from the JSON
+     * @throws IllegalArgumentException if the JSON is invalid
+     */
+    private Instruction decodeL2() {
+        String subType = json.get(InstructionCodec.SUBTYPE).asText();
+
+        if (subType.equals(L2ModificationInstruction.L2SubType.ETH_SRC.name())) {
+            String mac = nullIsIllegal(json.get(InstructionCodec.MAC),
+                    InstructionCodec.MAC + InstructionCodec.MISSING_MEMBER_MESSAGE).asText();
+            return Instructions.modL2Src(MacAddress.valueOf(mac));
+        } else if (subType.equals(L2ModificationInstruction.L2SubType.ETH_DST.name())) {
+            String mac = nullIsIllegal(json.get(InstructionCodec.MAC),
+                    InstructionCodec.MAC + InstructionCodec.MISSING_MEMBER_MESSAGE).asText();
+            return Instructions.modL2Dst(MacAddress.valueOf(mac));
+        } else if (subType.equals(L2ModificationInstruction.L2SubType.VLAN_ID.name())) {
+            short vlanId = (short) nullIsIllegal(json.get(InstructionCodec.VLAN_ID),
+                    InstructionCodec.VLAN_ID + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt();
+            return Instructions.modVlanId(VlanId.vlanId(vlanId));
+        } else if (subType.equals(L2ModificationInstruction.L2SubType.VLAN_PCP.name())) {
+            byte vlanPcp = (byte) nullIsIllegal(json.get(InstructionCodec.VLAN_PCP),
+                    InstructionCodec.VLAN_PCP + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt();
+            return Instructions.modVlanPcp(vlanPcp);
+        } else if (subType.equals(L2ModificationInstruction.L2SubType.MPLS_LABEL.name())) {
+            int label = nullIsIllegal(json.get(InstructionCodec.MPLS_LABEL),
+                    InstructionCodec.MPLS_LABEL + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt();
+            return Instructions.modMplsLabel(MplsLabel.mplsLabel(label));
+        } else if (subType.equals(L2ModificationInstruction.L2SubType.MPLS_PUSH.name())) {
+            return Instructions.pushMpls();
+        } else if (subType.equals(L2ModificationInstruction.L2SubType.MPLS_POP.name())) {
+            return Instructions.popMpls();
+        } else if (subType.equals(L2ModificationInstruction.L2SubType.DEC_MPLS_TTL.name())) {
+            return Instructions.decMplsTtl();
+        } else if (subType.equals(L2ModificationInstruction.L2SubType.VLAN_POP.name())) {
+            return Instructions.popVlan();
+        } else if (subType.equals(L2ModificationInstruction.L2SubType.VLAN_PUSH.name())) {
+            return Instructions.pushVlan();
+        }
+        throw new IllegalArgumentException("L2 Instruction subtype "
+                + subType + " is not supported");
+    }
+
+    /**
+     * Decodes a Layer 3 instruction.
+     *
+     * @return instruction object decoded from the JSON
+     * @throws IllegalArgumentException if the JSON is invalid
+     */
+    private Instruction decodeL3() {
+        String subType = json.get(InstructionCodec.SUBTYPE).asText();
+
+        if (subType.equals(L3ModificationInstruction.L3SubType.IPV4_SRC.name())) {
+            IpAddress ip = IpAddress.valueOf(nullIsIllegal(json.get(InstructionCodec.IP),
+                    InstructionCodec.IP + InstructionCodec.MISSING_MEMBER_MESSAGE).asText());
+            return Instructions.modL3Src(ip);
+        } else if (subType.equals(L3ModificationInstruction.L3SubType.IPV4_DST.name())) {
+            IpAddress ip = IpAddress.valueOf(nullIsIllegal(json.get(InstructionCodec.IP),
+                    InstructionCodec.IP + InstructionCodec.MISSING_MEMBER_MESSAGE).asText());
+            return Instructions.modL3Dst(ip);
+        } else if (subType.equals(L3ModificationInstruction.L3SubType.IPV6_SRC.name())) {
+            IpAddress ip = IpAddress.valueOf(nullIsIllegal(json.get(InstructionCodec.IP),
+                    InstructionCodec.IP + InstructionCodec.MISSING_MEMBER_MESSAGE).asText());
+            return Instructions.modL3IPv6Src(ip);
+        } else if (subType.equals(L3ModificationInstruction.L3SubType.IPV6_DST.name())) {
+            IpAddress ip = IpAddress.valueOf(nullIsIllegal(json.get(InstructionCodec.IP),
+                    InstructionCodec.IP + InstructionCodec.MISSING_MEMBER_MESSAGE).asText());
+            return Instructions.modL3IPv6Dst(ip);
+        } else if (subType.equals(L3ModificationInstruction.L3SubType.IPV6_FLABEL.name())) {
+            int flowLabel = nullIsIllegal(json.get(InstructionCodec.FLOW_LABEL),
+                    InstructionCodec.FLOW_LABEL + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt();
+            return Instructions.modL3IPv6FlowLabel(flowLabel);
+        }
+        throw new IllegalArgumentException("L3 Instruction subtype "
+                + subType + " is not supported");
+    }
+
+    /**
+     * Decodes a Layer 0 instruction.
+     *
+     * @return instruction object decoded from the JSON
+     * @throws IllegalArgumentException if the JSON is invalid
+     */
+    private Instruction decodeL0() {
+        String subType = json.get(InstructionCodec.SUBTYPE).asText();
+
+
+        if (subType.equals(L0ModificationInstruction.L0SubType.LAMBDA.name())) {
+            int lambda = nullIsIllegal(json.get(InstructionCodec.LAMBDA),
+                    InstructionCodec.LAMBDA + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt();
+            return Instructions.modL0Lambda(Lambda.indexedLambda(lambda));
+        } else if (subType.equals(L0ModificationInstruction.L0SubType.OCH.name())) {
+            String gridTypeString = nullIsIllegal(json.get(InstructionCodec.GRID_TYPE),
+                    InstructionCodec.GRID_TYPE + InstructionCodec.MISSING_MEMBER_MESSAGE).asText();
+            GridType gridType = GridType.valueOf(gridTypeString);
+            if (gridType == null) {
+                throw new IllegalArgumentException("Unknown grid type  "
+                        + gridTypeString);
+            }
+            String channelSpacingString = nullIsIllegal(json.get(InstructionCodec.CHANNEL_SPACING),
+                    InstructionCodec.CHANNEL_SPACING + InstructionCodec.MISSING_MEMBER_MESSAGE).asText();
+            ChannelSpacing channelSpacing = ChannelSpacing.valueOf(channelSpacingString);
+            if (channelSpacing == null) {
+                throw new IllegalArgumentException("Unknown channel spacing  "
+                        + channelSpacingString);
+            }
+            int spacingMultiplier = nullIsIllegal(json.get(InstructionCodec.SPACING_MULTIPLIER),
+                    InstructionCodec.SPACING_MULTIPLIER + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt();
+            int slotGranularity = nullIsIllegal(json.get(InstructionCodec.SLOT_GRANULARITY),
+                    InstructionCodec.SLOT_GRANULARITY + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt();
+            return Instructions.modL0Lambda(new OchSignal(gridType, channelSpacing,
+                    spacingMultiplier, slotGranularity));
+        }
+        throw new IllegalArgumentException("L0 Instruction subtype "
+                + subType + " is not supported");
+    }
+
+    /**
+     * Decodes the JSON into an instruction object.
+     *
+     * @return Criterion object
+     * @throws IllegalArgumentException if the JSON is invalid
+     */
+    public Instruction decode() {
+        String type = json.get(InstructionCodec.TYPE).asText();
+
+        if (type.equals(Instruction.Type.OUTPUT.name())) {
+            PortNumber portNumber =
+                    PortNumber.portNumber(nullIsIllegal(json.get(InstructionCodec.PORT),
+                            InstructionCodec.PORT + InstructionCodec.MISSING_MEMBER_MESSAGE).asLong());
+            return Instructions.createOutput(portNumber);
+        } else if (type.equals(Instruction.Type.DROP.name())) {
+            return Instructions.createDrop();
+        } else if (type.equals(Instruction.Type.L0MODIFICATION.name())) {
+            return decodeL0();
+        } else if (type.equals(Instruction.Type.L2MODIFICATION.name())) {
+            return decodeL2();
+        } else if (type.equals(Instruction.Type.L3MODIFICATION.name())) {
+            return decodeL3();
+        }
+        throw new IllegalArgumentException("Instruction type "
+                + type + " is not supported");
+    }
+
+}
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/EncodeCriterionCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/EncodeCriterionCodec.java
new file mode 100644
index 0000000..0ca344d
--- /dev/null
+++ b/core/common/src/main/java/org/onosproject/codec/impl/EncodeCriterionCodec.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright 2015 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.codec.impl;
+
+import java.util.EnumMap;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.EthCriterion;
+import org.onosproject.net.flow.criteria.EthTypeCriterion;
+import org.onosproject.net.flow.criteria.IPCriterion;
+import org.onosproject.net.flow.criteria.IPDscpCriterion;
+import org.onosproject.net.flow.criteria.IPEcnCriterion;
+import org.onosproject.net.flow.criteria.IPProtocolCriterion;
+import org.onosproject.net.flow.criteria.IPv6ExthdrFlagsCriterion;
+import org.onosproject.net.flow.criteria.IPv6FlowLabelCriterion;
+import org.onosproject.net.flow.criteria.IPv6NDLinkLayerAddressCriterion;
+import org.onosproject.net.flow.criteria.IPv6NDTargetAddressCriterion;
+import org.onosproject.net.flow.criteria.IcmpCodeCriterion;
+import org.onosproject.net.flow.criteria.IcmpTypeCriterion;
+import org.onosproject.net.flow.criteria.Icmpv6CodeCriterion;
+import org.onosproject.net.flow.criteria.Icmpv6TypeCriterion;
+import org.onosproject.net.flow.criteria.MetadataCriterion;
+import org.onosproject.net.flow.criteria.MplsCriterion;
+import org.onosproject.net.flow.criteria.OchSignalCriterion;
+import org.onosproject.net.flow.criteria.OchSignalTypeCriterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.criteria.SctpPortCriterion;
+import org.onosproject.net.flow.criteria.TcpPortCriterion;
+import org.onosproject.net.flow.criteria.UdpPortCriterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
+import org.onosproject.net.flow.criteria.VlanPcpCriterion;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Encode portion of the criterion codec.
+ */
+public final class EncodeCriterionCodec {
+
+    private final Criterion criterion;
+    private final CodecContext context;
+
+    private final EnumMap<Criterion.Type, CriterionTypeFormatter> formatMap;
+
+    /**
+     * Creates an encoder object for a criterion.
+     * Initializes the formatter lookup map for the criterion subclasses.
+     *
+     * @param criterion Criterion to encode
+     * @param context context of the JSON encoding
+     */
+    public EncodeCriterionCodec(Criterion criterion, CodecContext context) {
+        this.criterion = criterion;
+        this.context = context;
+
+        formatMap = new EnumMap<>(Criterion.Type.class);
+
+        formatMap.put(Criterion.Type.IN_PORT, new FormatInPort());
+        formatMap.put(Criterion.Type.IN_PHY_PORT, new FormatInPort());
+        formatMap.put(Criterion.Type.METADATA, new FormatMetadata());
+        formatMap.put(Criterion.Type.ETH_DST, new FormatEth());
+        formatMap.put(Criterion.Type.ETH_SRC, new FormatEth());
+        formatMap.put(Criterion.Type.ETH_TYPE, new FormatEthType());
+        formatMap.put(Criterion.Type.VLAN_VID, new FormatVlanVid());
+        formatMap.put(Criterion.Type.VLAN_PCP, new FormatVlanPcp());
+        formatMap.put(Criterion.Type.IP_DSCP, new FormatIpDscp());
+        formatMap.put(Criterion.Type.IP_ECN, new FormatIpEcn());
+        formatMap.put(Criterion.Type.IP_PROTO, new FormatIpProto());
+        formatMap.put(Criterion.Type.IPV4_SRC, new FormatIp());
+        formatMap.put(Criterion.Type.IPV4_DST, new FormatIp());
+        formatMap.put(Criterion.Type.TCP_SRC, new FormatTcp());
+        formatMap.put(Criterion.Type.TCP_DST, new FormatTcp());
+        formatMap.put(Criterion.Type.UDP_SRC, new FormatUdp());
+        formatMap.put(Criterion.Type.UDP_DST, new FormatUdp());
+        formatMap.put(Criterion.Type.SCTP_SRC, new FormatSctp());
+        formatMap.put(Criterion.Type.SCTP_DST, new FormatSctp());
+        formatMap.put(Criterion.Type.ICMPV4_TYPE, new FormatIcmpV4Type());
+        formatMap.put(Criterion.Type.ICMPV4_CODE, new FormatIcmpV4Code());
+        formatMap.put(Criterion.Type.IPV6_SRC, new FormatIp());
+        formatMap.put(Criterion.Type.IPV6_DST, new FormatIp());
+        formatMap.put(Criterion.Type.IPV6_FLABEL, new FormatIpV6FLabel());
+        formatMap.put(Criterion.Type.ICMPV6_TYPE, new FormatIcmpV6Type());
+        formatMap.put(Criterion.Type.ICMPV6_CODE, new FormatIcmpV6Code());
+        formatMap.put(Criterion.Type.IPV6_ND_TARGET, new FormatV6NDTarget());
+        formatMap.put(Criterion.Type.IPV6_ND_SLL, new FormatV6NDTll());
+        formatMap.put(Criterion.Type.IPV6_ND_TLL, new FormatV6NDTll());
+        formatMap.put(Criterion.Type.MPLS_LABEL, new FormatMplsLabel());
+        formatMap.put(Criterion.Type.IPV6_EXTHDR, new FormatIpV6Exthdr());
+        formatMap.put(Criterion.Type.OCH_SIGID, new FormatOchSigId());
+        formatMap.put(Criterion.Type.OCH_SIGTYPE, new FormatOchSigType());
+        formatMap.put(Criterion.Type.DUMMY, new FormatDummyType());
+
+        // Currently unimplemented
+        formatMap.put(Criterion.Type.ARP_OP, new FormatUnknown());
+        formatMap.put(Criterion.Type.ARP_SPA, new FormatUnknown());
+        formatMap.put(Criterion.Type.ARP_TPA, new FormatUnknown());
+        formatMap.put(Criterion.Type.ARP_SHA, new FormatUnknown());
+        formatMap.put(Criterion.Type.ARP_THA, new FormatUnknown());
+        formatMap.put(Criterion.Type.MPLS_TC, new FormatUnknown());
+        formatMap.put(Criterion.Type.MPLS_BOS, new FormatUnknown());
+        formatMap.put(Criterion.Type.PBB_ISID, new FormatUnknown());
+        formatMap.put(Criterion.Type.TUNNEL_ID, new FormatUnknown());
+        formatMap.put(Criterion.Type.UNASSIGNED_40, new FormatUnknown());
+        formatMap.put(Criterion.Type.PBB_UCA, new FormatUnknown());
+        formatMap.put(Criterion.Type.TCP_FLAGS, new FormatUnknown());
+        formatMap.put(Criterion.Type.ACTSET_OUTPUT, new FormatUnknown());
+        formatMap.put(Criterion.Type.PACKET_TYPE, new FormatUnknown());
+    }
+
+    private interface CriterionTypeFormatter {
+        ObjectNode encodeCriterion(ObjectNode root, Criterion criterion);
+    }
+
+    private static class FormatUnknown implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            return root;
+        }
+    }
+
+    private static class FormatInPort implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final PortCriterion portCriterion = (PortCriterion) criterion;
+            return root.put(CriterionCodec.PORT, portCriterion.port().toLong());
+        }
+    }
+
+    private static class FormatMetadata implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final MetadataCriterion metadataCriterion =
+                    (MetadataCriterion) criterion;
+            return root.put(CriterionCodec.METADATA, metadataCriterion.metadata());
+        }
+    }
+
+    private static class FormatEth implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final EthCriterion ethCriterion = (EthCriterion) criterion;
+            return root.put(CriterionCodec.MAC, ethCriterion.mac().toString());
+        }
+    }
+
+    private static class FormatEthType implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final EthTypeCriterion ethTypeCriterion =
+                    (EthTypeCriterion) criterion;
+            return root.put(CriterionCodec.ETH_TYPE, ethTypeCriterion.ethType());
+        }
+    }
+
+    private static class FormatVlanVid implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final VlanIdCriterion vlanIdCriterion =
+                    (VlanIdCriterion) criterion;
+            return root.put(CriterionCodec.VLAN_ID, vlanIdCriterion.vlanId().toShort());
+        }
+    }
+
+    private static class FormatVlanPcp implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final VlanPcpCriterion vlanPcpCriterion =
+                    (VlanPcpCriterion) criterion;
+            return root.put(CriterionCodec.PRIORITY, vlanPcpCriterion.priority());
+        }
+    }
+
+    private static class FormatIpDscp implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final IPDscpCriterion ipDscpCriterion =
+                    (IPDscpCriterion) criterion;
+            return root.put(CriterionCodec.IP_DSCP, ipDscpCriterion.ipDscp());
+        }
+    }
+
+    private static class FormatIpEcn implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final IPEcnCriterion ipEcnCriterion =
+                    (IPEcnCriterion) criterion;
+            return root.put(CriterionCodec.IP_ECN, ipEcnCriterion.ipEcn());
+        }
+    }
+
+    private static class FormatIpProto implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final IPProtocolCriterion iPProtocolCriterion =
+                    (IPProtocolCriterion) criterion;
+            return root.put(CriterionCodec.PROTOCOL, iPProtocolCriterion.protocol());
+        }
+    }
+
+    private static class FormatIp implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final IPCriterion iPCriterion = (IPCriterion) criterion;
+            return root.put(CriterionCodec.IP, iPCriterion.ip().toString());
+        }
+    }
+
+    private static class FormatTcp implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final TcpPortCriterion tcpPortCriterion =
+                    (TcpPortCriterion) criterion;
+            return root.put(CriterionCodec.TCP_PORT, tcpPortCriterion.tcpPort());
+        }
+    }
+
+    private static class FormatUdp implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final UdpPortCriterion udpPortCriterion =
+                    (UdpPortCriterion) criterion;
+            return root.put(CriterionCodec.UDP_PORT, udpPortCriterion.udpPort());
+        }
+    }
+
+    private static class FormatSctp implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final SctpPortCriterion sctpPortCriterion =
+                    (SctpPortCriterion) criterion;
+            return root.put(CriterionCodec.SCTP_PORT, sctpPortCriterion.sctpPort());
+        }
+    }
+
+    private static class FormatIcmpV4Type implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final IcmpTypeCriterion icmpTypeCriterion =
+                    (IcmpTypeCriterion) criterion;
+            return root.put(CriterionCodec.ICMP_TYPE, icmpTypeCriterion.icmpType());
+        }
+    }
+
+    private static class FormatIcmpV4Code implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final IcmpCodeCriterion icmpCodeCriterion =
+                    (IcmpCodeCriterion) criterion;
+            return root.put(CriterionCodec.ICMP_CODE, icmpCodeCriterion.icmpCode());
+        }
+    }
+
+    private static class FormatIpV6FLabel implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final IPv6FlowLabelCriterion ipv6FlowLabelCriterion =
+                    (IPv6FlowLabelCriterion) criterion;
+            return root.put(CriterionCodec.FLOW_LABEL, ipv6FlowLabelCriterion.flowLabel());
+        }
+    }
+
+    private static class FormatIcmpV6Type implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final Icmpv6TypeCriterion icmpv6TypeCriterion =
+                    (Icmpv6TypeCriterion) criterion;
+            return root.put(CriterionCodec.ICMPV6_TYPE, icmpv6TypeCriterion.icmpv6Type());
+        }
+    }
+
+    private static class FormatIcmpV6Code implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final Icmpv6CodeCriterion icmpv6CodeCriterion =
+                    (Icmpv6CodeCriterion) criterion;
+            return root.put(CriterionCodec.ICMPV6_CODE, icmpv6CodeCriterion.icmpv6Code());
+        }
+    }
+
+    private static class FormatV6NDTarget implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final IPv6NDTargetAddressCriterion ipv6NDTargetAddressCriterion
+                    = (IPv6NDTargetAddressCriterion) criterion;
+            return root.put(CriterionCodec.TARGET_ADDRESS, ipv6NDTargetAddressCriterion.targetAddress().toString());
+        }
+    }
+
+    private static class FormatV6NDTll implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final IPv6NDLinkLayerAddressCriterion ipv6NDLinkLayerAddressCriterion
+                    = (IPv6NDLinkLayerAddressCriterion) criterion;
+            return root.put(CriterionCodec.MAC, ipv6NDLinkLayerAddressCriterion.mac().toString());
+        }
+    }
+
+    private static class FormatMplsLabel implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final MplsCriterion mplsCriterion =
+                    (MplsCriterion) criterion;
+            return root.put(CriterionCodec.LABEL, mplsCriterion.label().toInt());
+        }
+    }
+
+    private static class FormatIpV6Exthdr implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final IPv6ExthdrFlagsCriterion exthdrCriterion =
+                    (IPv6ExthdrFlagsCriterion) criterion;
+            return root.put(CriterionCodec.EXT_HDR_FLAGS, exthdrCriterion.exthdrFlags());
+        }
+    }
+
+    private static class FormatOchSigId implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            OchSignal ochSignal = ((OchSignalCriterion) criterion).lambda();
+            ObjectNode child = root.putObject(CriterionCodec.OCH_SIGNAL_ID);
+
+            child.put(CriterionCodec.GRID_TYPE, ochSignal.gridType().name());
+            child.put(CriterionCodec.CHANNEL_SPACING, ochSignal.channelSpacing().name());
+            child.put(CriterionCodec.SPACING_MULIPLIER, ochSignal.spacingMultiplier());
+            child.put(CriterionCodec.SLOT_GRANULARITY, ochSignal.slotGranularity());
+
+            return root;
+        }
+    }
+
+    private static class FormatOchSigType implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final OchSignalTypeCriterion ochSignalTypeCriterion =
+                    (OchSignalTypeCriterion) criterion;
+            return root.put("ochSignalType", ochSignalTypeCriterion.signalType().name());
+        }
+    }
+
+    private class FormatDummyType implements CriterionTypeFormatter {
+
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            checkNotNull(criterion, "Criterion cannot be null");
+
+            return root.put(CriterionCodec.TYPE, criterion.type().toString());
+
+        }
+    }
+
+    /**
+     * Encodes a criterion into a JSON node.
+     *
+     * @return encoded JSON object for the given criterion
+     */
+    public ObjectNode encode() {
+        final ObjectNode result = context.mapper().createObjectNode()
+                .put(CriterionCodec.TYPE, criterion.type().toString());
+
+        CriterionTypeFormatter formatter =
+                checkNotNull(
+                        formatMap.get(criterion.type()),
+                        "No formatter found for criterion type "
+                                + criterion.type().toString());
+
+        return formatter.encodeCriterion(result, criterion);
+    }
+
+}
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodec.java
new file mode 100644
index 0000000..7fb56fd
--- /dev/null
+++ b/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodec.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2015 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.codec.impl;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flow.instructions.L0ModificationInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * JSON encoding of Instructions.
+ */
+public final class EncodeInstructionCodec {
+    protected static final Logger log = LoggerFactory.getLogger(EncodeInstructionCodec.class);
+    private final Instruction instruction;
+    private final CodecContext context;
+
+    /**
+     * Creates an instruction object encoder.
+     *
+     * @param instruction instruction to encode
+     * @param context codec context for the encoding
+     */
+    public EncodeInstructionCodec(Instruction instruction, CodecContext context) {
+        this.instruction = instruction;
+        this.context = context;
+    }
+
+
+    /**
+     * Encode an L0 modification instruction.
+     *
+     * @param result json node that the instruction attributes are added to
+     */
+    private void encodeL0(ObjectNode result) {
+        L0ModificationInstruction instruction =
+                (L0ModificationInstruction) this.instruction;
+        result.put(InstructionCodec.SUBTYPE, instruction.subtype().name());
+
+        switch (instruction.subtype()) {
+            case LAMBDA:
+                final L0ModificationInstruction.ModLambdaInstruction modLambdaInstruction =
+                        (L0ModificationInstruction.ModLambdaInstruction) instruction;
+                result.put(InstructionCodec.LAMBDA, modLambdaInstruction.lambda());
+                break;
+
+            case OCH:
+                L0ModificationInstruction.ModOchSignalInstruction ochSignalInstruction =
+                        (L0ModificationInstruction.ModOchSignalInstruction) instruction;
+                OchSignal ochSignal = ochSignalInstruction.lambda();
+                result.put(InstructionCodec.GRID_TYPE, ochSignal.gridType().name());
+                result.put(InstructionCodec.CHANNEL_SPACING, ochSignal.channelSpacing().name());
+                result.put(InstructionCodec.SPACING_MULTIPLIER, ochSignal.spacingMultiplier());
+                result.put(InstructionCodec.SLOT_GRANULARITY, ochSignal.slotGranularity());
+                break;
+
+            default:
+                log.info("Cannot convert L0 subtype of {}", instruction.subtype());
+        }
+    }
+
+    /**
+     * Encode an L2 modification instruction.
+     *
+     * @param result json node that the instruction attributes are added to
+     */
+    private void encodeL2(ObjectNode result) {
+        L2ModificationInstruction instruction =
+                (L2ModificationInstruction) this.instruction;
+        result.put(InstructionCodec.SUBTYPE, instruction.subtype().name());
+
+        switch (instruction.subtype()) {
+            case ETH_SRC:
+            case ETH_DST:
+                final L2ModificationInstruction.ModEtherInstruction modEtherInstruction =
+                        (L2ModificationInstruction.ModEtherInstruction) instruction;
+                result.put(InstructionCodec.MAC, modEtherInstruction.mac().toString());
+                break;
+
+            case VLAN_ID:
+                final L2ModificationInstruction.ModVlanIdInstruction modVlanIdInstruction =
+                        (L2ModificationInstruction.ModVlanIdInstruction) instruction;
+                result.put(InstructionCodec.VLAN_ID, modVlanIdInstruction.vlanId().toShort());
+                break;
+
+            case VLAN_PCP:
+                final L2ModificationInstruction.ModVlanPcpInstruction modVlanPcpInstruction =
+                        (L2ModificationInstruction.ModVlanPcpInstruction) instruction;
+                result.put(InstructionCodec.VLAN_PCP, modVlanPcpInstruction.vlanPcp());
+                break;
+
+            case MPLS_LABEL:
+                final L2ModificationInstruction.ModMplsLabelInstruction modMplsLabelInstruction =
+                        (L2ModificationInstruction.ModMplsLabelInstruction) instruction;
+                result.put(InstructionCodec.MPLS_LABEL, modMplsLabelInstruction.label());
+                break;
+
+            case MPLS_PUSH:
+                final L2ModificationInstruction.PushHeaderInstructions pushHeaderInstructions =
+                        (L2ModificationInstruction.PushHeaderInstructions) instruction;
+
+                result.put(InstructionCodec.ETHERNET_TYPE, pushHeaderInstructions.ethernetType());
+                break;
+
+            default:
+                log.info("Cannot convert L2 subtype of {}", instruction.subtype());
+                break;
+        }
+    }
+
+    /**
+     * Encode an L3 modification instruction.
+     *
+     * @param result json node that the instruction attributes are added to
+     */
+    private void encodeL3(ObjectNode result) {
+        L3ModificationInstruction instruction =
+                (L3ModificationInstruction) this.instruction;
+        result.put(InstructionCodec.SUBTYPE, instruction.subtype().name());
+        switch (instruction.subtype()) {
+            case IPV4_SRC:
+            case IPV4_DST:
+            case IPV6_SRC:
+            case IPV6_DST:
+                final L3ModificationInstruction.ModIPInstruction modIPInstruction =
+                        (L3ModificationInstruction.ModIPInstruction) instruction;
+                result.put(InstructionCodec.IP, modIPInstruction.ip().toString());
+                break;
+
+            case IPV6_FLABEL:
+                final L3ModificationInstruction.ModIPv6FlowLabelInstruction
+                        modFlowLabelInstruction =
+                        (L3ModificationInstruction.ModIPv6FlowLabelInstruction) instruction;
+                result.put(InstructionCodec.FLOW_LABEL, modFlowLabelInstruction.flowLabel());
+                break;
+
+            default:
+                log.info("Cannot convert L3 subtype of {}", instruction.subtype());
+                break;
+        }
+    }
+
+    /**
+     * Encodes the given instruction into JSON.
+     *
+     * @return JSON object node representing the instruction
+     */
+    public ObjectNode encode() {
+        final ObjectNode result = context.mapper().createObjectNode()
+                .put(InstructionCodec.TYPE, instruction.type().toString());
+
+        switch (instruction.type()) {
+            case OUTPUT:
+                final Instructions.OutputInstruction outputInstruction =
+                        (Instructions.OutputInstruction) instruction;
+                result.put(InstructionCodec.PORT, outputInstruction.port().toLong());
+                break;
+
+            case DROP:
+                break;
+
+            case L0MODIFICATION:
+                encodeL0(result);
+                break;
+
+            case L2MODIFICATION:
+                encodeL2(result);
+                break;
+
+            case L3MODIFICATION:
+                encodeL3(result);
+                break;
+
+            default:
+                log.info("Cannot convert instruction type of {}", instruction.type());
+                break;
+        }
+        return result;
+    }
+
+}
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/FlowRuleCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/FlowRuleCodec.java
new file mode 100644
index 0000000..94a1948
--- /dev/null
+++ b/core/common/src/main/java/org/onosproject/codec/impl/FlowRuleCodec.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2015 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.codec.impl;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import static org.onlab.util.Tools.nullIsIllegal;
+
+/**
+ * Flow rule JSON codec.
+ */
+public final class FlowRuleCodec extends JsonCodec<FlowRule> {
+
+    private static final String APP_ID = "appId";
+    private static final String PRIORITY = "priority";
+    private static final String TIMEOUT = "timeout";
+    private static final String IS_PERMANENT = "isPermanent";
+    private static final String DEVICE_ID = "deviceId";
+    private static final String TREATMENT = "treatment";
+    private static final String SELECTOR = "selector";
+    private static final String MISSING_MEMBER_MESSAGE =
+            " member is required in FlowRule";
+
+
+    @Override
+    public FlowRule decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        FlowRule.Builder resultBuilder = new DefaultFlowRule.Builder();
+
+        short appId = nullIsIllegal(json.get(APP_ID),
+                APP_ID + MISSING_MEMBER_MESSAGE).shortValue();
+        CoreService coreService = context.getService(CoreService.class);
+        resultBuilder.fromApp(coreService.getAppId(appId));
+
+        int priority = nullIsIllegal(json.get(PRIORITY),
+                PRIORITY + MISSING_MEMBER_MESSAGE).asInt();
+        resultBuilder.withPriority(priority);
+
+        boolean isPermanent = nullIsIllegal(json.get(IS_PERMANENT),
+                IS_PERMANENT + MISSING_MEMBER_MESSAGE).asBoolean();
+        if (isPermanent) {
+            resultBuilder.makePermanent();
+        } else {
+            resultBuilder.makeTemporary(nullIsIllegal(json.get(TIMEOUT),
+                            TIMEOUT
+                            + MISSING_MEMBER_MESSAGE
+                            + " if the flow is temporary").asInt());
+        }
+
+        DeviceId deviceId = DeviceId.deviceId(nullIsIllegal(json.get(DEVICE_ID),
+                DEVICE_ID + MISSING_MEMBER_MESSAGE).asText());
+        resultBuilder.forDevice(deviceId);
+
+        ObjectNode treatmentJson = (ObjectNode) json.get(TREATMENT);
+        if (treatmentJson != null) {
+            JsonCodec<TrafficTreatment> treatmentCodec =
+                    context.codec(TrafficTreatment.class);
+            resultBuilder.withTreatment(treatmentCodec.decode(treatmentJson, context));
+        }
+
+        ObjectNode selectorJson = (ObjectNode) json.get(SELECTOR);
+        if (selectorJson != null) {
+            JsonCodec<TrafficSelector> selectorCodec =
+                    context.codec(TrafficSelector.class);
+            resultBuilder.withSelector(selectorCodec.decode(selectorJson, context));
+        }
+
+        return resultBuilder.build();
+    }
+}
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/InstructionCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/InstructionCodec.java
index ef8968c..e903dfa 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/InstructionCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/InstructionCodec.java
@@ -17,12 +17,7 @@
 
 import org.onosproject.codec.CodecContext;
 import org.onosproject.codec.JsonCodec;
-import org.onosproject.net.OchSignal;
 import org.onosproject.net.flow.instructions.Instruction;
-import org.onosproject.net.flow.instructions.Instructions;
-import org.onosproject.net.flow.instructions.L0ModificationInstruction;
-import org.onosproject.net.flow.instructions.L2ModificationInstruction;
-import org.onosproject.net.flow.instructions.L3ModificationInstruction;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -37,159 +32,39 @@
 
     protected static final Logger log = LoggerFactory.getLogger(InstructionCodec.class);
 
-    /**
-     * Encode an L0 modification instruction.
-     *
-     * @param result json node that the instruction attributes are added to
-     * @param instruction The L0 instruction
-     */
-    private void encodeL0(ObjectNode result, L0ModificationInstruction instruction) {
-        result.put("subtype", instruction.subtype().name());
+    protected static final String TYPE = "type";
+    protected static final String SUBTYPE = "subtype";
+    protected static final String PORT = "port";
+    protected static final String MAC = "mac";
+    protected static final String VLAN_ID = "vlanId";
+    protected static final String VLAN_PCP = "vlanPcp";
+    protected static final String MPLS_LABEL = "label";
+    protected static final String IP = "ip";
+    protected static final String FLOW_LABEL = "flowLabel";
+    protected static final String LAMBDA = "lambda";
+    protected static final String GRID_TYPE = "gridType";
+    protected static final String CHANNEL_SPACING = "channelSpacing";
+    protected static final String SPACING_MULTIPLIER = "spacingMultiplier";
+    protected static final String SLOT_GRANULARITY = "slotGranularity";
+    protected static final String ETHERNET_TYPE = "ethernetType";
 
-        switch (instruction.subtype()) {
-            case LAMBDA:
-                final L0ModificationInstruction.ModLambdaInstruction modLambdaInstruction =
-                        (L0ModificationInstruction.ModLambdaInstruction) instruction;
-                result.put("lambda", modLambdaInstruction.lambda());
-                break;
+    protected static final String MISSING_MEMBER_MESSAGE =
+            " member is required in Instruction";
 
-            case OCH:
-                L0ModificationInstruction.ModOchSignalInstruction ochSignalInstruction =
-                        (L0ModificationInstruction.ModOchSignalInstruction) instruction;
-                OchSignal ochSignal = ochSignalInstruction.lambda();
-                result.put("gridType", ochSignal.gridType().name());
-                result.put("channelSpacing", ochSignal.channelSpacing().name());
-                result.put("spacingMultiplier", ochSignal.spacingMultiplier());
-                result.put("slotGranularity", ochSignal.slotGranularity());
-                break;
-
-            default:
-                log.info("Cannot convert L0 subtype of {}", instruction.subtype());
-        }
-    }
-
-    /**
-     * Encode an L2 modification instruction.
-     *
-     * @param result json node that the instruction attributes are added to
-     * @param instruction The L2 instruction
-     * @param context context of the request
-     */
-    private void encodeL2(ObjectNode result,
-                          L2ModificationInstruction instruction,
-                          CodecContext context) {
-        result.put("subtype", instruction.subtype().name());
-
-        switch (instruction.subtype()) {
-            case ETH_SRC:
-            case ETH_DST:
-                final L2ModificationInstruction.ModEtherInstruction modEtherInstruction =
-                        (L2ModificationInstruction.ModEtherInstruction) instruction;
-                result.put("mac", modEtherInstruction.mac().toString());
-                break;
-
-            case VLAN_ID:
-                final L2ModificationInstruction.ModVlanIdInstruction modVlanIdInstruction =
-                        (L2ModificationInstruction.ModVlanIdInstruction) instruction;
-                result.put("vlanId", modVlanIdInstruction.vlanId().toShort());
-                break;
-
-            case VLAN_PCP:
-                final L2ModificationInstruction.ModVlanPcpInstruction modVlanPcpInstruction =
-                        (L2ModificationInstruction.ModVlanPcpInstruction) instruction;
-                result.put("vlanPcp", modVlanPcpInstruction.vlanPcp());
-                break;
-
-            case MPLS_LABEL:
-                final L2ModificationInstruction.ModMplsLabelInstruction modMplsLabelInstruction =
-                        (L2ModificationInstruction.ModMplsLabelInstruction) instruction;
-                result.put("label", modMplsLabelInstruction.label());
-                break;
-
-            case MPLS_PUSH:
-                final L2ModificationInstruction.PushHeaderInstructions pushHeaderInstructions =
-                        (L2ModificationInstruction.PushHeaderInstructions) instruction;
-
-                result.put("ethernetType", pushHeaderInstructions.ethernetType());
-                break;
-
-            default:
-                log.info("Cannot convert L2 subtype of {}", instruction.subtype());
-                break;
-        }
-    }
-
-    /**
-     * Encode an L3 modification instruction.
-     *
-     * @param result json node that the instruction attributes are added to
-     * @param instruction The L3 instruction
-     */
-    private void encodeL3(ObjectNode result, L3ModificationInstruction instruction) {
-        result.put("subtype", instruction.subtype().name());
-        switch (instruction.subtype()) {
-            case IPV4_SRC:
-            case IPV4_DST:
-            case IPV6_SRC:
-            case IPV6_DST:
-                final L3ModificationInstruction.ModIPInstruction modIPInstruction =
-                        (L3ModificationInstruction.ModIPInstruction) instruction;
-                result.put("ip", modIPInstruction.ip().toString());
-                break;
-
-            case IPV6_FLABEL:
-                final L3ModificationInstruction.ModIPv6FlowLabelInstruction
-                    modFlowLabelInstruction =
-                        (L3ModificationInstruction.ModIPv6FlowLabelInstruction) instruction;
-                result.put("flowLabel", modFlowLabelInstruction.flowLabel());
-                break;
-
-            default:
-                log.info("Cannot convert L3 subtype of {}", instruction.subtype());
-                break;
-        }
-    }
 
     @Override
     public ObjectNode encode(Instruction instruction, CodecContext context) {
         checkNotNull(instruction, "Instruction cannot be null");
 
-        final ObjectNode result = context.mapper().createObjectNode()
-                .put("type", instruction.type().toString());
+        return new EncodeInstructionCodec(instruction, context).encode();
+    }
 
-
-        switch (instruction.type()) {
-            case OUTPUT:
-                final Instructions.OutputInstruction outputInstruction =
-                        (Instructions.OutputInstruction) instruction;
-                result.put("port", outputInstruction.port().toLong());
-                break;
-
-            case DROP:
-                break;
-
-            case L0MODIFICATION:
-                final L0ModificationInstruction l0ModificationInstruction =
-                        (L0ModificationInstruction) instruction;
-                encodeL0(result, l0ModificationInstruction);
-                break;
-
-            case L2MODIFICATION:
-                final L2ModificationInstruction l2ModificationInstruction =
-                        (L2ModificationInstruction) instruction;
-                encodeL2(result, l2ModificationInstruction, context);
-                break;
-
-            case L3MODIFICATION:
-                final L3ModificationInstruction l3ModificationInstruction =
-                        (L3ModificationInstruction) instruction;
-                encodeL3(result, l3ModificationInstruction);
-                break;
-
-            default:
-                log.info("Cannot convert instruction type of {}", instruction.type());
-                break;
+    @Override
+    public Instruction decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
         }
-        return result;
+
+        return new DecodeInstructionCodec(json).decode();
     }
 }
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/TrafficSelectorCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/TrafficSelectorCodec.java
index 29b1969..eea9fae 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/TrafficSelectorCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/TrafficSelectorCodec.java
@@ -15,11 +15,15 @@
  */
 package org.onosproject.codec.impl;
 
+import java.util.stream.IntStream;
+
 import org.onosproject.codec.CodecContext;
 import org.onosproject.codec.JsonCodec;
+import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.criteria.Criterion;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
@@ -29,12 +33,14 @@
  * Traffic selector codec.
  */
 public final class TrafficSelectorCodec extends JsonCodec<TrafficSelector> {
+    private static final String CRITERIA = "criteria";
+
     @Override
     public ObjectNode encode(TrafficSelector selector, CodecContext context) {
         checkNotNull(selector, "Traffic selector cannot be null");
 
         final ObjectNode result = context.mapper().createObjectNode();
-        final ArrayNode jsonCriteria = result.putArray("criteria");
+        final ArrayNode jsonCriteria = result.putArray(CRITERIA);
 
         if (selector.criteria() != null) {
             final JsonCodec<Criterion> criterionCodec =
@@ -46,4 +52,20 @@
 
         return result;
     }
+
+    @Override
+    public TrafficSelector decode(ObjectNode json, CodecContext context) {
+        final JsonCodec<Criterion> criterionCodec =
+                context.codec(Criterion.class);
+
+        JsonNode criteriaJson = json.get(CRITERIA);
+        TrafficSelector.Builder builder = DefaultTrafficSelector.builder();
+        if (criteriaJson != null) {
+            IntStream.range(0, criteriaJson.size())
+                    .forEach(i -> builder.add(
+                            criterionCodec.decode((ObjectNode) criteriaJson.get(i),
+                                    context)));
+        }
+        return builder.build();
+    }
 }
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/TrafficTreatmentCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/TrafficTreatmentCodec.java
index 14a74d9..edfe10c 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/TrafficTreatmentCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/TrafficTreatmentCodec.java
@@ -15,11 +15,15 @@
  */
 package org.onosproject.codec.impl;
 
+import java.util.stream.IntStream;
+
 import org.onosproject.codec.CodecContext;
 import org.onosproject.codec.JsonCodec;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.instructions.Instruction;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
@@ -29,12 +33,14 @@
  * Traffic treatment codec.
  */
 public final class TrafficTreatmentCodec extends JsonCodec<TrafficTreatment> {
+    private static final String INSTRUCTIONS = "instructions";
+
     @Override
     public ObjectNode encode(TrafficTreatment treatment, CodecContext context) {
         checkNotNull(treatment, "Traffic treatment cannot be null");
 
         final ObjectNode result = context.mapper().createObjectNode();
-        final ArrayNode jsonInstructions = result.putArray("instructions");
+        final ArrayNode jsonInstructions = result.putArray(INSTRUCTIONS);
 
         final JsonCodec<Instruction> instructionCodec =
                 context.codec(Instruction.class);
@@ -51,4 +57,20 @@
 
         return result;
     }
+
+    @Override
+    public TrafficTreatment decode(ObjectNode json, CodecContext context) {
+        final JsonCodec<Instruction> instructionsCodec =
+                context.codec(Instruction.class);
+
+        JsonNode instructionsJson = json.get(INSTRUCTIONS);
+        TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
+        if (instructionsJson != null) {
+            IntStream.range(0, instructionsJson.size())
+                    .forEach(i -> builder.add(
+                            instructionsCodec.decode((ObjectNode) instructionsJson.get(i),
+                                    context)));
+        }
+        return builder.build();
+    }
 }
diff --git a/core/common/src/test/java/org/onosproject/codec/impl/CriterionCodecTest.java b/core/common/src/test/java/org/onosproject/codec/impl/CriterionCodecTest.java
index 919719c..abd77ad 100644
--- a/core/common/src/test/java/org/onosproject/codec/impl/CriterionCodecTest.java
+++ b/core/common/src/test/java/org/onosproject/codec/impl/CriterionCodecTest.java
@@ -72,8 +72,10 @@
      */
     @Test
     public void checkCriterionTypes() throws Exception {
+        EncodeCriterionCodec encoder = new EncodeCriterionCodec(
+                Criteria.dummy(), context);
         EnumMap<Criterion.Type, Object> formatMap =
-                getField(criterionCodec, "formatMap");
+                getField(encoder, "formatMap");
         assertThat(formatMap, notNullValue());
 
         for (Criterion.Type type : Criterion.Type.values()) {
diff --git a/core/common/src/test/java/org/onosproject/codec/impl/FlowRuleCodecTest.java b/core/common/src/test/java/org/onosproject/codec/impl/FlowRuleCodecTest.java
new file mode 100644
index 0000000..5eaa20e
--- /dev/null
+++ b/core/common/src/test/java/org/onosproject/codec/impl/FlowRuleCodecTest.java
@@ -0,0 +1,506 @@
+/*
+ * Copyright 2015 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.codec.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.MplsLabel;
+import org.onlab.packet.VlanId;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.GridType;
+import org.onosproject.net.Lambda;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.EthCriterion;
+import org.onosproject.net.flow.criteria.EthTypeCriterion;
+import org.onosproject.net.flow.criteria.IPCriterion;
+import org.onosproject.net.flow.criteria.IPDscpCriterion;
+import org.onosproject.net.flow.criteria.IPEcnCriterion;
+import org.onosproject.net.flow.criteria.IPProtocolCriterion;
+import org.onosproject.net.flow.criteria.IPv6ExthdrFlagsCriterion;
+import org.onosproject.net.flow.criteria.IPv6FlowLabelCriterion;
+import org.onosproject.net.flow.criteria.IPv6NDLinkLayerAddressCriterion;
+import org.onosproject.net.flow.criteria.IPv6NDTargetAddressCriterion;
+import org.onosproject.net.flow.criteria.IcmpCodeCriterion;
+import org.onosproject.net.flow.criteria.IcmpTypeCriterion;
+import org.onosproject.net.flow.criteria.Icmpv6CodeCriterion;
+import org.onosproject.net.flow.criteria.Icmpv6TypeCriterion;
+import org.onosproject.net.flow.criteria.IndexedLambdaCriterion;
+import org.onosproject.net.flow.criteria.MplsCriterion;
+import org.onosproject.net.flow.criteria.OchSignalCriterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.criteria.SctpPortCriterion;
+import org.onosproject.net.flow.criteria.TcpPortCriterion;
+import org.onosproject.net.flow.criteria.UdpPortCriterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
+import org.onosproject.net.flow.criteria.VlanPcpCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flow.instructions.L0ModificationInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.onosproject.net.NetTestTools.APP_ID;
+
+/**
+ * Flow rule codec unit tests.
+ */
+public class FlowRuleCodecTest {
+
+    MockCodecContext context;
+    JsonCodec<FlowRule> flowRuleCodec;
+    final CoreService mockCoreService = createMock(CoreService.class);
+
+    /**
+     * Sets up for each test.  Creates a context and fetches the flow rule
+     * codec.
+     */
+    @Before
+    public void setUp() {
+        context = new MockCodecContext();
+        flowRuleCodec = context.codec(FlowRule.class);
+        assertThat(flowRuleCodec, notNullValue());
+
+        expect(mockCoreService.getAppId(APP_ID.id()))
+                .andReturn(APP_ID).anyTimes();
+        replay(mockCoreService);
+        context.registerService(CoreService.class, mockCoreService);
+    }
+
+    /**
+     * Reads in a rule from the given resource and decodes it.
+     *
+     * @param resourceName resource to use to read the JSON for the rule
+     * @return decoded flow rule
+     * @throws IOException if processing the resource fails
+     */
+    private FlowRule getRule(String resourceName) throws IOException {
+        InputStream jsonStream = FlowRuleCodecTest.class
+                .getResourceAsStream(resourceName);
+        JsonNode json = context.mapper().readTree(jsonStream);
+        assertThat(json, notNullValue());
+        FlowRule rule = flowRuleCodec.decode((ObjectNode) json, context);
+        assertThat(rule, notNullValue());
+        return rule;
+    }
+
+    /**
+     * Checks that the data shared by all the resources is correct for a
+     * given rule.
+     *
+     * @param rule rule to check
+     */
+    private void checkCommonData(FlowRule rule) {
+        assertThat(rule.appId(), is(APP_ID.id()));
+        assertThat(rule.isPermanent(), is(false));
+        assertThat(rule.timeout(), is(1));
+        assertThat(rule.priority(), is(1));
+        assertThat(rule.deviceId().toString(), is("of:0000000000000001"));
+    }
+
+    /**
+     * Checks that a simple rule decodes properly.
+     *
+     * @throws IOException if the resource cannot be processed
+     */
+    @Test
+    public void codecSimpleFlowTest() throws IOException {
+        FlowRule rule = getRule("simple-flow.json");
+
+        checkCommonData(rule);
+
+        assertThat(rule.selector().criteria().size(), is(1));
+        Criterion criterion1 = rule.selector().criteria().iterator().next();
+        assertThat(criterion1.type(), is(Criterion.Type.ETH_TYPE));
+        assertThat(((EthTypeCriterion) criterion1).ethType(), is(2054));
+
+        assertThat(rule.treatment().allInstructions().size(), is(1));
+        Instruction instruction1 = rule.treatment().allInstructions().get(0);
+        assertThat(instruction1.type(), is(Instruction.Type.OUTPUT));
+        assertThat(((Instructions.OutputInstruction) instruction1).port(), is(PortNumber.CONTROLLER));
+    }
+
+    SortedMap<String, Instruction> instructions = new TreeMap<>();
+
+    /**
+     * Looks up an instruction in the instruction map based on type and subtype.
+     *
+     * @param type type string
+     * @param subType subtype string
+     * @return instruction that matches
+     */
+    private Instruction getInstruction(Instruction.Type type, String subType) {
+        Instruction instruction = instructions.get(type.name() + "/" + subType);
+        assertThat(instruction, notNullValue());
+        assertThat(instruction.type(), is(type));
+        return instruction;
+    }
+
+    /**
+     * Checks that a rule with one of each instruction type decodes properly.
+     *
+     * @throws IOException if the resource cannot be processed
+     */
+    @Test
+    public void decodeInstructionsFlowTest() throws Exception {
+        FlowRule rule = getRule("instructions-flow.json");
+
+        checkCommonData(rule);
+
+        rule.treatment().allInstructions()
+                .stream()
+                .forEach(instruction ->
+                {
+                    String subType;
+                    if (instruction.type() == Instruction.Type.L0MODIFICATION) {
+                        subType = ((L0ModificationInstruction) instruction)
+                                .subtype().name();
+                    } else if (instruction.type() == Instruction.Type.L2MODIFICATION) {
+                        subType = ((L2ModificationInstruction) instruction)
+                                .subtype().name();
+                    } else if (instruction.type() == Instruction.Type.L3MODIFICATION) {
+                        subType = ((L3ModificationInstruction) instruction)
+                                .subtype().name();
+                    } else {
+                        subType = "";
+                    }
+                    instructions.put(
+                            instruction.type().name() + "/" + subType, instruction);
+                });
+
+        assertThat(rule.treatment().allInstructions().size(), is(19));
+
+        Instruction instruction;
+
+        instruction = getInstruction(Instruction.Type.OUTPUT, "");
+        assertThat(instruction.type(), is(Instruction.Type.OUTPUT));
+        assertThat(((Instructions.OutputInstruction) instruction).port(), is(PortNumber.CONTROLLER));
+
+        instruction = getInstruction(Instruction.Type.L2MODIFICATION,
+                L2ModificationInstruction.L2SubType.ETH_SRC.name());
+        assertThat(instruction.type(), is(Instruction.Type.L2MODIFICATION));
+        assertThat(((L2ModificationInstruction.ModEtherInstruction) instruction).mac(),
+                is(MacAddress.valueOf("12:34:56:78:90:12")));
+
+        instruction = getInstruction(Instruction.Type.L2MODIFICATION,
+                L2ModificationInstruction.L2SubType.ETH_DST.name());
+        assertThat(instruction.type(), is(Instruction.Type.L2MODIFICATION));
+        assertThat(((L2ModificationInstruction.ModEtherInstruction) instruction).mac(),
+                is(MacAddress.valueOf("98:76:54:32:01:00")));
+
+        instruction = getInstruction(Instruction.Type.L2MODIFICATION,
+                L2ModificationInstruction.L2SubType.VLAN_ID.name());
+        assertThat(instruction.type(), is(Instruction.Type.L2MODIFICATION));
+        assertThat(((L2ModificationInstruction.ModVlanIdInstruction) instruction).vlanId().toShort(),
+                is((short) 22));
+
+        instruction = getInstruction(Instruction.Type.L2MODIFICATION,
+                L2ModificationInstruction.L2SubType.VLAN_PCP.name());
+        assertThat(instruction.type(), is(Instruction.Type.L2MODIFICATION));
+        assertThat(((L2ModificationInstruction.ModVlanPcpInstruction) instruction).vlanPcp(),
+                is((byte) 1));
+
+        instruction = getInstruction(Instruction.Type.L2MODIFICATION,
+                L2ModificationInstruction.L2SubType.MPLS_LABEL.name());
+        assertThat(instruction.type(), is(Instruction.Type.L2MODIFICATION));
+        assertThat(((L2ModificationInstruction.ModMplsLabelInstruction) instruction)
+                        .label().shortValue(),
+                is((short) 777));
+
+        instruction = getInstruction(Instruction.Type.L2MODIFICATION,
+                L2ModificationInstruction.L2SubType.MPLS_PUSH.name());
+        assertThat(instruction.type(), is(Instruction.Type.L2MODIFICATION));
+        assertThat((short) ((L2ModificationInstruction.PushHeaderInstructions) instruction)
+                        .ethernetType(),
+                is(Ethernet.MPLS_UNICAST));
+
+        instruction = getInstruction(Instruction.Type.L2MODIFICATION,
+                L2ModificationInstruction.L2SubType.MPLS_POP.name());
+        assertThat(instruction.type(), is(Instruction.Type.L2MODIFICATION));
+        assertThat((short) ((L2ModificationInstruction.PushHeaderInstructions) instruction)
+                        .ethernetType(),
+                is(Ethernet.MPLS_UNICAST));
+
+        instruction = getInstruction(Instruction.Type.L2MODIFICATION,
+                L2ModificationInstruction.L2SubType.DEC_MPLS_TTL.name());
+        assertThat(instruction.type(), is(Instruction.Type.L2MODIFICATION));
+        assertThat(instruction, instanceOf(L2ModificationInstruction.ModMplsTtlInstruction.class));
+
+        instruction = getInstruction(Instruction.Type.L2MODIFICATION,
+                L2ModificationInstruction.L2SubType.VLAN_POP.name());
+        assertThat(instruction.type(), is(Instruction.Type.L2MODIFICATION));
+        assertThat(instruction, instanceOf(L2ModificationInstruction.PopVlanInstruction.class));
+
+        instruction = getInstruction(Instruction.Type.L2MODIFICATION,
+                L2ModificationInstruction.L2SubType.VLAN_PUSH.name());
+        assertThat(instruction.type(), is(Instruction.Type.L2MODIFICATION));
+        assertThat(instruction, instanceOf(L2ModificationInstruction.PushHeaderInstructions.class));
+
+        instruction = getInstruction(Instruction.Type.L3MODIFICATION,
+                L3ModificationInstruction.L3SubType.IPV4_SRC.name());
+        assertThat(instruction.type(), is(Instruction.Type.L3MODIFICATION));
+        assertThat(((L3ModificationInstruction.ModIPInstruction) instruction).ip(),
+                is(IpAddress.valueOf("1.2.3.4")));
+
+        instruction = getInstruction(Instruction.Type.L3MODIFICATION,
+                L3ModificationInstruction.L3SubType.IPV4_DST.name());
+        assertThat(instruction.type(), is(Instruction.Type.L3MODIFICATION));
+        assertThat(((L3ModificationInstruction.ModIPInstruction) instruction).ip(),
+                is(IpAddress.valueOf("1.2.3.3")));
+
+        instruction = getInstruction(Instruction.Type.L3MODIFICATION,
+                L3ModificationInstruction.L3SubType.IPV6_SRC.name());
+        assertThat(instruction.type(), is(Instruction.Type.L3MODIFICATION));
+        assertThat(((L3ModificationInstruction.ModIPInstruction) instruction).ip(),
+                is(IpAddress.valueOf("1.2.3.2")));
+
+        instruction = getInstruction(Instruction.Type.L3MODIFICATION,
+                L3ModificationInstruction.L3SubType.IPV6_DST.name());
+        assertThat(instruction.type(), is(Instruction.Type.L3MODIFICATION));
+        assertThat(((L3ModificationInstruction.ModIPInstruction) instruction).ip(),
+                is(IpAddress.valueOf("1.2.3.1")));
+
+        instruction = getInstruction(Instruction.Type.L3MODIFICATION,
+                L3ModificationInstruction.L3SubType.IPV6_FLABEL.name());
+        assertThat(instruction.type(), is(Instruction.Type.L3MODIFICATION));
+        assertThat(((L3ModificationInstruction.ModIPv6FlowLabelInstruction) instruction)
+                .flowLabel(),
+                is(8));
+
+        instruction = getInstruction(Instruction.Type.L0MODIFICATION,
+                L0ModificationInstruction.L0SubType.LAMBDA.name());
+        assertThat(instruction.type(), is(Instruction.Type.L0MODIFICATION));
+        assertThat(((L0ModificationInstruction.ModLambdaInstruction) instruction)
+                        .lambda(),
+                is((short) 7));
+
+        instruction = getInstruction(Instruction.Type.L0MODIFICATION,
+                L0ModificationInstruction.L0SubType.OCH.name());
+        assertThat(instruction.type(), is(Instruction.Type.L0MODIFICATION));
+        L0ModificationInstruction.ModOchSignalInstruction och =
+                (L0ModificationInstruction.ModOchSignalInstruction) instruction;
+        assertThat(och.lambda().spacingMultiplier(), is(4));
+        assertThat(och.lambda().slotGranularity(), is(8));
+        assertThat(och.lambda().gridType(), is(GridType.DWDM));
+        assertThat(och.lambda().channelSpacing(), is(ChannelSpacing.CHL_100GHZ));
+    }
+
+    SortedMap<String, Criterion> criteria = new TreeMap<>();
+
+    /**
+     * Looks up a criterion in the instruction map based on type and subtype.
+     *
+     * @param type type string
+     * @return criterion that matches
+     */
+    private Criterion getCriterion(Criterion.Type type) {
+        Criterion criterion = criteria.get(type.name());
+        assertThat(criterion.type(), is(type));
+        return criterion;
+    }
+
+    /**
+     * Checks that a rule with one of each kind of criterion decodes properly.
+     *
+     * @throws IOException if the resource cannot be processed
+     */
+    @Test
+    public void codecCriteriaFlowTest() throws Exception {
+        FlowRule rule = getRule("criteria-flow.json");
+
+        checkCommonData(rule);
+
+        assertThat(rule.selector().criteria().size(), is(32));
+
+        rule.selector().criteria()
+                .stream()
+                .forEach(criterion ->
+                        criteria.put(criterion.type().name(), criterion));
+
+        Criterion criterion;
+
+        criterion = getCriterion(Criterion.Type.ETH_TYPE);
+        assertThat(((EthTypeCriterion) criterion).ethType(), is(2054));
+
+        criterion = getCriterion(Criterion.Type.ETH_DST);
+        assertThat(((EthCriterion) criterion).mac(),
+                is(MacAddress.valueOf("00:11:22:33:44:55")));
+
+        criterion = getCriterion(Criterion.Type.ETH_SRC);
+        assertThat(((EthCriterion) criterion).mac(),
+                is(MacAddress.valueOf("00:11:22:33:44:55")));
+
+        criterion = getCriterion(Criterion.Type.IN_PORT);
+        assertThat(((PortCriterion) criterion).port(),
+                is(PortNumber.portNumber(23)));
+
+        criterion = getCriterion(Criterion.Type.IN_PHY_PORT);
+        assertThat(((PortCriterion) criterion).port(),
+                is(PortNumber.portNumber(44)));
+
+        criterion = getCriterion(Criterion.Type.VLAN_VID);
+        assertThat(((VlanIdCriterion) criterion).vlanId(),
+                is(VlanId.vlanId((short) 777)));
+
+        criterion = getCriterion(Criterion.Type.VLAN_PCP);
+        assertThat(((VlanPcpCriterion) criterion).priority(),
+                is(((byte) 3)));
+
+        criterion = getCriterion(Criterion.Type.IP_DSCP);
+        assertThat(((IPDscpCriterion) criterion).ipDscp(),
+                is(((byte) 2)));
+
+        criterion = getCriterion(Criterion.Type.IP_ECN);
+        assertThat(((IPEcnCriterion) criterion).ipEcn(),
+                is(((byte) 1)));
+
+        criterion = getCriterion(Criterion.Type.IP_PROTO);
+        assertThat(((IPProtocolCriterion) criterion).protocol(),
+                is(((short) 4)));
+
+        criterion = getCriterion(Criterion.Type.IPV4_SRC);
+        assertThat(((IPCriterion) criterion).ip(),
+                is((IpPrefix.valueOf("1.2.0.0/32"))));
+
+        criterion = getCriterion(Criterion.Type.IPV4_DST);
+        assertThat(((IPCriterion) criterion).ip(),
+                is((IpPrefix.valueOf("2.2.0.0/32"))));
+
+        criterion = getCriterion(Criterion.Type.IPV6_SRC);
+        assertThat(((IPCriterion) criterion).ip(),
+                is((IpPrefix.valueOf("3.2.0.0/32"))));
+
+        criterion = getCriterion(Criterion.Type.IPV6_DST);
+        assertThat(((IPCriterion) criterion).ip(),
+                is((IpPrefix.valueOf("4.2.0.0/32"))));
+
+        criterion = getCriterion(Criterion.Type.TCP_SRC);
+        assertThat(((TcpPortCriterion) criterion).tcpPort(),
+                is(80));
+
+        criterion = getCriterion(Criterion.Type.TCP_DST);
+        assertThat(((TcpPortCriterion) criterion).tcpPort(),
+                is(443));
+
+        criterion = getCriterion(Criterion.Type.UDP_SRC);
+        assertThat(((UdpPortCriterion) criterion).udpPort(),
+                is(180));
+
+        criterion = getCriterion(Criterion.Type.UDP_DST);
+        assertThat(((UdpPortCriterion) criterion).udpPort(),
+                is(1443));
+
+        criterion = getCriterion(Criterion.Type.SCTP_SRC);
+        assertThat(((SctpPortCriterion) criterion).sctpPort(),
+                is(280));
+
+        criterion = getCriterion(Criterion.Type.SCTP_DST);
+        assertThat(((SctpPortCriterion) criterion).sctpPort(),
+                is(2443));
+
+        criterion = getCriterion(Criterion.Type.ICMPV4_TYPE);
+        assertThat(((IcmpTypeCriterion) criterion).icmpType(),
+                is((short) 24));
+
+        criterion = getCriterion(Criterion.Type.ICMPV4_CODE);
+        assertThat(((IcmpCodeCriterion) criterion).icmpCode(),
+                is((short) 16));
+
+        criterion = getCriterion(Criterion.Type.ICMPV6_TYPE);
+        assertThat(((Icmpv6TypeCriterion) criterion).icmpv6Type(),
+                is((short) 14));
+
+        criterion = getCriterion(Criterion.Type.ICMPV6_CODE);
+        assertThat(((Icmpv6CodeCriterion) criterion).icmpv6Code(),
+                is((short) 6));
+
+        criterion = getCriterion(Criterion.Type.IPV6_FLABEL);
+        assertThat(((IPv6FlowLabelCriterion) criterion).flowLabel(),
+                is(8));
+
+        criterion = getCriterion(Criterion.Type.IPV6_ND_TARGET);
+        assertThat(((IPv6NDTargetAddressCriterion) criterion)
+                        .targetAddress().toString(),
+                is("1111:2222:3333:4444:5555:6666:7777:8888"));
+
+        criterion = getCriterion(Criterion.Type.IPV6_ND_SLL);
+        assertThat(((IPv6NDLinkLayerAddressCriterion) criterion).mac(),
+                is(MacAddress.valueOf("00:11:22:33:44:56")));
+
+        criterion = getCriterion(Criterion.Type.IPV6_ND_TLL);
+        assertThat(((IPv6NDLinkLayerAddressCriterion) criterion).mac(),
+                is(MacAddress.valueOf("00:11:22:33:44:57")));
+
+        criterion = getCriterion(Criterion.Type.MPLS_LABEL);
+        assertThat(((MplsCriterion) criterion).label(),
+                is(MplsLabel.mplsLabel(123)));
+
+        criterion = getCriterion(Criterion.Type.IPV6_EXTHDR);
+        assertThat(((IPv6ExthdrFlagsCriterion) criterion).exthdrFlags(),
+                is(99));
+
+        criterion = getCriterion(Criterion.Type.OCH_SIGID);
+        assertThat(((IndexedLambdaCriterion) criterion).lambda(),
+                is(Lambda.indexedLambda(122)));
+    }
+
+    /**
+     * Checks that a rule with a SigId criterion decodes properly.
+     *
+     * @throws IOException if the resource cannot be processed
+     */
+    @Test
+    public void codecSigIdCriteriaFlowTest() throws Exception {
+        FlowRule rule = getRule("sigid-flow.json");
+
+        checkCommonData(rule);
+
+        assertThat(rule.selector().criteria().size(), is(1));
+        Criterion criterion = rule.selector().criteria().iterator().next();
+        assertThat(criterion.type(), is(Criterion.Type.OCH_SIGID));
+        Lambda lambda = ((OchSignalCriterion) criterion).lambda();
+        assertThat(lambda, instanceOf(OchSignal.class));
+        OchSignal ochSignal = (OchSignal) lambda;
+        assertThat(ochSignal.spacingMultiplier(), is(3));
+        assertThat(ochSignal.slotGranularity(), is(4));
+        assertThat(ochSignal.gridType(), is(GridType.CWDM));
+        assertThat(ochSignal.channelSpacing(), is(ChannelSpacing.CHL_25GHZ));
+    }
+
+}
diff --git a/core/common/src/test/java/org/onosproject/codec/impl/ImmutableCodecsTest.java b/core/common/src/test/java/org/onosproject/codec/impl/ImmutableCodecsTest.java
index f0fb070..744248a 100644
--- a/core/common/src/test/java/org/onosproject/codec/impl/ImmutableCodecsTest.java
+++ b/core/common/src/test/java/org/onosproject/codec/impl/ImmutableCodecsTest.java
@@ -38,6 +38,8 @@
         assertThatClassIsImmutable(ConnectPointCodec.class);
         assertThatClassIsImmutable(ConstraintCodec.class);
         assertThatClassIsImmutable(CriterionCodec.class);
+        assertThatClassIsImmutable(EncodeCriterionCodec.class);
+        assertThatClassIsImmutable(DecodeCriterionCodec.class);
         assertThatClassIsImmutable(DeviceCodec.class);
         assertThatClassIsImmutable(EthernetCodec.class);
         assertThatClassIsImmutable(FlowEntryCodec.class);
@@ -45,6 +47,8 @@
         assertThatClassIsImmutable(HostLocationCodec.class);
         assertThatClassIsImmutable(HostToHostIntentCodec.class);
         assertThatClassIsImmutable(InstructionCodec.class);
+        assertThatClassIsImmutable(EncodeInstructionCodec.class);
+        assertThatClassIsImmutable(DecodeInstructionCodec.class);
         assertThatClassIsImmutable(IntentCodec.class);
         assertThatClassIsImmutable(LinkCodec.class);
         assertThatClassIsImmutable(PathCodec.class);
@@ -54,5 +58,6 @@
         assertThatClassIsImmutable(TopologyCodec.class);
         assertThatClassIsImmutable(TrafficSelectorCodec.class);
         assertThatClassIsImmutable(TrafficTreatmentCodec.class);
+        assertThatClassIsImmutable(FlowRuleCodec.class);
     }
 }
diff --git a/core/common/src/test/resources/org/onosproject/codec/impl/criteria-flow.json b/core/common/src/test/resources/org/onosproject/codec/impl/criteria-flow.json
new file mode 100644
index 0000000..6df4d0a
--- /dev/null
+++ b/core/common/src/test/resources/org/onosproject/codec/impl/criteria-flow.json
@@ -0,0 +1,44 @@
+{
+  "appId":-29467,
+  "priority":1,
+  "isPermanent":"false",
+  "timeout":1,
+  "deviceId":"of:0000000000000001",
+  "selector":
+      {"criteria":
+          [
+            {"type":"IN_PORT", "port":23},
+            {"type":"IN_PHY_PORT", "port":44},
+            {"type":"METADATA", "metadata":123456},
+            {"type":"ETH_TYPE","ethType":2054},
+            {"type":"ETH_SRC","mac":"00:11:22:33:44:55"},
+            {"type":"ETH_DST","mac":"00:11:22:33:44:55"},
+            {"type":"VLAN_VID","vlanId":777},
+            {"type":"VLAN_PCP","priority":3},
+            {"type":"IP_DSCP","ipDscp":2},
+            {"type":"IP_ECN","ipEcn":1},
+            {"type":"IP_PROTO","protocol":4},
+            {"type":"IPV4_SRC", "ip":"1.2.0.0/32"},
+            {"type":"IPV4_DST", "ip":"2.2.0.0/32"},
+            {"type":"IPV6_SRC", "ip":"3.2.0.0/32"},
+            {"type":"IPV6_DST", "ip":"4.2.0.0/32"},
+            {"type":"TCP_SRC", "tcpPort":80},
+            {"type":"TCP_DST", "tcpPort":443},
+            {"type":"UDP_SRC", "udpPort":180},
+            {"type":"UDP_DST", "udpPort":1443},
+            {"type":"SCTP_SRC", "sctpPort":280},
+            {"type":"SCTP_DST", "sctpPort":2443},
+            {"type":"ICMPV4_TYPE", "icmpType":24},
+            {"type":"ICMPV4_CODE", "icmpCode":16},
+            {"type":"ICMPV6_TYPE", "icmpv6Type":14},
+            {"type":"ICMPV6_CODE", "icmpv6Code":6},
+            {"type":"IPV6_FLABEL", "flowLabel":8},
+            {"type":"IPV6_ND_TARGET", "targetAddress":"1111:2222:3333:4444:5555:6666:7777:8888"},
+            {"type":"IPV6_ND_SLL", "mac":"00:11:22:33:44:56"},
+            {"type":"IPV6_ND_TLL", "mac":"00:11:22:33:44:57"},
+            {"type":"MPLS_LABEL", "label":123},
+            {"type":"IPV6_EXTHDR", "exthdrFlags":99},
+            {"type":"OCH_SIGID", "lambda":122}
+          ]
+      }
+}
diff --git a/core/common/src/test/resources/org/onosproject/codec/impl/instructions-flow.json b/core/common/src/test/resources/org/onosproject/codec/impl/instructions-flow.json
new file mode 100644
index 0000000..d60362d
--- /dev/null
+++ b/core/common/src/test/resources/org/onosproject/codec/impl/instructions-flow.json
@@ -0,0 +1,35 @@
+{
+  "appId":-29467,
+  "priority":1,
+  "isPermanent":"false",
+  "timeout":1,
+  "deviceId":"of:0000000000000001",
+  "treatment":
+  {
+      "instructions":
+      [
+        {"type":"OUTPUT","port":-3},
+        {"type":"DROP"},
+        {"type":"L2MODIFICATION","subtype":"ETH_SRC","mac":"12:34:56:78:90:12"},
+        {"type":"L2MODIFICATION","subtype":"ETH_DST","mac":"98:76:54:32:01:00"},
+        {"type":"L2MODIFICATION","subtype":"VLAN_ID","vlanId":22},
+        {"type":"L2MODIFICATION","subtype":"VLAN_PCP","vlanPcp":1},
+        {"type":"L2MODIFICATION","subtype":"MPLS_LABEL","label":777},
+        {"type":"L2MODIFICATION","subtype":"MPLS_PUSH"},
+        {"type":"L2MODIFICATION","subtype":"MPLS_POP"},
+        {"type":"L2MODIFICATION","subtype":"DEC_MPLS_TTL"},
+        {"type":"L2MODIFICATION","subtype":"VLAN_POP"},
+        {"type":"L2MODIFICATION","subtype":"VLAN_PUSH"},
+        {"type":"L3MODIFICATION","subtype":"IPV4_SRC", "ip":"1.2.3.4"},
+        {"type":"L3MODIFICATION","subtype":"IPV4_DST", "ip":"1.2.3.3"},
+        {"type":"L3MODIFICATION","subtype":"IPV6_SRC", "ip":"1.2.3.2"},
+        {"type":"L3MODIFICATION","subtype":"IPV6_DST", "ip":"1.2.3.1"},
+        {"type":"L3MODIFICATION","subtype":"IPV6_FLABEL", "flowLabel":8},
+        {"type":"L0MODIFICATION","subtype":"LAMBDA","lambda":7},
+        {"type":"L0MODIFICATION","subtype":"OCH","gridType":"DWDM",
+          "channelSpacing":"CHL_100GHZ","spacingMultiplier":4,"slotGranularity":8}
+      ],
+      "deferred":[]
+  },
+  "selector": {"criteria":[{"type":"ETH_TYPE","ethType":2054}]}
+}
diff --git a/core/common/src/test/resources/org/onosproject/codec/impl/sigid-flow.json b/core/common/src/test/resources/org/onosproject/codec/impl/sigid-flow.json
new file mode 100644
index 0000000..ba576bf
--- /dev/null
+++ b/core/common/src/test/resources/org/onosproject/codec/impl/sigid-flow.json
@@ -0,0 +1,21 @@
+{
+  "appId":-29467,
+  "priority":1,
+  "isPermanent":"false",
+  "timeout":1,
+  "deviceId":"of:0000000000000001",
+  "selector":
+  {"criteria":
+  [
+    {"type":"OCH_SIGID",
+      "ochSignalId":
+      {
+        "gridType":"CWDM",
+        "channelSpacing":"CHL_25GHZ",
+        "spacingMultiplier":3,
+        "slotGranularity":4
+      }
+    }
+  ]
+  }
+}
diff --git a/core/common/src/test/resources/org/onosproject/codec/impl/simple-flow.json b/core/common/src/test/resources/org/onosproject/codec/impl/simple-flow.json
new file mode 100644
index 0000000..4011c2d
--- /dev/null
+++ b/core/common/src/test/resources/org/onosproject/codec/impl/simple-flow.json
@@ -0,0 +1,13 @@
+{
+   "appId":-29467,
+   "priority":1,
+   "isPermanent":"false",
+   "timeout":1,
+   "deviceId":"of:0000000000000001",
+   "treatment":
+      {"instructions":
+         [{"type":"OUTPUT","port":-3}],"deferred":[]},
+   "selector":
+      {"criteria":
+         [{"type":"ETH_TYPE","ethType":2054}]}
+}
diff --git a/utils/misc/src/main/java/org/onlab/util/Tools.java b/utils/misc/src/main/java/org/onlab/util/Tools.java
index 73c8802..0e23dbf 100644
--- a/utils/misc/src/main/java/org/onlab/util/Tools.java
+++ b/utils/misc/src/main/java/org/onlab/util/Tools.java
@@ -123,7 +123,7 @@
     }
 
     /**
-     * Returns the specified item if that items is null; otherwise throws
+     * Returns the specified item if that item is not null; otherwise throws
      * not found exception.
      *
      * @param item    item to check
@@ -140,6 +140,23 @@
     }
 
     /**
+     * Returns the specified item if that item is not null; otherwise throws
+     * bad argument exception.
+     *
+     * @param item    item to check
+     * @param message not found message
+     * @param <T>     item type
+     * @return item if not null
+     * @throws IllegalArgumentException if item is null
+     */
+    public static <T> T nullIsIllegal(T item, String message) {
+        if (item == null) {
+            throw new IllegalArgumentException(message);
+        }
+        return item;
+    }
+
+    /**
      * Converts a string from hex to long.
      *
      * @param string hex number in string form; sans 0x
diff --git a/web/api/src/main/java/org/onosproject/rest/exceptions/IllegalArgumentExceptionMapper.java b/web/api/src/main/java/org/onosproject/rest/exceptions/IllegalArgumentExceptionMapper.java
new file mode 100644
index 0000000..6a381f7
--- /dev/null
+++ b/web/api/src/main/java/org/onosproject/rest/exceptions/IllegalArgumentExceptionMapper.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014-2015 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.exceptions;
+
+import javax.ws.rs.core.Response;
+
+/**
+ * Mapper for illegal argument exceptions to the BAD_REQUEST response code.
+ */
+public class IllegalArgumentExceptionMapper extends AbstractMapper<IllegalArgumentException> {
+    @Override
+    protected Response.Status responseStatus() {
+        return Response.Status.BAD_REQUEST;
+    }
+}
+
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java
index 49840c4..9c39268 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java
@@ -15,23 +15,30 @@
  */
 package org.onosproject.rest.resources;
 
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import org.onlab.util.ItemNotFoundException;
-import org.onosproject.net.Device;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.flow.FlowEntry;
-import org.onosproject.net.flow.FlowRuleService;
-import org.onosproject.rest.AbstractWebResource;
+import java.io.IOException;
+import java.io.InputStream;
 
+import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
+import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 
+import org.onlab.util.ItemNotFoundException;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.rest.AbstractWebResource;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
 /**
  * REST resource for interacting with the inventory of flows.
  */
@@ -113,4 +120,27 @@
         }
         return ok(root).build();
     }
+
+    /**
+     * Creates a flow rule from a POST of a JSON string and attempts to apply it.
+     *
+     * @param stream input JSON
+     * @return status of the request - ACCEPTED if the JSON is correct,
+     * BAD_REQUEST if the JSON is invalid
+     */
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response createFlow(InputStream stream) {
+        try {
+            FlowRuleService service = get(FlowRuleService.class);
+            ObjectNode root = (ObjectNode) mapper().readTree(stream);
+            FlowRule rule = codec(FlowRule.class).decode(root, this);
+            service.applyFlowRules(rule);
+        } catch (IOException ex) {
+            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+        }
+        return Response.status(Response.Status.ACCEPTED).build();
+    }
+
 }
diff --git a/web/api/src/main/webapp/WEB-INF/web.xml b/web/api/src/main/webapp/WEB-INF/web.xml
index a3e2060..d8759fe 100644
--- a/web/api/src/main/webapp/WEB-INF/web.xml
+++ b/web/api/src/main/webapp/WEB-INF/web.xml
@@ -61,6 +61,7 @@
                 org.onosproject.rest.exceptions.ServerErrorMapper,
                 org.onosproject.rest.exceptions.BadRequestMapper,
                 org.onosproject.rest.exceptions.WebApplicationExceptionMapper,
+                org.onosproject.rest.exceptions.IllegalArgumentExceptionMapper,
                 org.onosproject.rest.resources.JsonBodyWriter,
 
                 org.onosproject.rest.resources.ApplicationsWebResource,
diff --git a/web/api/src/test/java/org/onosproject/rest/FlowsResourceTest.java b/web/api/src/test/java/org/onosproject/rest/FlowsResourceTest.java
index 0f0dae8..b18340a 100644
--- a/web/api/src/test/java/org/onosproject/rest/FlowsResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/FlowsResourceTest.java
@@ -15,10 +15,13 @@
  */
 package org.onosproject.rest;
 
+import java.net.HttpURLConnection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Set;
 
+import javax.ws.rs.core.MediaType;
+
 import org.hamcrest.Description;
 import org.hamcrest.TypeSafeMatcher;
 import org.junit.After;
@@ -30,11 +33,13 @@
 import org.onlab.rest.BaseResource;
 import org.onosproject.codec.CodecService;
 import org.onosproject.codec.impl.CodecManager;
+import org.onosproject.core.CoreService;
 import org.onosproject.core.DefaultGroupId;
 import org.onosproject.core.GroupId;
 import org.onosproject.net.DefaultDevice;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.NetTestTools;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
@@ -51,12 +56,15 @@
 import com.eclipsesource.json.JsonArray;
 import com.eclipsesource.json.JsonObject;
 import com.google.common.collect.ImmutableSet;
+import com.sun.jersey.api.client.ClientResponse;
 import com.sun.jersey.api.client.UniformInterfaceException;
 import com.sun.jersey.api.client.WebResource;
 
 import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.anyShort;
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
 import static org.easymock.EasyMock.replay;
 import static org.easymock.EasyMock.verify;
 import static org.hamcrest.Matchers.containsString;
@@ -72,6 +80,8 @@
  */
 public class FlowsResourceTest extends ResourceTest {
     final FlowRuleService mockFlowService = createMock(FlowRuleService.class);
+    CoreService mockCoreService = createMock(CoreService.class);
+
     final HashMap<DeviceId, Set<FlowEntry>> rules = new HashMap<>();
 
     final DeviceService mockDeviceService = createMock(DeviceService.class);
@@ -245,6 +255,11 @@
         expect(mockDeviceService.getDevices())
                 .andReturn(ImmutableSet.of(device1, device2));
 
+        // Mock Core Service
+        expect(mockCoreService.getAppId(anyShort()))
+                .andReturn(NetTestTools.APP_ID).anyTimes();
+        replay(mockCoreService);
+
         // Register the services needed for the test
         final CodecManager codecService =  new CodecManager();
         codecService.activate();
@@ -252,7 +267,8 @@
                 new TestServiceDirectory()
                         .add(FlowRuleService.class, mockFlowService)
                         .add(DeviceService.class, mockDeviceService)
-                        .add(CodecService.class, codecService);
+                        .add(CodecService.class, codecService)
+                        .add(CoreService.class, mockCoreService);
 
         BaseResource.setServiceDirectory(testDirectory);
     }
@@ -263,6 +279,7 @@
     @After
     public void tearDownTest() {
         verify(mockFlowService);
+        verify(mockCoreService);
     }
 
     /**
@@ -542,4 +559,27 @@
                     containsString("returned a response status of"));
         }
     }
+
+    /**
+     * Tests creating a flow with POST.
+     */
+    @Test
+    public void testPost() {
+        String json = "{\"appId\":2,\"priority\":1,\"isPermanent\":true,"
+        + "\"deviceId\":\"of:0000000000000001\","
+        + "\"treatment\":{\"instructions\":[ {\"type\":\"OUTPUT\",\"port\":2}]},"
+        + "\"selector\":{\"criteria\":[ {\"type\":\"ETH_TYPE\",\"ethType\":2054}]}}";
+
+        mockFlowService.applyFlowRules(anyObject());
+        expectLastCall();
+        replay(mockFlowService);
+
+        WebResource rs = resource();
+
+
+        ClientResponse response = rs.path("flows/")
+                .type(MediaType.APPLICATION_JSON_TYPE)
+                .post(ClientResponse.class, json);
+        assertThat(response.getStatus(), is(HttpURLConnection.HTTP_ACCEPTED));
+    }
 }