Add MatchSerializerInjector 22/48622/15
authorTomas Slusny <tomas.slusny@pantheon.tech>
Wed, 23 Nov 2016 12:16:03 +0000 (13:16 +0100)
committerTomas Slusny <tomas.slusny@pantheon.tech>
Wed, 14 Dec 2016 17:11:12 +0000 (18:11 +0100)
Add injector where all match and match entry serializers will be
registered and call it from general SerializerInjector.
Add AbstractMatchEntrySerializer and MatchSerializer to be able to
actually create match entry serializers and serialize match body.
Add AbstractExperimenterMatchEntrySerializer to serialize match
entries with experimenter id.
Add match extension serialization to MatchSerializer, using match
extension converters. This needs to be changed later, to also remove
use of match extension converters.

See also: bug 7138

Change-Id: Ifa3f0d52b384b173cf639f7d00fd5a4a3e0d30f4
Signed-off-by: Tomas Slusny <tomas.slusny@pantheon.tech>
openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/protocol/serialization/MatchEntrySerializer.java [new file with mode: 0644]
openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/protocol/serialization/MatchEntrySerializerKey.java [new file with mode: 0644]
openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/protocol/serialization/MatchEntrySerializerRegistry.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/protocol/serialization/MatchSerializerInjector.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/protocol/serialization/SerializerInjector.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/protocol/serialization/match/AbstractExperimenterMatchEntrySerializer.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/protocol/serialization/match/AbstractMatchEntrySerializer.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/protocol/serialization/match/MatchEntrySerializerKeyImpl.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/protocol/serialization/match/MatchSerializer.java [new file with mode: 0644]
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/protocol/serialization/match/AbstractExperimenterMatchEntrySerializerTest.java [new file with mode: 0644]
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/protocol/serialization/match/AbstractMatchEntrySerializerTest.java [new file with mode: 0644]

