org.opendaylight.sxp.core.messaging.MessageFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.opendaylight.sxp.core.messaging.MessageFactory.java

Source

/*
 * Copyright (c) 2014 Cisco Systems, Inc. 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.sxp.core.messaging;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.Collections2;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import org.opendaylight.sxp.core.Configuration;
import org.opendaylight.sxp.core.messaging.legacy.LegacyMessageFactory;
import org.opendaylight.sxp.util.ArraysUtil;
import org.opendaylight.sxp.util.exception.ErrorCodeDataLengthException;
import org.opendaylight.sxp.util.exception.message.ErrorMessageException;
import org.opendaylight.sxp.util.exception.message.attribute.AddressLengthException;
import org.opendaylight.sxp.util.exception.message.attribute.AttributeLengthException;
import org.opendaylight.sxp.util.exception.message.attribute.AttributeNotFoundException;
import org.opendaylight.sxp.util.exception.message.attribute.AttributeVariantException;
import org.opendaylight.sxp.util.exception.message.attribute.CapabilityLengthException;
import org.opendaylight.sxp.util.exception.message.attribute.HoldTimeMaxException;
import org.opendaylight.sxp.util.exception.message.attribute.HoldTimeMinException;
import org.opendaylight.sxp.util.exception.message.attribute.SecurityGroupTagValueException;
import org.opendaylight.sxp.util.exception.message.attribute.TlvNotFoundException;
import org.opendaylight.sxp.util.exception.unknown.UnknownNodeIdException;
import org.opendaylight.sxp.util.exception.unknown.UnknownPrefixException;
import org.opendaylight.sxp.util.exception.unknown.UnknownSxpMessageTypeException;
import org.opendaylight.sxp.util.exception.unknown.UnknownVersionException;
import org.opendaylight.sxp.util.filtering.SxpBindingFilter;
import org.opendaylight.sxp.util.inet.NodeIdConv;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.Sgt;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.SxpBindingFields;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.peer.sequence.fields.PeerSequence;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.AttributeType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.CapabilityType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.ConnectionMode;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.ErrorCode;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.ErrorCodeNonExtended;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.ErrorSubCode;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.ErrorType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.MessageType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.NodeId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.SxpHeader;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.SxpPayload;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.Version;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.attributes.fields.Attribute;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.attributes.fields.attribute.attribute.optional.fields.CapabilitiesAttribute;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.attributes.fields.attribute.attribute.optional.fields.capabilities.attribute.capabilities.attributes.Capabilities;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.sxp.messages.ErrorMessageBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.sxp.messages.KeepaliveMessageBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.sxp.messages.Notification;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.sxp.messages.OpenMessage;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.sxp.messages.OpenMessageBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.sxp.messages.PurgeAllMessageBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.sxp.messages.UpdateMessageBuilder;

import javax.annotation.Nullable;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

/**
 * MessageFactory class contains logic for creating and parsing messages
 */
public class MessageFactory {

    public static final int MESSAGE_HEADER_LENGTH_LENGTH = Configuration.getConstants()
            .getMessageHeaderLengthLength();

    public static final int MESSAGE_HEADER_TYPE_LENGTH = Configuration.getConstants().getMessageHeaderTypeLength();

    public static final int MESSAGE_LENGTH_MAX = Configuration.getConstants().getMessageLengthMax();

    /**
     * Creates Error message based on provided error code
     *
     * @param errorCode    ErrorCodeNonExtended defining type of error message
     * @param errorSubCode ErrorSubCode defining error  sub type
     * @param data         Byte Array containing additional information
     * @return ByteBuf representation of Error
     * @throws ErrorCodeDataLengthException If message Length is to long
     */
    public static ByteBuf createError(ErrorCode errorCode, ErrorSubCode errorSubCode, byte[] data)
            throws ErrorCodeDataLengthException {
        if (data == null) {
            data = new byte[0];
        } else if (data.length > 10) {
            throw new ErrorCodeDataLengthException("Variable message length - 10 bytes.");
        }

        byte _errorCode = errorCode != null ? (byte) errorCode.getIntValue() : 0x00;
        byte _errorSubCode = errorSubCode != null ? (byte) errorSubCode.getIntValue() : 0x00;
        byte[] payload = ArraysUtil
                .combine(new byte[] { ArraysUtil.setBit(_errorCode, 8, true), _errorSubCode, 0x00, 0x00 }, data);
        return getMessage(MessageType.Error, payload);
    }

