org.opendaylight.sxp.core.SxpNode.java Source code

Java tutorial

Introduction

Here is the source code for org.opendaylight.sxp.core.SxpNode.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;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.Sets;
import com.google.common.net.InetAddresses;
import com.google.common.util.concurrent.ListenableScheduledFuture;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelPromiseNotifier;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nullable;
import org.opendaylight.sxp.core.handler.ConnectionDecoder;
import org.opendaylight.sxp.core.handler.HandlerFactory;
import org.opendaylight.sxp.core.handler.MessageDecoder;
import org.opendaylight.sxp.core.service.BindingDispatcher;
import org.opendaylight.sxp.core.service.BindingHandler;
import org.opendaylight.sxp.core.service.ConnectFacade;
import org.opendaylight.sxp.core.threading.ThreadsWorker;
import org.opendaylight.sxp.util.Security;
import org.opendaylight.sxp.util.database.MasterDatabaseImpl;
import org.opendaylight.sxp.util.database.SxpDatabase;
import org.opendaylight.sxp.util.database.SxpDatabaseImpl;
import org.opendaylight.sxp.util.database.spi.MasterDatabaseInf;
import org.opendaylight.sxp.util.database.spi.SxpDatabaseInf;
import org.opendaylight.sxp.util.exception.node.DomainNotFoundException;
import org.opendaylight.sxp.util.exception.unknown.UnknownTimerTypeException;
import org.opendaylight.sxp.util.filtering.SxpBindingFilter;
import org.opendaylight.sxp.util.inet.NodeIdConv;
import org.opendaylight.sxp.util.inet.Search;
import org.opendaylight.sxp.util.time.SxpTimerTask;
import org.opendaylight.sxp.util.time.node.RetryOpenTimerTask;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.master.database.fields.MasterDatabaseBinding;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.filter.rev150911.FilterSpecific;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.filter.rev150911.FilterType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.filter.rev150911.SxpFilterFields;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.filter.rev150911.sxp.peer.group.SxpPeerGroup;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.filter.rev150911.sxp.peer.group.SxpPeerGroupBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.filter.rev150911.sxp.peer.group.fields.SxpFilter;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.filter.rev150911.sxp.peer.group.fields.sxp.peers.SxpPeer;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.SxpNodeIdentity;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.SxpNodeIdentityBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.TimerType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.sxp.connections.fields.Connections;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.sxp.connections.fields.connections.Connection;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.sxp.domain.fields.domain.filters.DomainFilter;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.sxp.node.fields.SecurityBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.ConnectionMode;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.NodeId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.Version;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The source-group tag exchange protocol (SXP) aware node implementation. SXP
 * is a control protocol to propagate IP address to Source Group Tag (SGT)
 * binding information across network devices.
 * Source groups are the endpoints connecting to the network that have common
 * network policies. Each source group is identified by a unique SGT value. The
 * SGT to which an endpoint belongs can be assigned statically or dynamically,
 * and the SGT can be used as a classifier in network policies.
 */
public class SxpNode {

    protected static final Logger LOG = LoggerFactory.getLogger(SxpNode.class.getName());
    protected static final long THREAD_DELAY = 10;
    public static String DEFAULT_DOMAIN = "global";

    /**
     * Create new instance of SxpNode with empty databases
     * and default ThreadWorkers
     *
     * @param nodeId ID of newly created Node
     * @param node   Node setup data
     * @return New instance of SxpNode
     */
    public static SxpNode createInstance(NodeId nodeId, SxpNodeIdentity node) {
        return createInstance(nodeId, node, new MasterDatabaseImpl(), new SxpDatabaseImpl(), new ThreadsWorker());
    }

    /**
     * Create new instance of SxpNode containing provided database data
     * and default ThreadWorkers
     * Be aware that sharing of the same DB among multiple SxpNode isn't
     * supported and may cause unexpected behaviour
     *
     * @param nodeId         ID of newly created Node
     * @param node           Node setup data
     * @param masterDatabase Data which will be added to Master-DB
     * @param sxpDatabase    Data which will be added to SXP-DB
     * @return New instance of SxpNode
     */
    public static SxpNode createInstance(NodeId nodeId, SxpNodeIdentity node, MasterDatabaseInf masterDatabase,
            SxpDatabaseInf sxpDatabase) {
        return createInstance(nodeId, node, masterDatabase, sxpDatabase, new ThreadsWorker());
    }

    /**
     * Create new instance of SxpNode containing provided database data
     * and custom ThreadWorkers
     * Be aware that sharing of the same DB among multiple SxpNode isn't
     * supported and may cause unexpected behaviour
     *
     * @param nodeId         ID of newly created Node
     * @param node           Node setup data
     * @param masterDatabase Data which will be added to Master-DB
     * @param sxpDatabase    Data which will be added to SXP-DB
     * @param worker         Thread workers which will be executing task inside SxpNode
     * @return New instance of SxpNode
     */
    public static SxpNode createInstance(NodeId nodeId, SxpNodeIdentity node, MasterDatabaseInf masterDatabase,
            SxpDatabaseInf sxpDatabase, ThreadsWorker worker) {
        Preconditions.checkNotNull(sxpDatabase);
        Preconditions.checkNotNull(masterDatabase);
        SxpNode sxpNode = new SxpNode(nodeId, node, worker);
        if (node.getSxpDomains() != null && node.getSxpDomains().getSxpDomain() != null) {
            node.getSxpDomains().getSxpDomain().forEach(sxpNode::addDomain);
        }
        if (!sxpNode.sxpDomains.containsKey(DEFAULT_DOMAIN))
            sxpNode.sxpDomains.put(DEFAULT_DOMAIN,
                    SxpDomain.createInstance(sxpNode, DEFAULT_DOMAIN, sxpDatabase, masterDatabase));
        if (node.getSxpPeerGroups() != null && node.getSxpPeerGroups().getSxpPeerGroup() != null) {
            node.getSxpPeerGroups().getSxpPeerGroup()
                    .forEach(g -> sxpNode.addPeerGroup(new SxpPeerGroupBuilder(g).build()));
        }
        sxpNode.handlerFactoryServer.addDecoder(new ConnectionDecoder(sxpNode), HandlerFactory.Position.Begin);
        return sxpNode;
    }