diff --git a/openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/protocol/serialization/MatchEntrySerializer.java b/openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/protocol/serialization/MatchEntrySerializer.java
new file mode 100644 (file)
index 0000000..f1fc772
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2016 Pantheon Technologies s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.api.openflow.protocol.serialization;
+
+import org.opendaylight.openflowjava.protocol.api.extensibility.OFSerializer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.Match;
+
+public interface MatchEntrySerializer extends OFSerializer<Match> {
+
+    /**
+     * Checks if current match is this match type
+     * @param match Openflow match
+     * @return true if matched
+     */
+    boolean matchTypeCheck(final Match match);
+
+}
\ No newline at end of file
diff --git a/openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/protocol/serialization/MatchEntrySerializerKey.java b/openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/protocol/serialization/MatchEntrySerializerKey.java
new file mode 100644 (file)
index 0000000..a53549b
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2016 Pantheon Technologies s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.api.openflow.protocol.serialization;
+
+/**
+ * Marker interface - marks keys appropriate for match entry serializer registration
+ */
+public interface MatchEntrySerializerKey {
+}
diff --git a/openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/protocol/serialization/MatchEntrySerializerRegistry.java b/openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/protocol/serialization/MatchEntrySerializerRegistry.java
new file mode 100644 (file)
index 0000000..f9060ed
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2016 Pantheon Technologies s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.api.openflow.protocol.serialization;
+
+public interface MatchEntrySerializerRegistry {
+
+    /**
+     * Registers match entry serializer.
+
+     * @param key used for deserializer lookup
+     * @param serializer serializer instance
+     */
+    void registerEntrySerializer(MatchEntrySerializerKey key, MatchEntrySerializer serializer);
+
+    /**
+     * Unregisters match entry serializer
+     * @param key used for serializer lookup
+     * @return true if serializer was removed, false if no serializer was found under specified key
+     */
+    boolean unregisterEntrySerializer(MatchEntrySerializerKey key);
+
+}
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/protocol/serialization/MatchSerializerInjector.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/protocol/serialization/MatchSerializerInjector.java
new file mode 100644 (file)
index 0000000..b909002
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2016 Pantheon Technologies s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.impl.protocol.serialization;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import org.opendaylight.openflowjava.protocol.api.extensibility.SerializerExtensionProvider;
+import org.opendaylight.openflowjava.protocol.api.keys.MessageTypeKey;
+import org.opendaylight.openflowjava.protocol.api.util.EncodeConstants;
+import org.opendaylight.openflowplugin.api.openflow.protocol.serialization.MatchEntrySerializer;
+import org.opendaylight.openflowplugin.api.openflow.protocol.serialization.MatchEntrySerializerRegistry;
+import org.opendaylight.openflowplugin.impl.protocol.serialization.match.MatchEntrySerializerKeyImpl;
+import org.opendaylight.openflowplugin.impl.protocol.serialization.match.MatchSerializer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.Match;
+
+/**
+ * Util class for injecting new match serializers into OpenflowJava
+ */
+public class MatchSerializerInjector {
+
+    /**
+     * Injects match serializers into provided {@link org.opendaylight.openflowjava.protocol.api.extensibility.SerializerExtensionProvider}
+     * @param provider OpenflowJava serializer extension provider
+     */
+    public static void injectSerializers(final SerializerExtensionProvider provider) {
+        final MatchSerializer serializer = new MatchSerializer();
+        provider.registerSerializer(
+                new MessageTypeKey<>(EncodeConstants.OF13_VERSION_ID, Match.class),
+                serializer);
+
+        // Inject all match entry serializers to match serializers using injector created by createInjector method
+        final Function<Integer, Function<Integer, Consumer<MatchEntrySerializer>>> injector =
+                createInjector(serializer, EncodeConstants.OF13_VERSION_ID);
+    }
+
+    /**
+     * Create injector that will inject new match entry serializers into #{@link org.opendaylight.openflowplugin.api.openflow.protocol.serialization.MatchEntrySerializerRegistry}
+     * @param registry Match entry serializer registry
+     * @param version Openflow version
+     * @return injector
+     */
+    @VisibleForTesting
+    static Function<Integer, Function<Integer, Consumer<MatchEntrySerializer>>> createInjector(
+            final MatchEntrySerializerRegistry registry,
+            final byte version) {
+        return oxmClass -> oxmField -> serializer ->
+                registry.registerEntrySerializer(
+                        new MatchEntrySerializerKeyImpl(version, oxmClass, oxmField),
+                        serializer);
+    }
+}
index 0ec19620e6e5db016fcf457825e95bddd2050b8c..3e3f983f59a9416f5fe06eea1a054e49c08ce79f 100644 (file)
@@ -21,6 +21,7 @@ public class SerializerInjector {
      */
     public static void injectSerializers(final SerializerExtensionProvider provider) {
         // Inject new serializers here
+        MatchSerializerInjector.injectSerializers(provider);
         MessageSerializerInjector.injectSerializers(provider);
     }
 }
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/protocol/serialization/match/AbstractExperimenterMatchEntrySerializer.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/protocol/serialization/match/AbstractExperimenterMatchEntrySerializer.java
new file mode 100644 (file)
index 0000000..810a41c
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2016 Pantheon Technologies s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.impl.protocol.serialization.match;
+
+import io.netty.buffer.ByteBuf;
+import org.opendaylight.openflowjava.protocol.api.util.EncodeConstants;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.Match;
+
+public abstract class AbstractExperimenterMatchEntrySerializer extends AbstractMatchEntrySerializer {
+
+    @Override
+    public void serialize(Match match, ByteBuf outBuffer) {
+        super.serialize(match, outBuffer);
+        outBuffer.writeInt(Long.valueOf(getExperimenterId()).intValue());
+    }
+
+    @Override
+    public void serializeHeader(Match match, ByteBuf outBuffer) {
+        outBuffer.writeShort(getOxmClassCode());
+
+        int fieldAndMask = getOxmFieldCode() << 1;
+        int length = getValueLength();
+
+        if (getHasMask(match)) {
+            fieldAndMask |= 1;
+            length *= 2;
+        }
+
+        outBuffer.writeByte(fieldAndMask);
+
+        // Add length of experimenter ID to total length
+        outBuffer.writeByte(length + EncodeConstants.SIZE_OF_INT_IN_BYTES);
+    }
+
+    /**
+     * @return experimenter match entry id
+     */
+    protected abstract long getExperimenterId();
+}
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/protocol/serialization/match/AbstractMatchEntrySerializer.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/protocol/serialization/match/AbstractMatchEntrySerializer.java
new file mode 100644 (file)
index 0000000..f463861
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2016 Pantheon Technologies s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.impl.protocol.serialization.match;
+
+import io.netty.buffer.ByteBuf;
+import java.util.Iterator;
+import java.util.Optional;
+import org.opendaylight.openflowjava.protocol.api.extensibility.HeaderSerializer;
+import org.opendaylight.openflowjava.protocol.api.util.EncodeConstants;
+import org.opendaylight.openflowplugin.api.openflow.protocol.serialization.MatchEntrySerializer;
+import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.common.IpConversionUtil;
+import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.match.MatchConvertorUtil;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.IetfYangUtil;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.Match;
+
+public abstract class AbstractMatchEntrySerializer implements HeaderSerializer<Match>, MatchEntrySerializer {
+
+    @Override
+    public void serialize(Match match, ByteBuf outBuffer) {
+        serializeHeader(match, outBuffer);
+    }
+
+    @Override
+    public void serializeHeader(Match match, ByteBuf outBuffer) {
+        outBuffer.writeShort(getOxmClassCode());
+
+        int fieldAndMask = getOxmFieldCode() << 1;
+        int length = getValueLength();
+
+        if (getHasMask(match)) {
+            fieldAndMask |= 1;
+            length *= 2;
+        }
+
+        outBuffer.writeByte(fieldAndMask);
+        outBuffer.writeByte(length);
+    }
+
+    /**
+     * Serialize byte mask to bytes. checking for mask length
+     * @param mask byte mask
+     * @param outBuffer output buffer
+     * @param length mask length
+     */
+    protected static void writeMask(byte[] mask, ByteBuf outBuffer, int length) {
+        if (mask != null && mask.length != length) {
+            throw new IllegalArgumentException("incorrect length of mask: "+
+                    mask.length + ", expected: " + length);
+        }
+
+        outBuffer.writeBytes(mask);
+    }
+
+    /**
+     * Serialize Ipv4 address to bytes
+     * @param address Ipv4 address
+     * @param outBuffer output buffer
+     */
+    protected static void writeIpv4Address(final Ipv4Address address, final ByteBuf outBuffer) {
+        outBuffer.writeBytes(IetfInetUtil.INSTANCE.ipv4AddressBytes(address));
+    }
+
+    /**
+     * Serialize Ipv6 address to bytes
+     * @param address Ipv6 address
+     * @param outBuffer output buffer
+     */
+    protected static void writeIpv6Address(final Ipv6Address address, final ByteBuf outBuffer) {
+        outBuffer.writeBytes(IetfInetUtil.INSTANCE.ipv6AddressBytes(address));
+    }
+
+    /**
+     * Serialize Mac address to bytes
+     * @param address Mac address
+     * @param outBuffer output buffer
+     */
+    protected static void writeMacAddress(final MacAddress address, final ByteBuf outBuffer) {
+        outBuffer.writeBytes(IetfYangUtil.INSTANCE.bytesFor(address)); // 48 b + mask [OF 1.3.2 spec]
+    }
+
+    /**
+     * Serialize Ipv4 prefix (address and mask)
+     * @param prefix Ipv4 prefix
+     * @param outBuffer output buffer
+     */
+    protected static void writeIpv4Prefix(final Ipv4Prefix prefix, final ByteBuf outBuffer) {
+        // Split address to IP and mask
+        final Iterator<String> addressParts = IpConversionUtil.splitToParts(prefix);
+
+        // Write address part of prefix
+        writeIpv4Address(new Ipv4Address(addressParts.next()), outBuffer);
+
+        // If prefix had mask, also write prefix
+        Optional.ofNullable(MatchConvertorUtil.extractIpv4Mask(addressParts)).ifPresent(mask ->
+                writeMask(mask, outBuffer, EncodeConstants.GROUPS_IN_IPV4_ADDRESS));
+    }
+
+    /**
+     * Serialize Ipv6 prefix (address and mask)
+     * @param prefix Ipv6 prefix
+     * @param outBuffer output buffer
+     */
+    protected static void writeIpv6Prefix(final Ipv6Prefix prefix, final ByteBuf outBuffer) {
+        // Write address part of prefix
+        writeIpv6Address(IpConversionUtil.extractIpv6Address(prefix), outBuffer);
+
+        // If prefix had mask, also write prefix
+        Optional.ofNullable(IpConversionUtil.extractIpv6Prefix(prefix)).ifPresent(mask ->
+                writeMask(IpConversionUtil.convertIpv6PrefixToByteArray(mask), outBuffer,
+                        EncodeConstants.SIZE_OF_IPV6_ADDRESS_IN_BYTES));
+    }
+
+    /**
+     * @param match Openflow match
+     * @return if field has or has not mask
+     */
+    protected abstract boolean getHasMask(final Match match);
+
+    /**
+     * @return numeric representation of oxm_field
+     */
+    protected abstract int getOxmFieldCode();
+
+    /**
+     * @return numeric representation of oxm_class
+     */
+    protected abstract int getOxmClassCode();
+
+    /**
+     * @return match entry value length (without mask length)
+     */
+    protected abstract int getValueLength();
+}
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/protocol/serialization/match/MatchEntrySerializerKeyImpl.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/protocol/serialization/match/MatchEntrySerializerKeyImpl.java
new file mode 100644 (file)
index 0000000..525f7e8
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2016 Pantheon Technologies s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.impl.protocol.serialization.match;
+
+import org.opendaylight.openflowplugin.api.openflow.protocol.serialization.MatchEntrySerializerKey;
+
+public class MatchEntrySerializerKeyImpl implements MatchEntrySerializerKey {
+
+    private final short version;
+    private final int oxmClass;
+    private final int oxmField;
+
+    /**
+     * Create new instance of MatchEntrySerializerKeyImpl
+     * @param version openflow version
+     * @param oxmClass match entry oxm class
+     * @param oxmField match entry field code
+     */
+    public MatchEntrySerializerKeyImpl(final short version, final int oxmClass, final int oxmField) {
+        this.version = version;
+        this.oxmClass = oxmClass;
+        this.oxmField = oxmField;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + oxmClass;
+        result = prime * result + oxmField;
+        result = prime * result + version;
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj == null) {
+            return false;
+        }
+
+        if (!(obj instanceof MatchEntrySerializerKeyImpl)) {
+            return false;
+        }
+
+        final MatchEntrySerializerKeyImpl other = (MatchEntrySerializerKeyImpl) obj;
+
+
+        return oxmClass == other.oxmClass
+                && oxmField == other.oxmField
+                && version == other.version;
+    }
+
+
+    @Override
+    public String toString() {
+        return "version: " + version
+                + " oxmClass:" + oxmClass
+                + " oxmField: " + oxmField;
+    }
+
+}
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/protocol/serialization/match/MatchSerializer.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/protocol/serialization/match/MatchSerializer.java
new file mode 100644 (file)
index 0000000..b6d7eb6
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2016 Pantheon Technologies s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.impl.protocol.serialization.match;
+
+import io.netty.buffer.ByteBuf;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Objects;
+import org.opendaylight.openflowjava.protocol.api.extensibility.HeaderSerializer;
+import org.opendaylight.openflowjava.protocol.api.extensibility.OFSerializer;
+import org.opendaylight.openflowjava.protocol.api.extensibility.SerializerRegistry;
+import org.opendaylight.openflowjava.protocol.api.extensibility.SerializerRegistryInjector;
+import org.opendaylight.openflowjava.protocol.api.keys.MatchEntrySerializerKey;
+import org.opendaylight.openflowjava.protocol.api.util.EncodeConstants;
+import org.opendaylight.openflowplugin.api.OFConstants;
+import org.opendaylight.openflowplugin.api.openflow.protocol.serialization.MatchEntrySerializer;
+import org.opendaylight.openflowplugin.api.openflow.protocol.serialization.MatchEntrySerializerRegistry;
+import org.opendaylight.openflowplugin.extension.api.ConverterExtensionKey;
+import org.opendaylight.openflowplugin.openflow.md.core.extension.ExtensionResolvers;
+import org.opendaylight.openflowplugin.openflow.md.core.session.OFSessionUtil;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev150225.oxm.container.match.entry.value.ExperimenterIdCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev150225.ExperimenterClass;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev150225.match.entries.grouping.MatchEntry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MatchSerializer implements OFSerializer<Match>, HeaderSerializer<Match>,
+        MatchEntrySerializerRegistry, SerializerRegistryInjector {
+
+    private static final Logger LOG = LoggerFactory.getLogger(MatchSerializer.class);
+    private static final byte OXM_MATCH_TYPE_CODE = 1;
+    private final Map<org.opendaylight.openflowplugin.api.openflow.protocol.serialization.
+            MatchEntrySerializerKey, MatchEntrySerializer> entryRegistry = new LinkedHashMap<>();
+    private SerializerRegistry registry;
+
+    @Override
+    public void serialize(Match match, ByteBuf outBuffer) {
+        if (match == null) {
+            LOG.debug("Match is null");
+            return;
+        }
+
+        // Save start index in buffer
+        int matchStartIndex = outBuffer.writerIndex();
+
+        // With OpenflowPlugin models, we cannot check difference between OXM and Standard match type
+        // so all matches will be OXM
+        outBuffer.writeShort(OXM_MATCH_TYPE_CODE);
+
+        // Save length of match type
+        int matchLengthIndex = outBuffer.writerIndex();
+        outBuffer.writeShort(EncodeConstants.EMPTY_LENGTH);
+
+        // Small hack to be able to serialize only match entryRegistry externally
+        serializeHeader(match, outBuffer);
+
+        // Length of ofp_match (excluding padding)
+        int matchLength = outBuffer.writerIndex() - matchStartIndex;
+        outBuffer.setShort(matchLengthIndex, matchLength);
+
+        // If we have any remaining padding, write it at end
+        int paddingRemainder = matchLength % EncodeConstants.PADDING;
+        if (paddingRemainder != 0) {
+            outBuffer.writeZero(EncodeConstants.PADDING - paddingRemainder);
+        }
+    }
+
+    @Override
+    public void serializeHeader(Match match, ByteBuf outBuffer) {
+        if (match == null) {
+            LOG.debug("Match is null");
+            return;
+        }
+
+        // Serialize match entries
+        entryRegistry.values().stream()
+                .filter(entry -> entry.matchTypeCheck(match))
+                .forEach(entry -> entry.serialize(match, outBuffer));
+
+        // Serialize match extensions
+        ExtensionResolvers.getMatchExtensionResolver().getExtension(match).transform(extensions -> {
+            if (Objects.nonNull(extensions)) {
+                extensions.getExtensionList().forEach(extension-> {
+                    // TODO: Remove also extension converters
+                    final MatchEntry entry = OFSessionUtil
+                            .getExtensionConvertorProvider()
+                            .<MatchEntry>getConverter(new ConverterExtensionKey<>(
+                                    extension.getExtensionKey(),
+                                    OFConstants.OFP_VERSION_1_3))
+                            .convert(extension.getExtension());
+
+                    final MatchEntrySerializerKey<?, ?> key = new MatchEntrySerializerKey<>(
+                            EncodeConstants.OF13_VERSION_ID, entry.getOxmClass(), entry.getOxmMatchField());
+
+                    // If entry is experimenter, set experimenter ID to key
+                    if (entry.getOxmClass().equals(ExperimenterClass.class)) {
+                        key.setExperimenterId(ExperimenterIdCase.class.cast(entry.getMatchEntryValue())
+                                .getExperimenter().getExperimenter().getValue());
+                    }
+
+                    final OFSerializer<MatchEntry> entrySerializer = registry.getSerializer(key);
+                    entrySerializer.serialize(entry, outBuffer);
+                });
+            }
+
+            return extensions;
+        });
+    }
+
+    @Override
+    public void injectSerializerRegistry(SerializerRegistry serializerRegistry) {
+        registry = serializerRegistry;
+    }
+
+    @Override
+    public void registerEntrySerializer(org.opendaylight.openflowplugin.api.openflow.protocol.serialization.MatchEntrySerializerKey key, MatchEntrySerializer serializer) {
+        if (Objects.isNull(key) || Objects.isNull(serializer)) {
+            throw new IllegalArgumentException("MatchEntrySerializerKey or Serializer is null");
+        }
+
+        final MatchEntrySerializer seInRegistry = entryRegistry.put(key, serializer);
+        if (seInRegistry != null) {
+            LOG.debug("Serializer for key {} overwritten. Old serializer: {}, new serializer: {}", key,
+                    seInRegistry.getClass().getName(), serializer.getClass().getName());
+        }
+    }
+
+    @Override
+    public boolean unregisterEntrySerializer(org.opendaylight.openflowplugin.api.openflow.protocol.serialization.MatchEntrySerializerKey key) {
+        return Objects.nonNull(entryRegistry.remove(key));
+    }
+}
diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/protocol/serialization/match/AbstractExperimenterMatchEntrySerializerTest.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/protocol/serialization/match/AbstractExperimenterMatchEntrySerializerTest.java
new file mode 100644 (file)
index 0000000..21efbee
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2016 Pantheon Technologies s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.impl.protocol.serialization.match;
+
+import static org.junit.Assert.assertEquals;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.UnpooledByteBufAllocator;
+import java.util.function.Consumer;
+import org.opendaylight.openflowjava.protocol.api.util.EncodeConstants;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.Match;
+
+public abstract class AbstractExperimenterMatchEntrySerializerTest extends AbstractMatchEntrySerializerTest {
+
+    protected void assertMatch(final Match match, final boolean hasMask, final Consumer<ByteBuf> assertBody) throws Exception {
+        final ByteBuf buffer = UnpooledByteBufAllocator.DEFAULT.buffer();
+        getSerializer().serialize(match, buffer);
+
+        final int length = (hasMask ? 2 : 1) * getLength();
+        final int lengthExp = length + EncodeConstants.SIZE_OF_INT_IN_BYTES; // add length of Experimenter ID
+
+        assertEquals(buffer.readShort(), 1); // OXM_MATCH_TYPE
+        assertEquals(buffer.readShort(), // Total length of match
+                EncodeConstants.SIZE_OF_SHORT_IN_BYTES // OXM_MATCH_TYPE length
+                        + EncodeConstants.SIZE_OF_SHORT_IN_BYTES // LENGTH length
+                        + EncodeConstants.SIZE_OF_SHORT_IN_BYTES // OXM_CLASS_CODE length
+                        + EncodeConstants.SIZE_OF_BYTE_IN_BYTES // OXM field and mask length
+                        + EncodeConstants.SIZE_OF_BYTE_IN_BYTES // OXM field and mask length length
+                        + lengthExp // length of data in match entry
+        );
+
+        assertEquals(buffer.readUnsignedShort(), getOxmClassCode());
+        final short fieldAndMask = buffer.readUnsignedByte();
+        assertEquals(getOxmFieldCode(), fieldAndMask >>> 1);
+        assertEquals(hasMask, (fieldAndMask & 1) != 0);
+        assertEquals(buffer.readUnsignedByte(), lengthExp);
+        assertEquals(buffer.readUnsignedInt(), getExperimenterId());
+        assertBody.accept(buffer);
+
+        int paddingRemainder = lengthExp % EncodeConstants.PADDING;
+
+        if (paddingRemainder != 0) {
+            buffer.skipBytes(EncodeConstants.PADDING - paddingRemainder);
+        }
+
+        assertEquals(buffer.readableBytes(), 0);
+    }
+
+    protected abstract long getExperimenterId();
+
+}
diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/protocol/serialization/match/AbstractMatchEntrySerializerTest.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/protocol/serialization/match/AbstractMatchEntrySerializerTest.java
new file mode 100644 (file)
index 0000000..8815017
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2016 Pantheon Technologies s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.impl.protocol.serialization.match;
+
+import static org.junit.Assert.assertEquals;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.UnpooledByteBufAllocator;
+import java.util.function.Consumer;
+import org.opendaylight.openflowjava.protocol.api.keys.MessageTypeKey;
+import org.opendaylight.openflowjava.protocol.api.util.EncodeConstants;
+import org.opendaylight.openflowplugin.impl.protocol.serialization.AbstractSerializerTest;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.Match;
+
+public abstract class AbstractMatchEntrySerializerTest extends AbstractSerializerTest {
+    private MatchSerializer serializer;
+
+    @Override
+    protected void init() {
+        serializer = getRegistry().getSerializer(new MessageTypeKey<>(EncodeConstants.OF13_VERSION_ID, Match.class));
+    }
+
+    protected void assertMatch(final Match match, final boolean hasMask, final Consumer<ByteBuf> assertBody) throws Exception {
+        final ByteBuf buffer = UnpooledByteBufAllocator.DEFAULT.buffer();
+        serializer.serialize(match, buffer);
+
+        final int length = (hasMask ? 2 : 1) * getLength();
+
+        assertEquals(buffer.readShort(), 1); // OXM_MATCH_TYPE
+        assertEquals(buffer.readShort(), // Total length of match
+                EncodeConstants.SIZE_OF_SHORT_IN_BYTES // OXM_MATCH_TYPE length
+                        + EncodeConstants.SIZE_OF_SHORT_IN_BYTES // LENGTH length
+                        + EncodeConstants.SIZE_OF_SHORT_IN_BYTES // OXM_CLASS_CODE length
+                        + EncodeConstants.SIZE_OF_BYTE_IN_BYTES // OXM field and mask length
+                        + EncodeConstants.SIZE_OF_BYTE_IN_BYTES // OXM field and mask length length
+                        + length // length of data in match entry
+        );
+
+        assertEquals(buffer.readUnsignedShort(), getOxmClassCode());
+        final short fieldAndMask = buffer.readUnsignedByte();
+        assertEquals(getOxmFieldCode(), fieldAndMask >>> 1);
+        assertEquals(hasMask, (fieldAndMask & 1) != 0);
+        assertEquals(buffer.readUnsignedByte(), length);
+        assertBody.accept(buffer);
+
+        int paddingRemainder = length % EncodeConstants.PADDING;
+
+        if (paddingRemainder != 0) {
+            buffer.skipBytes(EncodeConstants.PADDING - paddingRemainder);
+        }
+
+        assertEquals(buffer.readableBytes(), 0);
+    }
+
+    protected MatchSerializer getSerializer() {
+        return serializer;
+    }
+
+    protected abstract int getOxmFieldCode();
+    protected abstract int getOxmClassCode();
+    protected abstract short getLength();
+
+}
\ No newline at end of file