    /**
     * @return Generated KeepAlive message
     */
    public static ByteBuf createKeepalive() {
        return getMessage(MessageType.Keepalive, new byte[0]);
    }

    /**
     * Creates OpenMessage using provided values
     *
     * @param version   Version included in message
     * @param nodeMode  ConnectionMode included in message
     * @param nodeID    NodeId included in message
     * @param attribute Attribute included in message
     * @return ByteBuf representation of created OpenMessage
     * @throws AttributeVariantException If attribute variant isn't supported
     * @throws UnknownVersionException   If version isn't supported
     * @throws CapabilityLengthException If some Attributes has incorrect length
     */
    private static ByteBuf createOpen(Version version, ConnectionMode nodeMode, NodeId nodeID, Attribute attribute)
            throws AttributeVariantException, UnknownVersionException, CapabilityLengthException {
        AttributeList attributes = createOpenAttribute(version, nodeMode, nodeID);
        if (attribute != null) {
            attributes.add(attribute);
        }
        // Add optional attributes..
        byte[] payload = ArraysUtil.combine(new byte[] { 0x00, 0x00, 0x00, (byte) version.getIntValue(), 0x00, 0x00,
                0x00, (byte) nodeMode.getIntValue() }, attributes.toBytes());
        return getMessage(MessageType.Open, payload);
    }

    /**
     * Creates OpenMessage using provided values
     *
     * @param version               Version included in message
     * @param nodeMode              ConnectionMode included in message
     * @param nodeID                NodeId included in message
     * @param holdTimeMinAcceptable Minimal acceptable Hold time included in message
     * @return ByteBuf representation of created OpenMessage
     * @throws HoldTimeMinException      If Min hold time isn't in range of [0, 65535]
     * @throws AttributeVariantException If attribute variant isn't supported
     * @throws UnknownVersionException   If version isn't supported
     * @throws CapabilityLengthException If some Attributes has incorrect length
     */
    public static ByteBuf createOpen(Version version, ConnectionMode nodeMode, NodeId nodeID,
            int holdTimeMinAcceptable) throws HoldTimeMinException, AttributeVariantException,
            UnknownVersionException, CapabilityLengthException {
        return createOpen(version, nodeMode, nodeID, AttributeFactory.createHoldTime(holdTimeMinAcceptable));
    }

    /**
     * Creates OpenMessage using provided values
     *
     * @param version     Version included in message
     * @param nodeMode    ConnectionMode included in message
     * @param nodeID      NodeId included in message
     * @param holdTimeMin Minimal Hold time included in message
     * @param holdTimeMax Maximal Hold time included in message
     * @return ByteBuf representation of created OpenMessage
     * @throws HoldTimeMaxException      If Max hold time is greater than minimal
     * @throws HoldTimeMinException      If Min hold time isn't in range of [0, 65535]
     * @throws AttributeVariantException If attribute variant isn't supported
     * @throws UnknownVersionException   If version isn't supported
     * @throws CapabilityLengthException If some Attributes has incorrect length
     */
    public static ByteBuf createOpen(Version version, ConnectionMode nodeMode, NodeId nodeID, int holdTimeMin,
            int holdTimeMax) throws HoldTimeMaxException, HoldTimeMinException, AttributeVariantException,
            UnknownVersionException, CapabilityLengthException {
        return createOpen(version, nodeMode, nodeID, AttributeFactory.createHoldTime(holdTimeMin, holdTimeMax));
    }

    /**
     * Creates Attribute into OpenMessage according to connection mode and version
     *
     * @param version  Version of connection
     * @param nodeMode ConnectionMode of connection
     * @param nodeID   NodeId included in message
     * @return List of Attributes
     * @throws UnknownVersionException   If version isn't supported
     * @throws CapabilityLengthException If some Attributes has incorrect length
     */
    private static AttributeList createOpenAttribute(Version version, ConnectionMode nodeMode, NodeId nodeID)
            throws UnknownVersionException, CapabilityLengthException {
        AttributeList attributes = new AttributeList();
        if (nodeMode.equals(ConnectionMode.Speaker)) {
            attributes.add(AttributeFactory.createSxpNodeId(nodeID));
        } else if (nodeMode.equals(ConnectionMode.Listener)) {
            attributes.add(AttributeFactory.createCapabilities(version));
        }
        return attributes;
    }