    protected final HandlerFactory handlerFactoryClient = HandlerFactory
            .instanceAddDecoder(MessageDecoder.createClientProfile(this), HandlerFactory.Position.End);
    protected final HandlerFactory handlerFactoryServer = HandlerFactory
            .instanceAddDecoder(MessageDecoder.createServerProfile(this), HandlerFactory.Position.End);

    private SxpNodeIdentityBuilder nodeBuilder;
    private NodeId nodeId;

    private Channel serverChannel;
    protected InetAddress sourceIp;

    protected final BindingDispatcher svcBindingDispatcher;
    protected final BindingHandler svcBindingHandler;
    private final ThreadsWorker worker;

    /**
     * Common timers setup.
     */
    private HashMap<TimerType, ListenableScheduledFuture<?>> timers = new HashMap<>(6);
    protected final Map<String, SxpPeerGroupBuilder> peerGroupMap = new HashMap<>();
    protected final Map<String, SxpDomain> sxpDomains = new HashMap<>();

    /**
     * Default constructor that creates and start SxpNode using provided values
     *
     * @param nodeId ID of newly created Node
     * @param node   Node setup data
     * @param worker Thread workers which will be executing task inside SxpNode
     */
    protected SxpNode(NodeId nodeId, SxpNodeIdentity node, ThreadsWorker worker) {
        this.nodeBuilder = new SxpNodeIdentityBuilder(Preconditions.checkNotNull(node));
        this.nodeId = Preconditions.checkNotNull(nodeId);
        this.worker = Preconditions.checkNotNull(worker);
        this.svcBindingDispatcher = new BindingDispatcher(this);
        if (Objects.nonNull(node.getMessageBuffering()) && Objects.nonNull(node.getMessageBuffering().getInBuffer())
                && Objects.nonNull(node.getMessageBuffering().getOutBuffer())) {
            this.svcBindingHandler = new BindingHandler(this, this.svcBindingDispatcher,
                    node.getMessageBuffering().getInBuffer());
            setMessagePartitionSize(node.getMessageBuffering().getOutBuffer());
        } else {
            this.svcBindingHandler = new BindingHandler(this, this.svcBindingDispatcher);
        }
    }

    /**
     * @return SxpNodeIdentity containing configuration of current Node
     */
    protected SxpNodeIdentity getNodeIdentity() {
        return nodeBuilder.build();
    }

    /**
     * @param security Sets Security used for peers
     */
    protected void setSecurity(
            org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.sxp.node.fields.Security security) {
        nodeBuilder.setSecurity(Preconditions.checkNotNull(security));
    }

    /**
     * @param connection Adds connection and update MD5 keys if needed
     */
    protected SxpConnection addConnection(SxpConnection connection) {
        synchronized (peerGroupMap) {
            for (SxpPeerGroup peerGroup : getPeerGroup(connection)) {
                for (SxpFilter filter : peerGroup.getSxpFilter()) {
                    connection.putFilter(SxpBindingFilter.generateFilter(filter, peerGroup.getName()));
                }
            }
        }
        synchronized (sxpDomains) {
            if (!sxpDomains.containsKey(connection.getDomainName())) {
                LOG.warn("{} Domain {} does not exist", this, connection.getDomainName());
                throw new DomainNotFoundException(getName(), "Domain " + connection.getDomainName() + " not found");
            }
            sxpDomains.get(connection.getDomainName()).putConnection(connection);
        }
        updateMD5keys(connection);
        return connection;
    }

    /**
     * Checks if there is another PeerGroup with at least one same peer and Filter with same type
     *
     * @param group SxpPeerGroup to be checked on overlap
     * @return SxpPeerGroup that overlaps the specified one or null if there is no overlap
     */
    private SxpPeerGroup checkPeerGroupOverlap(SxpPeerGroupBuilder group) {
        Set<SxpPeer> peerSet1 = new HashSet<>();
        if (group.getSxpPeers() != null && group.getSxpPeers().getSxpPeer() != null) {
            peerSet1.addAll(group.getSxpPeers().getSxpPeer());
        }
        for (SxpPeerGroup peerGroup : getPeerGroups()) {
            Set<SxpPeer> peerSet2 = new HashSet<>();
            if (peerGroup.getSxpPeers() != null && peerGroup.getSxpPeers().getSxpPeer() != null) {
                peerSet2.addAll(peerGroup.getSxpPeers().getSxpPeer());
            }
            if (peerGroup.getName().equals(group.getName()) || (!peerSet1.isEmpty() && !peerSet2.isEmpty()
                    && Sets.intersection(peerSet1, peerSet2).isEmpty())) {
                continue;
            }
            if (group.getSxpFilter() != null) {
                for (SxpFilter filter1 : group.getSxpFilter()) {
                    for (SxpFilter filter2 : peerGroup.getSxpFilter()) {
                        if (SxpBindingFilter.checkInCompatibility(filter1, filter2)) {
                            return peerGroup;
                        }
                    }
                }
            }
        }
        return null;
    }

    /**
     * Gets all SxpConnections in node contained in SxpPeerGroup,
     * or All connection on node if none peers are specified in SxpPeerGroup
     *
     * @param peerGroup PeerGroup containing peers
     * @return List of Connections
     */
    private List<SxpConnection> getConnections(SxpPeerGroupBuilder peerGroup) {
        if (peerGroup.getSxpPeers() == null || peerGroup.getSxpPeers().getSxpPeer() == null
                || peerGroup.getSxpPeers().getSxpPeer().isEmpty()) {
            return getAllConnections();
        }
        List<SxpConnection> sxpConnections = new ArrayList<>();
        for (SxpPeer sxpPeer : peerGroup.getSxpPeers().getSxpPeer()) {
            SxpConnection connection = getByAddress(
                    new InetSocketAddress(Search.getAddress(sxpPeer.getPeerAddress()),
                            sxpPeer.getPeerPort() == null ? 64999 : sxpPeer.getPeerPort().getValue()));
            if (connection != null) {
                sxpConnections.add(connection);
            }
        }
        return sxpConnections;
    }