    /**
     * Creates OpenRespMessage using provided values
     *
     * @param version   Version included in message
     * @param nodeMode  ConnectionMode included in message
     * @param nodeID    NodeId included in message
     * @param attribute Attribute included in message
     * @return ByteBuff representation of created message
     * @throws UnknownVersionException   If version isn't supported
     * @throws CapabilityLengthException If some Attributes has incorrect length
     * @throws AttributeVariantException If attribute variant isn't supported
     */
    private static ByteBuf createOpenResp(Version version, ConnectionMode nodeMode, NodeId nodeID,
            Attribute attribute)
            throws UnknownVersionException, CapabilityLengthException, AttributeVariantException {
        AttributeList attributes = createOpenAttribute(version, nodeMode, nodeID);
        if (attribute != null) {
            attributes.add(attribute);
        }
        // Add optional attributes..
        byte[] payload = ArraysUtil.combine(new byte[] { 0x00, 0x00, 0x00, (byte) version.getIntValue(), 0x00, 0x00,
                0x00, (byte) nodeMode.getIntValue() }, attributes.toBytes());
        return getMessage(MessageType.OpenResp, payload);
    }

    /**
     * Creates OpenRespMessage using provided values
     *
     * @param version  Version included in message
     * @param nodeMode ConnectionMode included in message
     * @param nodeID   NodeId included in message
     * @return ByteBuff representation of created message
     * @throws UnknownVersionException   If version isn't supported
     * @throws CapabilityLengthException If some Attributes has incorrect length
     * @throws AttributeVariantException If attribute variant isn't supported
     */
    public static ByteBuf createOpenResp(Version version, ConnectionMode nodeMode, NodeId nodeID)
            throws CapabilityLengthException, UnknownVersionException, AttributeVariantException {
        return createOpenResp(version, nodeMode, nodeID, null);
    }

    /**
     * Creates OpenRespMessage using provided values
     *
     * @param version               Version included in message
     * @param nodeMode              ConnectionMode included in message
     * @param nodeID                NodeId included in message
     * @param holdTimeMinAcceptable Minimal acceptable Hold time included in message
     * @return ByteBuff representation of OpenRespMessage
     * @throws HoldTimeMinException      If Min hold time isn't in range of [0, 65535]
     * @throws UnknownVersionException   If version isn't supported
     * @throws CapabilityLengthException If some Attributes has incorrect length
     * @throws AttributeVariantException If attribute variant isn't supported
     */
    public static ByteBuf createOpenResp(Version version, ConnectionMode nodeMode, NodeId nodeID,
            int holdTimeMinAcceptable) throws HoldTimeMinException, CapabilityLengthException,
            UnknownVersionException, AttributeVariantException {
        return createOpenResp(version, nodeMode, nodeID, AttributeFactory.createHoldTime(holdTimeMinAcceptable));
    }

    /**
     * Creates OpenRespMessage using provided values
     *
     * @param version     Version included in message
     * @param nodeMode    ConnectionMode included in message
     * @param nodeID      NodeId included in message
     * @param holdTimeMin Minimal Hold time included in message
     * @param holdTimeMax Maximal Hold time included in message
     * @return ByteBuff representation of OpenRespMessage
     * @throws HoldTimeMaxException      If Max hold time is greater than minimal
     * @throws HoldTimeMinException      If Min hold time isn't in range of [0, 65535]
     * @throws UnknownVersionException   If version isn't supported
     * @throws CapabilityLengthException If some Attributes has incorrect length
     * @throws AttributeVariantException If attribute variant isn't supported
     */
    public static ByteBuf createOpenResp(Version version, ConnectionMode nodeMode, NodeId nodeID, int holdTimeMin,
            int holdTimeMax) throws HoldTimeMaxException, HoldTimeMinException, CapabilityLengthException,
            UnknownVersionException, AttributeVariantException {
        return createOpenResp(version, nodeMode, nodeID, AttributeFactory.createHoldTime(holdTimeMin, holdTimeMax));
    }

    /**
     * @return Generate PurgeAll message
     */
    public static ByteBuf createPurgeAll() {
        return getMessage(MessageType.PurgeAll, new byte[0]);
    }

    /**
     * Creates UpdateMessage using provided values
     *
     * @param deleteBindings Bindings that will be deleted
     * @param addBindings    Bindings that will be added
     * @param nodeId         NodeId included in message
     * @return ByteBuf representation of UpdateMessage
     * @throws SecurityGroupTagValueException      If some Sgt isn't in rage [2, 65519]
     * @throws AttributeVariantException           If some attribute variant isn't supported
     */
    public static <R extends SxpBindingFields, T extends SxpBindingFields> ByteBuf createUpdate(
            List<R> deleteBindings, List<T> addBindings, NodeId nodeId, List<CapabilityType> capabilities,
            SxpBindingFilter bindingFilter) throws SecurityGroupTagValueException, AttributeVariantException {
        AttributeList attributes = new AttributeList();
        List<IpPrefix> ipv4Prefixes = new ArrayList<>();
        List<IpPrefix> ipv6Prefixes = new ArrayList<>();

        // Processing of binding delete attributes.
        if (deleteBindings != null && !deleteBindings.isEmpty()) {
            deleteBindings.stream().forEach(b -> {
                if (b.getIpPrefix().getIpv4Prefix() != null) {
                    ipv4Prefixes.add(b.getIpPrefix());
                } else if (b.getIpPrefix().getIpv6Prefix() != null) {
                    ipv6Prefixes.add(b.getIpPrefix());
                }
            });
            // Binding delete attributes include any of IPv4-Del-Prefix,
            // IPv6-Del-Prefix, Del-IPv4, or Del-IPv6 attributes.
            if (!ipv4Prefixes.isEmpty()) {
                attributes.add(AttributeFactory.createIpv4DeletePrefix(ipv4Prefixes, AttributeFactory._oNpCe));
                ipv4Prefixes.clear();
            }
            if (!ipv6Prefixes.isEmpty()) {
                attributes.add(AttributeFactory.createIpv6DeletePrefix(ipv6Prefixes, AttributeFactory._oNpCe));
                ipv6Prefixes.clear();
            }
        }

        // Processing of binding add attributes.
        if (addBindings != null && !addBindings.isEmpty()) {
            Sgt sgt = null;
            PeerSequence peerSequence = null;
            for (T binding : addBindings) {
                if (bindingFilter != null && bindingFilter.apply(binding))
                    continue;
                if ((!binding.getPeerSequence().equals(peerSequence)
                        || !binding.getSecurityGroupTag().equals(sgt))) {

                    if (!ipv4Prefixes.isEmpty() && sgt != null && peerSequence != null) {
                        attributes.add(AttributeFactory.createIpv4AddPrefix(ipv4Prefixes,
                                capabilities.contains(CapabilityType.Ipv4Unicast) ? AttributeFactory._oNpCe
                                        : AttributeFactory._OnpCe));
                        ipv4Prefixes.clear();
                    }
                    if (!ipv6Prefixes.isEmpty() && sgt != null && peerSequence != null) {
                        attributes.add(AttributeFactory.createIpv6AddPrefix(ipv6Prefixes,
                                capabilities.contains(CapabilityType.Ipv6Unicast) ? AttributeFactory._oNpCe
                                        : AttributeFactory._OnpCe));
                        ipv6Prefixes.clear();
                    }

                    List<NodeId> peers = NodeIdConv.getPeerSequence(binding.getPeerSequence());
                    peers.add(0, nodeId);
                    attributes.add(AttributeFactory.createPeerSequence(peers));
                    attributes.add(AttributeFactory.createSourceGroupTag(binding.getSecurityGroupTag().getValue()));

                    sgt = binding.getSecurityGroupTag();
                    peerSequence = binding.getPeerSequence();
                }

                if (binding.getIpPrefix().getIpv4Prefix() != null) {
                    ipv4Prefixes.add(binding.getIpPrefix());
                } else if (binding.getIpPrefix().getIpv6Prefix() != null) {
                    ipv6Prefixes.add(binding.getIpPrefix());
                }

            }
            if (!ipv4Prefixes.isEmpty()) {
                attributes.add(AttributeFactory.createIpv4AddPrefix(ipv4Prefixes,
                        capabilities.contains(CapabilityType.Ipv4Unicast) ? AttributeFactory._oNpCe
                                : AttributeFactory._OnpCe));
                ipv4Prefixes.clear();
            }
            if (!ipv6Prefixes.isEmpty()) {
                attributes.add(AttributeFactory.createIpv6AddPrefix(ipv6Prefixes,
                        capabilities.contains(CapabilityType.Ipv6Unicast) ? AttributeFactory._oNpCe
                                : AttributeFactory._OnpCe));
                ipv6Prefixes.clear();
            }
        }
        return getMessage(MessageType.Update, attributes.toBytes());
    }