    /**
     * Gets SxpPeerGroups in which specified connection is
     *
     * @param connection SxpConnections to look for in PeerGroups
     * @return List of PeerGroups that contain SxpConnection
     */
    private List<SxpPeerGroup> getPeerGroup(SxpConnection connection) {
        List<SxpPeerGroup> sxpPeerGroups = new ArrayList<>();
        synchronized (peerGroupMap) {
            for (SxpPeerGroupBuilder peerGroup : peerGroupMap.values()) {
                if (peerGroup.getSxpPeers().getSxpPeer() == null
                        || peerGroup.getSxpPeers().getSxpPeer().isEmpty()) {
                    sxpPeerGroups.add(peerGroup.build());
                    continue;
                }
                for (SxpPeer peer : peerGroup.getSxpPeers().getSxpPeer()) {
                    InetAddress address = InetAddresses.forString(Search.getAddress(peer.getPeerAddress()));
                    if (address.equals(connection.getDestination().getAddress())) {
                        sxpPeerGroups.add(peerGroup.build());
                    }
                }
            }
        }
        return sxpPeerGroups;
    }

    /**
     * Adds new PeerGroup to Node and add filters to connections if any
     *
     * @param peerGroup PeerGroup to be added
     */
    public boolean addPeerGroup(SxpPeerGroup peerGroup) {
        synchronized (peerGroupMap) {
            if (peerGroup == null || peerGroup.getName() == null || peerGroup.getSxpPeers() == null) {
                LOG.warn("{} Cannot add PeerGroup {} due to missing fields", this, peerGroup);
                return false;
            }
            SxpPeerGroupBuilder groupBuilder = new SxpPeerGroupBuilder(peerGroup);
            if (checkPeerGroupOverlap(groupBuilder) != null) {
                throw new IllegalArgumentException("PeerGroup filter overlaps with filter on other PeerGroup");
            }
            if (groupBuilder.getSxpFilter() == null) {
                groupBuilder.setSxpFilter(new ArrayList<>());
            } else {
                Set<SxpBindingFilter<?, ? extends SxpFilterFields>> filters = new HashSet<>();
                for (SxpFilter filter : peerGroup.getSxpFilter()) {
                    filters.add(SxpBindingFilter.generateFilter(filter, peerGroup.getName()));
                }
                if (filters.size() != peerGroup.getSxpFilter().size()) {
                    LOG.warn("{} Cannot add PeerGroup {} due to multiple declarations of filter with same type",
                            this, peerGroup);
                    return false;
                }
                List<SxpConnection> connections = getConnections(groupBuilder);
                for (SxpConnection connection : connections) {
                    filters.forEach(connection::putFilter);
                }
            }
            peerGroupMap.put(peerGroup.getName(), groupBuilder);
            return true;
        }
    }

    /**
     * @param peerGroupName Name of PeerGroup
     * @return PeerGroup with specified name on this node
     */
    public SxpPeerGroup getPeerGroup(String peerGroupName) {
        synchronized (peerGroupMap) {
            return peerGroupMap.get(peerGroupName) != null ? peerGroupMap.get(peerGroupName).build() : null;
        }
    }

    /**
     * Removes PeerGroup from Node and removes filters from connections if any
     *
     * @param peerGroupName Name of PeerGroup that will be removed
     * @return Removed PeerGroup
     */
    public SxpPeerGroup removePeerGroup(String peerGroupName) {
        synchronized (peerGroupMap) {
            SxpPeerGroupBuilder peerGroup = peerGroupMap.remove(peerGroupName);
            if (peerGroup == null) {
                return null;
            }
            List<SxpConnection> connections = getConnections(peerGroup);
            for (SxpConnection connection : connections) {
                for (SxpFilter filter : peerGroup.getSxpFilter()) {
                    connection.removeFilter(filter.getFilterType(), filter.getFilterSpecific());
                }
            }
            return peerGroup.build();
        }
    }

    /**
     * @return All PeerGroups on this node
     */
    public Collection<SxpPeerGroup> getPeerGroups() {
        synchronized (peerGroupMap) {
            return Collections2.transform(peerGroupMap.values(), new Function<SxpPeerGroupBuilder, SxpPeerGroup>() {

                @Nullable
                @Override
                public SxpPeerGroup apply(SxpPeerGroupBuilder input) {
                    return input != null ? input.build() : null;
                }
            });
        }
    }

    /**
     * Adds new Filter to specified PeerGroup
     *
     * @param peerGroupName Name of PeerGroup where SxpFilter will be added
     * @param sxpFilter     SxpFilter that will be used
     */
    public boolean addFilterToPeerGroup(String peerGroupName, SxpFilter sxpFilter) {
        synchronized (peerGroupMap) {
            SxpPeerGroupBuilder peerGroup = peerGroupMap.get(peerGroupName);
            if (peerGroup == null || sxpFilter == null) {
                LOG.warn("{} Cannot add Filter | Due to null parameters", this);
                return false;
            }
            SxpBindingFilter<?, ? extends SxpFilterFields> bindingFilter = SxpBindingFilter
                    .generateFilter(sxpFilter, peerGroupName);
            List<SxpFilter> sxpFilters = peerGroup.getSxpFilter();
            for (SxpFilter filter : sxpFilters) {
                if (SxpBindingFilter.checkInCompatibility(filter, sxpFilter)) {
                    LOG.warn("{} Filter of type {} already defined", this, sxpFilter.getFilterType());
                    return filter.equals(sxpFilter);
                }
            }
            sxpFilters.add(sxpFilter);
            if (checkPeerGroupOverlap(peerGroup) != null) {
                sxpFilters.remove(sxpFilter);
                LOG.warn("{} Filter cannot be added due to overlap of Peer Groups", this, sxpFilter);
                return false;
            }
            List<SxpConnection> connections = getConnections(peerGroup);
            for (SxpConnection connection : connections) {
                connection.putFilter(bindingFilter);
            }
            return true;
        }
    }

    /**
     * Update Filter in PeerGroup of specific type
     *
     * @param peerGroupName Name of PeerGroup that contains filter
     * @param newFilter     SxpFilter with new values that will be used
     * @return Old SxpFilter
     */
    public SxpFilter updateFilterInPeerGroup(String peerGroupName, SxpFilter newFilter) {
        synchronized (peerGroupMap) {
            SxpPeerGroupBuilder peerGroup = peerGroupMap.get(peerGroupName);
            if (peerGroup == null || newFilter == null) {
                LOG.warn("{} Cannot update Filter | Due to null parameters", this);
                return null;
            }
            List<SxpFilter> sxpFilters = peerGroup.getSxpFilter();
            SxpFilter oldFilter = null;
            for (SxpFilter filter : sxpFilters) {
                if (SxpBindingFilter.checkInCompatibility(filter, newFilter)
                        && filter.getFilterEntries().getClass().equals(filter.getFilterEntries().getClass())) {
                    oldFilter = filter;
                    break;
                }
            }
            if (oldFilter == null) {
                LOG.warn("{} Cannot update Filter | No previous filter with type {} found", this,
                        newFilter.getFilterType());
                return null;
            }
            sxpFilters.remove(oldFilter);
            sxpFilters.add(newFilter);
            SxpBindingFilter bindingFilter = SxpBindingFilter.generateFilter(newFilter, peerGroupName);
            List<SxpConnection> connections = getConnections(peerGroup);
            for (SxpConnection connection : connections) {
                connection.putFilter(bindingFilter);
            }
            return oldFilter;
        }
    }