    /**
     * Decodes Byte Array into specific message
     *
     * @param version    Version used for decoding
     * @param headerType Type of header
     * @param payload    Byte Array containing message
     * @return Decoded message
     * @throws ErrorMessageException          If version Mismatch occurs
     * @throws UnknownPrefixException         If some attribute has incorrect or none Prefix
     * @throws AddressLengthException         If address length of some attribute is incorrect
     * @throws AttributeLengthException       If length of some attribute is incorrect
     * @throws UnknownHostException           If some attribute have incorrect or none address
     * @throws UnknownNodeIdException         If NodeId isn't found or is incorrect
     * @throws TlvNotFoundException           If Tvl isn't found
     * @throws UnknownSxpMessageTypeException If data contains unsupported message
     */
    private static Notification decode(Version version, byte[] headerType, byte[] payload)
            throws ErrorMessageException, UnknownPrefixException, AddressLengthException, AttributeLengthException,
            UnknownHostException, UnknownNodeIdException, TlvNotFoundException, UnknownSxpMessageTypeException,
            AttributeVariantException {
        MessageType messageType = MessageType.forValue(ArraysUtil.bytes2int(headerType));

        // Remote can send OpenResp with different version
        if (messageType == MessageType.OpenResp || messageType == MessageType.Open) {
            final Version remoteVersion = extractVersion(payload);
            // Override version setting for parsing
            if (remoteVersion != version) {
                version = remoteVersion;
            }
        }

        if (isLegacy(version)) {
            switch (messageType) {
            case Open:
                return LegacyMessageFactory.decodeOpen(payload);
            case OpenResp:
                return LegacyMessageFactory.decodeOpenResp(payload);
            case Update:
                return LegacyMessageFactory.decodeUpdate(payload);
            case Error:
                return decodeErrorMessage(payload);
            case PurgeAll:
                return decodePurgeAll(payload);
            default:
                break;
            }
        } else if (version.equals(Version.Version4)) {
            switch (messageType) {
            case Open:
                return decodeOpen(payload);
            case OpenResp:
                return decodeOpenResp(payload);
            case Update:
                return decodeUpdate(payload);
            case Error:
                return decodeErrorMessage(payload);
            case PurgeAll:
                return decodePurgeAll(payload);
            case Keepalive:
                return decodeKeepalive(payload);
            }
        }
        throw new UnknownSxpMessageTypeException();
    }

    /**
     * Decode ErrorMessage from Byte Array
     *
     * @param payload Byte Array containing message
     * @return Decoded Error message
     */
    public static Notification decodeErrorMessage(byte[] payload) {
        ErrorMessageBuilder messageBuilder = new ErrorMessageBuilder();
        messageBuilder.setType(MessageType.Error);
        messageBuilder.setLength(MESSAGE_HEADER_LENGTH_LENGTH + MESSAGE_HEADER_TYPE_LENGTH + payload.length);
        messageBuilder.setPayload(payload);

        if (ArraysUtil.getBit(payload[0], 8) == 1) {
            messageBuilder.setErrorType(ErrorType.Extended);
            messageBuilder.setErrorCode(ErrorCode.forValue(payload[0] & 0x7F));
            messageBuilder.setErrorSubCode(ErrorSubCode.forValue(payload[1]));
            messageBuilder.setData(ArraysUtil.readBytes(payload, 2));
            messageBuilder.setInformation(messageBuilder.getErrorCode() + " | " + messageBuilder.getErrorSubCode()
                    + getInformation(messageBuilder.getData()));
            return messageBuilder.build();
        }

        messageBuilder.setErrorType(ErrorType.Legacy);
        messageBuilder.setErrorCodeNonExtended(
                ErrorCodeNonExtended.forValue(ArraysUtil.bytes2int(ArraysUtil.readBytes(payload, 2, 2))));
        messageBuilder.setData(ArraysUtil.readBytes(payload, 4));
        messageBuilder.setInformation(
                messageBuilder.getErrorCodeNonExtended() + getInformation(messageBuilder.getData()));
        return messageBuilder.build();
    }

    /**
     * Decodes KeepAliveMessage from byte Array
     *
     * @param payload Byte Array containing message
     * @return Decoded KeepAlive message
     */
    public static Notification decodeKeepalive(byte[] payload) {
        KeepaliveMessageBuilder messageBuilder = new KeepaliveMessageBuilder();
        messageBuilder.setType(MessageType.Keepalive);
        messageBuilder.setLength(MESSAGE_HEADER_LENGTH_LENGTH + MESSAGE_HEADER_TYPE_LENGTH + payload.length);
        return messageBuilder.build();
    }

    /**
     * Decodes OpenMessage from Byte Array
     *
     * @param payload Byte Array containing message
     * @return Decoded Open message
     * @throws AddressLengthException   If address length of some attribute is incorrect
     * @throws AttributeLengthException If length of some attribute is incorrect
     * @throws UnknownNodeIdException   If NodeId isn't found or is incorrect
     * @throws UnknownPrefixException   If some attribute has incorrect or none Prefix
     * @throws TlvNotFoundException     If Tvl isn't found
     * @throws UnknownHostException     If some attribute have incorrect or none address
     */
    public static Notification decodeOpen(byte[] payload)
            throws AttributeLengthException, AddressLengthException, UnknownNodeIdException, UnknownPrefixException,
            TlvNotFoundException, UnknownHostException, AttributeVariantException {
        OpenMessageBuilder messageBuilder = new OpenMessageBuilder();
        messageBuilder.setType(MessageType.Open);
        messageBuilder.setLength(MESSAGE_HEADER_LENGTH_LENGTH + MESSAGE_HEADER_TYPE_LENGTH + payload.length);
        messageBuilder.setPayload(payload);

        Version version = extractVersion(payload);
        ConnectionMode nodeMode = ConnectionMode
                .forValue(ArraysUtil.bytes2int(ArraysUtil.readBytes(payload, 4, 4)));
        AttributeList attributes = AttributeList.decode(ArraysUtil.readBytes(payload, 8));

        messageBuilder.setVersion(version);
        messageBuilder.setSxpMode(nodeMode);
        messageBuilder.setAttribute(attributes);
        return messageBuilder.build();
    }

    /**
     * Decode OpenRespMessage from Byre Array
     *
     * @param payload Byte Array containing message
     * @return Decoded OpenResp message
     * @throws AddressLengthException   If address length of some attribute is incorrect
     * @throws AttributeLengthException If length of some attribute is incorrect
     * @throws UnknownNodeIdException   If NodeId isn't found or is incorrect
     * @throws UnknownPrefixException   If some attribute has incorrect or none Prefix
     * @throws TlvNotFoundException     If Tvl isn't found
     * @throws UnknownHostException     If some attribute have incorrect or none address
     */
    public static Notification decodeOpenResp(byte[] payload)
            throws AttributeLengthException, AddressLengthException, UnknownNodeIdException, UnknownPrefixException,
            TlvNotFoundException, UnknownHostException, ErrorMessageException, AttributeVariantException {
        OpenMessageBuilder messageBuilder = new OpenMessageBuilder();
        messageBuilder.setType(MessageType.OpenResp);
        messageBuilder.setLength(MESSAGE_HEADER_LENGTH_LENGTH + MESSAGE_HEADER_TYPE_LENGTH + payload.length);
        messageBuilder.setPayload(payload);

        Version version = extractVersion(payload);
        ConnectionMode nodeMode = ConnectionMode
                .forValue(ArraysUtil.bytes2int(ArraysUtil.readBytes(payload, 4, 4)));
        AttributeList attributes = AttributeList.decode(ArraysUtil.readBytes(payload, 8));

        messageBuilder.setVersion(version);
        messageBuilder.setSxpMode(nodeMode);
        messageBuilder.setAttribute(attributes);
        return messageBuilder.build();
    }

    /**
     * @param payload Byte Array containing message
     * @return Gets Version from message header
     */
    public static Version extractVersion(final byte[] payload) {
        return Version.forValue(ArraysUtil.bytes2int(ArraysUtil.readBytes(payload, 0, 4)));
    }