    /**
     * Removes last added Filter from specified PeerGroup
     *
     * @param peerGroupName Name of PeerGroup that contains filter
     * @param filterType    Type of Filter that will be removed
     * @return If any filters were removed
     */
    public boolean removeFilterFromPeerGroup(String peerGroupName, FilterType filterType, FilterSpecific specific) {
        synchronized (peerGroupMap) {
            SxpPeerGroupBuilder peerGroup = peerGroupMap.get(peerGroupName);
            if (peerGroup == null || filterType == null) {
                return false;
            }
            List<SxpConnection> connections = getConnections(peerGroup);
            return peerGroup.getSxpFilter().removeIf(f -> {
                boolean remove = specific == null ? f.getFilterType().equals(filterType)
                        : f.getFilterType().equals(filterType) && specific.equals(f.getFilterSpecific());
                if (remove) {
                    connections.forEach(c -> c.removeFilter(filterType, f.getFilterSpecific()));
                }
                return remove;
            });
        }
    }

    /**
     * Adds Domain into SxpNode if there is no other domain with omitting name
     *
     * @param domain SXpDomain initializer
     * @return If Domain was added
     */
    public boolean addDomain(
            org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.network.topology.topology.node.sxp.domains.SxpDomain domain) {
        Preconditions.checkNotNull(domain);
        Preconditions.checkNotNull(domain.getDomainName());
        synchronized (sxpDomains) {
            if (!sxpDomains.containsKey(domain.getDomainName()))
                sxpDomains.put(domain.getDomainName(), SxpDomain.createInstance(this, domain));
            else
                return false;
        }
        return true;
    }

    /**
     * Remove Domain from SxpNode if there is not domain with specified name thrown IllegalArgumentException
     *
     * @param domainName Nme assigned to domain that will be removed
     * @return SxpDomain that was removed
     */
    public SxpDomain removeDomain(String domainName) {
        synchronized (sxpDomains) {
            if (sxpDomains.containsKey(Preconditions.checkNotNull(domainName))
                    && sxpDomains.get(domainName).getConnections().isEmpty()) {
                sxpDomains.get(domainName).close();
                return sxpDomains.remove(domainName);
            }
            throw new IllegalStateException("Domain " + domainName + "cannot be removed.");
        }
    }

    /**
     * @param name Domain name to look for
     * @return SxpDomain or null if not found
     */
    public SxpDomain getDomain(String name) {
        synchronized (sxpDomains) {
            return sxpDomains.get(name);
        }
    }

    /**
     * @return Collections of All Domains in current Node
     */
    public Collection<SxpDomain> getDomains() {
        synchronized (sxpDomains) {
            return Collections.unmodifiableCollection(sxpDomains.values());
        }
    }

    /**
     * @param domain Name of Domain where filter will be added
     * @param filter Filter that will be added to domain
     * @return If filter was added to domain
     */
    public boolean addFilterToDomain(String domain, DomainFilter filter) {
        synchronized (sxpDomains) {
            return Preconditions.checkNotNull(getDomain(Preconditions.checkNotNull(domain)))
                    .addFilter(SxpBindingFilter.generateFilter(Preconditions.checkNotNull(filter), domain));
        }
    }

    /**
     * @param domain     Name of Domain containing Filter
     * @param specific   Filter type specification
     * @param filterName Name of filter
     * @return removed Filter or null if nothing was removed
     */
    public SxpBindingFilter removeFilterFromDomain(String domain, FilterSpecific specific, String filterName) {
        synchronized (sxpDomains) {
            SxpDomain sxpDomain = getDomain(Preconditions.checkNotNull(domain));
            return sxpDomain == null ? null
                    : sxpDomain.removeFilter(Preconditions.checkNotNull(specific),
                            Preconditions.checkNotNull(filterName));
        }
    }

    /**
     * Updated old Filter with new values
     *
     * @param domain    Domain where filter will be updated
     * @param newFilter Filter that will be used from now on
     * @return If filter was updated
     */
    public boolean updateDomainFilter(String domain, DomainFilter newFilter) {
        synchronized (sxpDomains) {
            SxpDomain sxpDomain = getDomain(Preconditions.checkNotNull(domain));
            return sxpDomain != null && sxpDomain
                    .updateFilter(SxpBindingFilter.generateFilter(Preconditions.checkNotNull(newFilter), domain));
        }
    }

    /**
     * Adds and afterward start new Connection
     *
     * @param connection Connection to be added
     * @throws IllegalArgumentException If Connection exist in Node
     */
    @Deprecated
    public void addConnection(Connection connection) {
        addConnection(connection, DEFAULT_DOMAIN);
    }

    /**
     * Adds and afterward start new Connection
     *
     * @param connection Connection to be added
     * @param domain     Domain where Connection will be added
     *                   if domain does no exist connection wont be added
     * @throws IllegalArgumentException If Connection exist in Node
     */
    public SxpConnection addConnection(Connection connection, String domain) {
        return addConnection(SxpConnection.create(this, Preconditions.checkNotNull(connection),
                Preconditions.checkNotNull(domain)));
    }

    /**
     * Adds and afterwards starts new Connections
     *
     * @param connections Connections to be added
     */
    @Deprecated
    public void addConnections(Connections connections) {
        addConnections(connections, DEFAULT_DOMAIN);
    }

    /**
     * Adds and afterwards starts new Connections
     *
     * @param connections Connections to be added
     * @param domain      Domain where Connections will be added
     *                    if domain does no exist connection wont be added
     */
    public void addConnections(Connections connections, String domain) {
        if (connections == null || domain == null || connections.getConnection() == null
                || connections.getConnection().isEmpty()) {
            return;
        }
        connections.getConnection().forEach(c -> addConnection(c, domain));
    }