    /**
     * Decodes PurgeAll message fom provided Byte Array
     *
     * @param payload Byte Array containing message
     * @return Decoded PurgeAll message
     */
    public static Notification decodePurgeAll(byte[] payload) {
        PurgeAllMessageBuilder messageBuilder = new PurgeAllMessageBuilder();
        messageBuilder.setType(MessageType.PurgeAll);
        messageBuilder.setLength(MESSAGE_HEADER_LENGTH_LENGTH + MESSAGE_HEADER_TYPE_LENGTH + payload.length);
        return messageBuilder.build();
    }

    /**
     * Decode UpdateMessage from provided Byte Array
     *
     * @param payload Byte Array containing message
     * @return Notification with decoded UpdateMessage
     * @throws AddressLengthException   If address length of some attribute is incorrect
     * @throws AttributeLengthException If length of some attribute is incorrect
     * @throws UnknownNodeIdException   If NodeId isn't found or is incorrect
     * @throws UnknownPrefixException   If some attribute has incorrect or none Prefix
     * @throws TlvNotFoundException     If Tvl isn't found
     * @throws UnknownHostException     If some attribute have incorrect or none address
     */
    public static Notification decodeUpdate(byte[] payload)
            throws AttributeLengthException, AddressLengthException, UnknownNodeIdException, UnknownPrefixException,
            TlvNotFoundException, UnknownHostException, AttributeVariantException {
        UpdateMessageBuilder messageBuilder = new UpdateMessageBuilder();
        messageBuilder.setType(MessageType.Update);
        messageBuilder.setLength(MESSAGE_HEADER_LENGTH_LENGTH + MESSAGE_HEADER_TYPE_LENGTH + payload.length);
        messageBuilder.setPayload(payload);

        messageBuilder.setAttribute(AttributeList.decode(payload));
        return messageBuilder.build();
    }

    /**
     * @param data Data to be analyzed
     * @return Gets each element in one String
     */
    private static String getInformation(byte[] data) {
        if (data == null || data.length == 0) {
            return "";
        }
        String result = " | ";
        for (byte aData : data) {
            result += aData + " ";
        }
        return result.trim();
    }

    /**
     * Generate Message header according message type and create ByteBuff,
     * that includes generated header and provided payload
     *
     * @param messageType Type of message header to be generated
     * @param payload     Data to be included into Message
     * @return ByteBuf representation of message
     */
    protected static ByteBuf getMessage(MessageType messageType, byte[] payload) {
        byte[] header = getMessageHeader(messageType, payload.length);
        int messageLength = header.length + payload.length;
        ByteBuf message = PooledByteBufAllocator.DEFAULT.buffer(messageLength, messageLength);
        message.writeBytes(header);
        message.writeBytes(payload);
        return message;
    }

    /**
     * Generate message header using provided values
     *
     * @param messageType Type of header
     * @param payloadLength Length of data
     * @return Byte array representing message header
     */
    private static byte[] getMessageHeader(MessageType messageType, int payloadLength) {
        return ArraysUtil.combine(
                ArraysUtil.int2bytes(MESSAGE_HEADER_LENGTH_LENGTH + MESSAGE_HEADER_TYPE_LENGTH + payloadLength),
                new byte[] { 0x00, 0x00, 0x00, (byte) messageType.getIntValue() });
    }

    /**
     * @param version Version to be checked
     * @return If is Version 1/2/3
     */
    protected static boolean isLegacy(Version version) {
        return version.equals(Version.Version1) || version.equals(Version.Version2)
                || version.equals(Version.Version3);

    }

    /**
     * Decode received message into specific message type
     *
     * @param version Version used for decoding
     * @param request ByteBuf containing data to be decoded
     * @return Decoded message
     * @throws ErrorMessageException          If version Mismatch occurs
     * @throws UnknownSxpMessageTypeException If data contains unsupported message
     * @throws AddressLengthException         If address length of some attribute is incorrect
     * @throws UnknownHostException           If some attribute have incorrect or none address
     * @throws AttributeLengthException       If length of some attribute is incorrect
     * @throws TlvNotFoundException           If Tvl isn't found
     * @throws UnknownPrefixException         If some attribute has incorrect or none Prefix
     * @throws UnknownNodeIdException         If NodeId isn't found or is incorrect
     */
    public static Notification parse(Version version, ByteBuf request) throws ErrorMessageException,
            UnknownSxpMessageTypeException, AddressLengthException, UnknownHostException, AttributeLengthException,
            TlvNotFoundException, UnknownPrefixException, UnknownNodeIdException, AttributeVariantException {
        request.resetReaderIndex();
        byte[] headerLength, headerType, payload;
        int messageLength;
        try {
            headerLength = new byte[MESSAGE_HEADER_LENGTH_LENGTH];
            request = request.readBytes(headerLength);

            headerType = new byte[MESSAGE_HEADER_TYPE_LENGTH];
            request = request.readBytes(headerType);

            messageLength = ArraysUtil.bytes2int(headerLength);
            int payloadLength = messageLength
                    - (MESSAGE_HEADER_LENGTH_LENGTH + Configuration.getConstants().getMessageHeaderTypeLength());

            payload = new byte[payloadLength];
            request = request.readBytes(payload);
        } catch (IndexOutOfBoundsException | NegativeArraySizeException e) {
            throw new ErrorMessageException(ErrorCode.MessageHeaderError, e);
        }

        validate(headerLength.length + headerType.length, payload.length, messageLength);
        return decode(version, headerType, payload);
    }

    /**
     * @param message Notification to be proceed
     * @return Gets String representation of Byte Array
     */
    public static String toString(byte[] message) {
        String result = "";
        for (int i = 0; i < message.length; i++) {
            if (i == MESSAGE_HEADER_LENGTH_LENGTH + MESSAGE_HEADER_TYPE_LENGTH) {
                result += "| ";
            }
            result += (0xFF & message[i]) + " ";
        }
        return result;
    }

    /**
     * @param message Notification to be proceed
     * @return Gets String representation of ByteBuf
     */
    public static String toString(ByteBuf message) {
        message.resetReaderIndex();
        byte[] _message = new byte[message.readableBytes()];
        message.readBytes(_message);
        message.resetReaderIndex();
        return toString(_message);
    }

    /**
     * @param message Notification to be proceed
     * @return Gets String representation of Notification
     */
    public static String toString(Notification message) {
        String result = "Unrecognized";
        byte[] _message = new byte[0];
        if (message instanceof SxpHeader) {
            MessageType messageType = ((SxpHeader) message).getType();
            if (messageType.equals(MessageType.OpenResp)) {
                result = "RESP";
            } else {
                result = messageType.toString().toUpperCase();
            }

            byte[] length = ArraysUtil.int2bytes(((SxpHeader) message).getLength());
            byte[] type = ArraysUtil.int2bytes(messageType.getIntValue());
            _message = ArraysUtil.combine(_message, length, type);
        }
        if (message instanceof SxpPayload) {
            _message = ArraysUtil.combine(_message, ((SxpPayload) message).getPayload());
        }
        return result + " " + toString(_message);
    }

    /**
     * Check if length of message is correct
     *
     * @param headerLength  Length of header
     * @param payloadLength Length od data
     * @param messageLength Total length
     * @throws ErrorMessageException If lengths are incorrect
     */
    private static void validate(int headerLength, int payloadLength, int messageLength)
            throws ErrorMessageException {
        if (headerLength + payloadLength > MESSAGE_LENGTH_MAX) {
            throw new ErrorMessageException(ErrorCode.MessageHeaderError,
                    new Exception("Message maximum length exceeded"));

        } else if (headerLength + payloadLength != messageLength) {
            throw new ErrorMessageException(ErrorCode.MessageHeaderError,
                    new Exception("Message incorporated length is not consistent"));
        }
    }

    /**
     * Decode Capabilities received from remote peer
     *
     * @param message Message containing capabilities
     * @return List of capabilities
     * @throws AttributeNotFoundException If no Capabilities were found
     */
    public static List<CapabilityType> decodeCapabilities(OpenMessage message) throws AttributeNotFoundException {
        CapabilitiesAttribute capabilitiesAttribute = (CapabilitiesAttribute) AttributeList
                .get(Preconditions.checkNotNull(message).getAttribute(), AttributeType.Capabilities);
        return new ArrayList<>(
                Collections2.transform(capabilitiesAttribute.getCapabilitiesAttributes().getCapabilities(),
                        new Function<Capabilities, CapabilityType>() {

                            @Nullable
                            @Override
                            public CapabilityType apply(Capabilities input) {
                                return input.getCode();
                            }
                        }));
    }
}