    /**
     * @param predicate Predicate used for selection of connections
     * @return List of Connections matching specified criteria
     */
    private List<SxpConnection> filterConnections(Predicate<SxpConnection> predicate) {
        List<SxpConnection> connections = new ArrayList<>();
        synchronized (sxpDomains) {
            sxpDomains.values()
                    .forEach(d -> connections.addAll(Collections2.filter(d.getConnections(), predicate)));
        }
        return Collections.unmodifiableList(connections);
    }

    /**
     * @param predicate Predicate used for selection of connections
     * @param domain    Domain where to look for Connections
     * @return List of Connections matching specified criteria
     */
    private List<SxpConnection> filterConnections(Predicate<SxpConnection> predicate, String domain) {
        if (domain == null)
            return filterConnections(predicate);
        List<SxpConnection> connections = new ArrayList<>();
        synchronized (sxpDomains) {
            if (sxpDomains.containsKey(Preconditions.checkNotNull(domain)))
                connections.addAll(Collections2.filter(sxpDomains.get(domain).getConnections(), predicate));
        }
        return Collections.unmodifiableList(connections);
    }

    /**
     * @return All SxpConnections on Node
     */
    public List<SxpConnection> getAllConnections() {
        List<SxpConnection> connections = new ArrayList<>();
        synchronized (sxpDomains) {
            sxpDomains.values().forEach(d -> connections.addAll(d.getConnections()));
        }
        return Collections.unmodifiableList(connections);
    }

    /**
     * @param domain Domain containing Connections
     * @return All SxpConnections on Node
     */
    public List<SxpConnection> getAllConnections(String domain) {
        List<SxpConnection> connections = new ArrayList<>();
        synchronized (sxpDomains) {
            if (sxpDomains.containsKey(Preconditions.checkNotNull(domain)))
                connections.addAll(sxpDomains.get(domain).getConnections());
        }
        return Collections.unmodifiableList(connections);
    }

    /**
     * @return Ip used by SxpNode
     */
    public InetAddress getSourceIp() {
        return sourceIp;
    }

    /**
     * @return Gets all SxpConnections with state set to DeleteHoldDown
     */
    public List<SxpConnection> getAllDeleteHoldDownConnections() {
        return getAllDeleteHoldDownConnections(null);
    }

    /**
     * @param domain Domain containing connection,
     *               if null gets from all domains
     * @return Gets all SxpConnections with state set to DeleteHoldDown
     */
    public List<SxpConnection> getAllDeleteHoldDownConnections(String domain) {
        return filterConnections(SxpConnection::isStateDeleteHoldDown, domain);
    }

    /**
     * @return Gets all SxpConnections with state set to Off
     */
    public List<SxpConnection> getAllOffConnections() {
        return getAllOffConnections(null);
    }

    /**
     * @param domain Domain containing connection,
     *               if null gets from all domains
     * @return Gets all SxpConnections with state set to Off
     */
    public List<SxpConnection> getAllOffConnections(String domain) {
        return filterConnections(SxpConnection::isStateOff, domain);
    }

    /**
     * @return Gets all SxpConnections with state set to On
     */
    public List<SxpConnection> getAllOnConnections() {
        return getAllOnConnections(null);
    }

    /**
     * @param domain Domain containing connection,
     *               if null gets from all domains
     * @return Gets all SxpConnections with state set to On
     */
    public List<SxpConnection> getAllOnConnections(String domain) {
        return filterConnections(SxpConnection::isStateOn, domain);
    }

    /**
     * @return Gets all SxpConnections with state set to On and mode Listener or Both
     */
    @Deprecated
    public List<SxpConnection> getAllOnListenerConnections() {
        return getAllOnListenerConnections(null);
    }

    /**
     * @param domain Domain containing connection,
     *               if null gets from all domains
     * @return Gets all SxpConnections with state set to On and mode Listener or Both
     */
    public List<SxpConnection> getAllOnListenerConnections(String domain) {
        return filterConnections(
                connection -> connection.isStateOn(SxpConnection.ChannelHandlerContextType.ListenerContext)
                        && (connection.getMode().equals(ConnectionMode.Listener) || connection.isModeBoth()),
                domain);
    }

    /**
     * @return Gets all SxpConnections with state set to On and mode Speaker or Both
     */
    @Deprecated
    public List<SxpConnection> getAllOnSpeakerConnections() {
        return getAllOnSpeakerConnections(null);
    }

    /**
     * @param domain Domain containing connection,
     *               if null gets from all domains
     * @return Gets all SxpConnections with state set to On and mode Speaker or Both
     */
    public List<SxpConnection> getAllOnSpeakerConnections(String domain) {
        return filterConnections(
                connection -> connection.isStateOn(SxpConnection.ChannelHandlerContextType.SpeakerContext)
                        && (connection.getMode().equals(ConnectionMode.Speaker) || connection.isModeBoth()),
                domain);
    }

    /**
     * @return Gets MasterDatabase that is used in Node
     */
    @Deprecated
    public MasterDatabaseInf getBindingMasterDatabase() {
        return getBindingMasterDatabase(DEFAULT_DOMAIN);
    }

    /**
     * @return Gets SxpDatabase that is used in Node
     */
    @Deprecated
    public SxpDatabaseInf getBindingSxpDatabase() {
        return getBindingSxpDatabase(DEFAULT_DOMAIN);
    }

    /**
     * @param domainName Domain containing MasterDatabase
     * @return Gets MasterDatabase that is used in Node
     * @throws DomainNotFoundException if Domain does not exist
     */
    public MasterDatabaseInf getBindingMasterDatabase(String domainName) {
        synchronized (sxpDomains) {
            if (sxpDomains.containsKey(Preconditions.checkNotNull(domainName)))
                return sxpDomains.get(domainName).getMasterDatabase();
            throw new DomainNotFoundException(getName(), "Domain " + domainName + " not found");
        }
    }

    /**
     * @param domainName Domain containing SxpDatabase
     * @return Gets SxpDatabase that is used in Node
     * @throws DomainNotFoundException if Domain does not exist
     */
    public SxpDatabaseInf getBindingSxpDatabase(String domainName) {
        synchronized (sxpDomains) {
            if (sxpDomains.containsKey(Preconditions.checkNotNull(domainName)))
                return sxpDomains.get(domainName).getSxpDatabase();
            throw new DomainNotFoundException(getName(), "Domain " + domainName + " not found");
        }
    }

    /**
     * Gets SxpConnection by its address
     *
     * @param inetSocketAddress InetSocketAddress that is used by SxpConnection
     * @return SxpConnection or null if Node doesn't contains specified address
     * @throws IllegalStateException If found more than 1 SxpConnection
     */
    public SxpConnection getByAddress(final InetSocketAddress inetSocketAddress) {
        Preconditions.checkNotNull(inetSocketAddress);
        List<SxpConnection> sxpConnections = new ArrayList<>();
        synchronized (sxpDomains) {
            sxpDomains.values().forEach(d -> {
                if (d.hasConnection(inetSocketAddress))
                    sxpConnections.add(d.getConnection(inetSocketAddress));
            });
        }
        if (sxpConnections.isEmpty()) {
            return null;
        } else if (sxpConnections.size() == 1) {
            return sxpConnections.get(0);
        }
        throw new IllegalStateException("Found multiple Connections on specified address");
    }

    /**
     * Gets SxpConnection by its port
     *
     * @param port Port that is used by SxpConnection
     * @return SxpConnection or null if Node doesn't contains address with specified port
     * @throws IllegalStateException If found more than 1 SxpConnection
     */
    @Deprecated
    public SxpConnection getByPort(final int port) {
        List<SxpConnection> sxpConnections = filterConnections(new Predicate<SxpConnection>() {

            @Override
            public boolean apply(SxpConnection connection) {
                return port == connection.getDestination().getPort();
            }
        });
        if (sxpConnections.isEmpty()) {
            return null;
        } else if (sxpConnections.size() == 1) {
            return sxpConnections.get(0);
        }
        throw new IllegalStateException("Found multiple Connections on specified address");
    }

    /**
     * Gets SxpConnection by its address
     *
     * @param socketAddress SocketAddress that is used by SxpConnection
     * @return SxpConnection if exists
     */
    public SxpConnection getConnection(SocketAddress socketAddress) {
        if (!(socketAddress instanceof InetSocketAddress)) {
            return null;
        }
        return getByAddress((InetSocketAddress) socketAddress);
    }

    /**
     * @return Gets Bindings expansion quantity or zero if disabled
     */
    public int getExpansionQuantity() {
        return getNodeIdentity().getMappingExpanded() != null ? getNodeIdentity().getMappingExpanded() : 0;
    }

    /**
     * @return Gets HoldTime value or zero if disabled
     */
    public int getHoldTime() {
        if (getNodeIdentity().getTimers() == null || getNodeIdentity().getTimers() == null
                || getNodeIdentity().getTimers().getHoldTime() == null) {
            return 0;
        }
        return getNodeIdentity().getTimers().getHoldTime();
    }

    /**
     * @return Gets HoldTimeMax value or zero if disabled
     */
    public int getHoldTimeMax() {
        if (getNodeIdentity().getTimers() == null || getNodeIdentity().getTimers() == null
                || getNodeIdentity().getTimers().getHoldTimeMax() == null) {
            return 0;
        }
        return getNodeIdentity().getTimers().getHoldTimeMax();
    }

    /**
     * @return Gets HoldTimeMin value or zero if disabled
     */
    public int getHoldTimeMin() {
        if (getNodeIdentity().getTimers() == null || getNodeIdentity().getTimers() == null
                || getNodeIdentity().getTimers().getHoldTimeMin() == null) {
            return 0;
        }
        return getNodeIdentity().getTimers().getHoldTimeMin();
    }

    /**
     * @return Gets HoldTimeMinAcceptable value or zero if disabled
     */
    public int getHoldTimeMinAcceptable() {
        if (getNodeIdentity().getTimers() == null || getNodeIdentity().getTimers() == null
                || getNodeIdentity().getTimers().getHoldTimeMinAcceptable() == null) {
            return 0;
        }
        return getNodeIdentity().getTimers().getHoldTimeMinAcceptable();
    }

    /**
     * @return Gets KeepAlive value or zero if disabled
     */
    public int getKeepAliveTime() {
        if (getNodeIdentity().getTimers() == null || getNodeIdentity().getTimers() == null
                || getNodeIdentity().getTimers().getKeepAliveTime() == null) {
            return 0;
        }
        return getNodeIdentity().getTimers().getKeepAliveTime();
    }

    /**
     * @return Gets Name of Node
     */
    public String getName() {
        return getNodeIdentity().getName() == null || getNodeIdentity().getName().isEmpty()
                ? NodeIdConv.toString(nodeId)
                : getNodeIdentity().getName();
    }

    /**
     * @return Gets NodeId
     */
    public NodeId getNodeId() {
        return nodeId;
    }

    /**
     * @return Gets Password used to connect to peers or null if disabled
     */
    public String getPassword() {
        return getNodeIdentity().getSecurity() != null ? getNodeIdentity().getSecurity().getPassword() : null;
    }

    /**
     * @return Gets RetryOpen value or zero if disabled
     */
    public int getRetryOpenTime() {
        if (getNodeIdentity().getTimers() == null || getNodeIdentity().getTimers().getRetryOpenTime() == null) {
            return 0;
        }
        return getNodeIdentity().getTimers().getRetryOpenTime();
    }

    /**
     * @return Gets Node server port or -1 if dissabled
     */
    public int getServerPort() {
        if (getNodeIdentity().getTcpPort() == null || getNodeIdentity().getTcpPort().getValue() == null) {
            return -1;
        }
        return getNodeIdentity().getTcpPort().getValue();
    }

    /**
     * Gets SxpNode specific Timer
     *
     * @param timerType Type of Timer
     * @return TimerType or null if not present
     */
    public ListenableScheduledFuture<?> getTimer(TimerType timerType) {
        return timers.get(timerType);
    }

    /**
     * @return Gets Version of of Node
     */
    public Version getVersion() {
        return getNodeIdentity().getVersion() != null ? getNodeIdentity().getVersion() : Version.Version4;
    }

    /**
     * @return If Node is enabled
     */
    public boolean isEnabled() {
        return serverChannel != null && serverChannel.isActive();
    }

    /**
     * Start all Connections that are in state Off
     */
    public void openConnections() {
        // Server not created yet.
        if (serverChannel == null) {
            return;
        }

        final SxpNode node = this;

        final int connectionsAllSize = getAllConnections().size();
        final int connectionsOnSize = getAllOnConnections().size();
        final List<SxpConnection> connections = filterConnections(
                c -> c.isStateOff() || c.isStateDeleteHoldDown() || c.isStatePendingOn());

        worker.executeTask(() -> {
            LOG.info(node + " Open connections [X/O/All=\"" + connections.size() + "/" + connectionsOnSize + "/"
                    + connectionsAllSize + "\"]");
            connections.forEach(this::openConnection);
        }, ThreadsWorker.WorkerType.DEFAULT);
    }

    /**
     * Connect specified connection to remote peer
     *
     * @param connection Connection containing necessary information for connecting to peer
     */
    public void openConnection(final SxpConnection connection) {
        if (!Preconditions.checkNotNull(connection).isStateOn() && isEnabled()) {
            if (!connection.isModeBoth()) {
                connection.closeChannelHandlerContextComplements(null);
            }
            if (!connection.isModeBoth() || !connection
                    .hasChannelHandlerContext(SxpConnection.ChannelHandlerContextType.ListenerContext)) {
                ConnectFacade.createClient(this, connection, handlerFactoryClient);
            }
        }
    }

    /**
     * @return BindingDispatcher routine used by current Node
     */
    public BindingDispatcher getSvcBindingDispatcher() {
        return svcBindingDispatcher;
    }

    /**
     * @return BindingHandler routine used by current Node
     */
    public BindingHandler getSvcBindingHandler() {
        return svcBindingHandler;
    }

    /**
     * Adds Bindings to database as Local bindings
     *
     * @param bindings MasterDatabase containing bindings that will be added
     */
    @Deprecated
    public List<MasterDatabaseBinding> putLocalBindingsMasterDatabase(List<MasterDatabaseBinding> bindings) {
        return putLocalBindingsMasterDatabase(bindings, DEFAULT_DOMAIN);
    }

    /**
     * Removes Local Bindings from database
     *
     * @param bindings MasterDatabase containing bindings that will be removed
     */
    @Deprecated
    public List<MasterDatabaseBinding> removeLocalBindingsMasterDatabase(List<MasterDatabaseBinding> bindings) {
        return removeLocalBindingsMasterDatabase(bindings, DEFAULT_DOMAIN);
    }

    /**
     * Adds Bindings to database as Local bindings
     *
     * @param bindings   MasterDatabase containing bindings that will be added
     * @param domainName Domain where bindings wil be added
     * @throws DomainNotFoundException if Domain does not exist
     */
    public List<MasterDatabaseBinding> putLocalBindingsMasterDatabase(List<MasterDatabaseBinding> bindings,
            String domainName) throws DomainNotFoundException {
        final SxpDomain sxpDomain = getDomain(domainName);
        if (sxpDomain == null)
            throw new DomainNotFoundException(getName(), "Domain " + domainName + " not found");
        List<MasterDatabaseBinding> addedBindings;
        synchronized (sxpDomain) {
            addedBindings = sxpDomain.getMasterDatabase().addLocalBindings(bindings);
            svcBindingDispatcher.propagateUpdate(null, addedBindings, getAllOnSpeakerConnections(domainName));
            sxpDomain.pushToSharedMasterDatabases(Collections.emptyList(), bindings);
        }
        return addedBindings;
    }

    /**
     * Removes Local Bindings from database
     *
     * @param bindings   MasterDatabase bindings that will be removed
     * @param domainName Domain from which bindings will be removed
     * @throws DomainNotFoundException if Domain does not exist
     */
    public List<MasterDatabaseBinding> removeLocalBindingsMasterDatabase(List<MasterDatabaseBinding> bindings,
            String domainName) throws DomainNotFoundException {
        final SxpDomain sxpDomain = getDomain(domainName);
        if (sxpDomain == null)
            throw new DomainNotFoundException(getName(), "Domain " + domainName + " not found");
        Map<NodeId, SxpBindingFilter> filterMap = SxpDatabase.getInboundFilters(this, domainName);
        List<MasterDatabaseBinding> deletedBindings;
        synchronized (sxpDomain) {
            deletedBindings = sxpDomain.getMasterDatabase().deleteBindingsLocal(bindings);
            svcBindingDispatcher.propagateUpdate(deletedBindings,
                    sxpDomain.getMasterDatabase()
                            .addBindings(SxpDatabase.getReplaceForBindings(deletedBindings,
                                    sxpDomain.getSxpDatabase(), filterMap)),
                    getAllOnSpeakerConnections(domainName));
            sxpDomain.pushToSharedMasterDatabases(bindings, Collections.emptyList());
        }
        return deletedBindings;
    }

    /**
     * Remove and afterwards shutdown connection
     *
     * @param destination InetSocketAddress that is used by SxpConnection
     * @return Removed SxpConnection
     */
    public SxpConnection removeConnection(InetSocketAddress destination) {
        SxpConnection connection = null;
        synchronized (sxpDomains) {
            for (SxpDomain domain : sxpDomains.values()) {
                if (domain.hasConnection(destination)) {
                    connection = domain.removeConnection(destination);
                    break;
                }
            }
        }
        if (connection != null) {
            connection.shutdown();
            updateMD5keys(connection);
        }
        return connection;
    }

    /**
     * Sets Security password used to connect
     *
     * @param security Security to be set
     * @return Newly set Security
     */
    protected org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.sxp.node.fields.Security setPassword(
            org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.sxp.node.fields.Security security) {
        SecurityBuilder securityBuilder = new SecurityBuilder();
        if (security == null || security.getPassword() == null || security.getPassword().isEmpty()) {
            securityBuilder.setPassword("");
            return securityBuilder.build();
        }

        if (getNodeIdentity().getSecurity() != null && getNodeIdentity().getSecurity().getPassword() != null
                && !getNodeIdentity().getSecurity().getPassword().isEmpty()
                && !getNodeIdentity().getSecurity().getPassword().equals(security.getPassword())) {
            shutdownConnections();
        }
        securityBuilder.setPassword(security.getPassword());
        securityBuilder.setMd5Digest(Security.getMD5s(security.getPassword()));
        return securityBuilder.build();
    }

    /**
     * Sets SxpNode specific Timer
     *
     * @param timerType Type of Timer that will be set
     * @param period    Time period to wait till execution in Seconds
     * @return ListenableScheduledFuture callback
     * @throws UnknownTimerTypeException If current TimerType isn't supported
     */
    public ListenableScheduledFuture<?> setTimer(TimerType timerType, int period) throws UnknownTimerTypeException {
        synchronized (timers) {
            SxpTimerTask timer;
            switch (timerType) {
            case RetryOpenTimer:
                timer = new RetryOpenTimerTask(this, period);
                break;
            default:
                throw new UnknownTimerTypeException(timerType);
            }
            ListenableScheduledFuture<?> timer_ = getTimer(timerType);
            if (period > 0 && (timer_ == null || !timer_.isCancelled())) {
                return this.setTimer(timerType, getWorker().scheduleTask(timer, period, TimeUnit.SECONDS));
            } else {
                return this.setTimer(timerType, null);
            }
        }
    }

    /**
     * Sets SxpNode specific Timer
     *
     * @param timerType Type of Timer that will be set
     * @param timer     Timer logic
     * @return ListenableScheduledFuture callback
     */
    private ListenableScheduledFuture<?> setTimer(TimerType timerType, ListenableScheduledFuture<?> timer) {
        ListenableScheduledFuture<?> t = this.timers.put(timerType, timer);
        if (t != null && !t.isDone()) {
            t.cancel(false);
        }
        return timer;
    }

    /**
     * Set max number of attributes exported in each Update Message.
     *
     * @param size Size which will be used for partitioning
     * @throws IllegalArgumentException If size of partitioning is bellow 2 or above 150
     */
    public void setMessagePartitionSize(int size) throws IllegalArgumentException {
        svcBindingDispatcher.setPartitionSize(size);
    }

    /**
     * Set max number of Add update messages to be merged together.
     *
     * @param size Size which will be used for message joining
     * @throws IllegalArgumentException If size of message merge is bellow 1
     */
    public void setMessageMergeSize(int size) throws IllegalArgumentException {
        svcBindingHandler.setBufferLimit(size);
    }

    /**
     * Gets Execution handler of current Node
     *
     * @return ThreadsWorker reference
     */
    public ThreadsWorker getWorker() {
        return worker;
    }

    /**
     * Blocking wait until previous operation on channel is done
     *
     * @param logMsg Message displayed if Error occurs
     */
    private void channelInitializationWait(String logMsg) {
        try {
            for (int i = 0; serverChannelInit.getAndSet(true) && i < 3; i++) {
                wait(THREAD_DELAY);
            }
        } catch (InterruptedException e) {
            LOG.warn("{} {} ", this, logMsg, e);
        }
    }

    /**
     * Administratively shutdown.
     */
    public synchronized SxpNode shutdown() {
        // Wait until server channel ends its own initialization.
        channelInitializationWait("Error while shut down");
        setTimer(TimerType.RetryOpenTimer, 0);
        shutdownConnections();
        for (ThreadsWorker.WorkerType type : ThreadsWorker.WorkerType.values()) {
            getWorker().cancelTasksInSequence(false, type);
        }
        if (serverChannel != null) {
            ChannelFuture channelFuture = serverChannel.close();
            if (channelFuture != null)
                channelFuture.syncUninterruptibly();
            serverChannel = null;
        }
        LOG.info(this + " Server stopped");
        serverChannelInit.set(false);
        return this;
    }

    /**
     * Shutdown all Connections
     */
    public void shutdownConnections() {
        getDomains().forEach(SxpDomain::close);
    }

    private final AtomicBoolean serverChannelInit = new AtomicBoolean(false);

    /**
     * Start SxpNode
     */
    public synchronized SxpNode start() {
        channelInitializationWait("Error while starting");
        if (isEnabled()) {
            return this;
        }
        this.sourceIp = InetAddresses.forString(Search.getAddress(getNodeIdentity().getSourceIp()));
        final SxpNode node = this;
        try {
            ConnectFacade.createServer(node, handlerFactoryServer).addListener(new ChannelFutureListener() {

                @Override
                public void operationComplete(ChannelFuture channelFuture) throws Exception {
                    if (channelFuture.isSuccess()) {
                        serverChannel = channelFuture.channel();
                        LOG.info(node + " Server created [" + getSourceIp().getHostAddress() + ":" + getServerPort()
                                + "]");
                        node.setTimer(TimerType.RetryOpenTimer, node.getRetryOpenTime());
                    } else {
                        LOG.error(node + " Server [" + node.getSourceIp().getHostAddress() + ":" + getServerPort()
                                + "] Could not be created " + channelFuture.cause());
                    }
                    serverChannelInit.set(false);
                }
            }).syncUninterruptibly();
        } catch (Exception e) {
            LOG.debug("Failed to bind SxpNode {} to ip", this, e);
        }
        return this;
    }

    private final AtomicInteger updateMD5counter = new AtomicInteger();

    /**
     * @param connection Connection containing password for MD5 key update
     */
    private void updateMD5keys(final SxpConnection connection) {
        if (connection.getPassword() != null && !connection.getPassword().trim().isEmpty()) {
            updateMD5keys();
        }
    }

    /**
     * Updates TCP-MD5 keys of SxpNode
     */
    public synchronized void updateMD5keys() {
        if (serverChannel != null && isEnabled() && updateMD5counter.incrementAndGet() == 1) {
            serverChannel.close().addListener(createMD5updateListener(this)).syncUninterruptibly();
        }
    }

    /**
     * @param sxpNode Node where MD5 keys will be updated
     * @return ChannelFutureListener callback
     */
    private ChannelFutureListener createMD5updateListener(final SxpNode sxpNode) {
        channelInitializationWait("Error while Updating MD5");
        if (serverChannel == null) {
            updateMD5counter.set(0);
            return new ChannelPromiseNotifier();
        }
        LOG.info("{} Updating MD5 keys", this);
        return future -> ConnectFacade.createServer(sxpNode, handlerFactoryServer)
                .addListener(new ChannelFutureListener() {

                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        serverChannel = future.channel();
                        serverChannelInit.set(false);
                        if (updateMD5counter.decrementAndGet() > 0) {
                            updateMD5counter.set(1);
                            serverChannel.close().addListener(createMD5updateListener(sxpNode))
                                    .syncUninterruptibly();
                        }
                    }
                });
    }

    @Override
    public String toString() {
        return "["
                + (nodeBuilder.getName() != null && !nodeBuilder.getName().isEmpty() ? nodeBuilder.getName() + ":"
                        : "")
                + NodeIdConv.toString(nodeId) + "]";
    }
}