org.opendaylight.sxp.core.service.BindingDispatcher.java Source code

Java tutorial

Introduction

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

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ListenableFuture;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;
import org.opendaylight.sxp.core.Configuration;
import org.opendaylight.sxp.core.SxpConnection;
import org.opendaylight.sxp.core.SxpNode;
import org.opendaylight.sxp.core.messaging.MessageFactory;
import org.opendaylight.sxp.core.threading.ThreadsWorker;
import org.opendaylight.sxp.util.ExportKey;
import org.opendaylight.sxp.util.exception.connection.ChannelHandlerContextDiscrepancyException;
import org.opendaylight.sxp.util.exception.connection.ChannelHandlerContextNotFoundException;
import org.opendaylight.sxp.util.exception.message.UpdateMessageCompositionException;
import org.opendaylight.sxp.util.filtering.SxpBindingFilter;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.SxpBindingFields;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.protocol.rev141002.CapabilityType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.opendaylight.sxp.util.inet.Search.expandBindings;

/**
 * SXP Speaker represents the server/provider side of the distributed
 * application, because it provides data to a SXP Listener, which is the client,
 * a service requester.
 */
public final class BindingDispatcher {

    protected static final Logger LOG = LoggerFactory.getLogger(BindingDispatcher.class.getName());

    private final AtomicInteger partitionSize = new AtomicInteger(0);
    private final ThreadsWorker worker;
    private final SxpNode owner;

    /**
     * Default constructor that sets SxpNode
     *
     * @param owner SxpNode to be set
     */
    public BindingDispatcher(SxpNode owner) {
        Preconditions.checkNotNull(owner);
        this.owner = Preconditions.checkNotNull(owner);
        worker = Preconditions.checkNotNull(owner.getWorker());
    }

    /**
     * Set max number of attributes exported in each Update Message.
     *
     * @param partitionSize Size which will be used for partitioning
     * @throws IllegalArgumentException If size of partitioning is bellow 2 or above 150
     */
    public void setPartitionSize(int partitionSize) throws IllegalArgumentException {
        if (partitionSize > 1 && partitionSize < 151) {
            this.partitionSize.set(partitionSize);
        } else {
            throw new IllegalArgumentException(
                    "Partition size must be between 2-150. Current value: " + partitionSize);
        }
    }

    /**
     * @param deleteBindings Bindings that will be deleted
     * @param addBindings    Bindings that will be added
     * @param <T>            Any type extending SxpBindingFields
     * @return BiFunction used for export of bindings to Peers
     */
    private <T extends SxpBindingFields> BiFunction<SxpConnection, SxpBindingFilter, ByteBuf> generatePart(
            List<T> deleteBindings, List<T> addBindings) {
        return (connection, bindingFilter) -> {
            try {
                return connection.getContext().executeUpdateMessageStrategy(connection, deleteBindings, addBindings,
                        bindingFilter);
            } catch (UpdateMessageCompositionException e) {
                LOG.error("{} Error creating update message {} {}", connection, deleteBindings, addBindings, e);
                return PooledByteBufAllocator.DEFAULT.buffer(0);
            }
        };
    }

    /**
     * Gets current partition size.
     *
     * @return value of partition size
     */
    private int getPartitionSize() {
        if (partitionSize.get() == 0) {
            return Math.max(2, Configuration.getConstants().getMessagesExportQuantity());
        }
        return partitionSize.get();
    }

    /**
     * Partition data based on pre configured value, expands bindings for legacy connections
     *
     * @param connection     SxpConnection for which data will be partitioned
     * @param deleteBindings Bindings for delete
     * @param addBindings    Bindings for add
     * @param <T>            Any type extending SxpBindingFields
     * @return List of Functions that will generate Byte representation of data
     */
    <T extends SxpBindingFields> List<BiFunction<SxpConnection, SxpBindingFilter, ByteBuf>> partitionBindings(
            SxpConnection connection, List<T> deleteBindings, List<T> addBindings) {
        List<BiFunction<SxpConnection, SxpBindingFilter, ByteBuf>> partitions = new ArrayList<>();
        List<T> lastPartition = null;
        //Prefix Expansion for legacy versions
        if (connection.isVersion4() && !connection.getCapabilitiesRemote().contains(CapabilityType.SubnetBindings)
                || connection.getVersion().getIntValue() < 3) {
            expandBindings(deleteBindings, owner.getExpansionQuantity());
            expandBindings(addBindings, owner.getExpansionQuantity());
        }
        //Split Delete Bindings
        if (deleteBindings != null && !deleteBindings.isEmpty()) {
            for (List<T> partition : Lists.partition(deleteBindings, getPartitionSize())) {
                partitions.add(generatePart(partition, null));
                lastPartition = partition;
            }
        }
        //Split Add Bindings and rest of Delete Bindings
        if (addBindings != null && !addBindings.isEmpty()) {
            int splitFactor = 0;
            if (lastPartition != null) {
                splitFactor = getPartitionSize() - lastPartition.size();
                partitions.set(partitions.size() - 1, generatePart(lastPartition,
                        splitFactor > addBindings.size() ? addBindings : addBindings.subList(0, splitFactor)));
            }
            for (List<T> partition : Lists.partition(
                    addBindings.subList(splitFactor > addBindings.size() ? 0 : splitFactor, addBindings.size()),
                    getPartitionSize())) {
                partitions.add(generatePart(null, partition));
            }
        }
        return partitions;
    }

    /**
     * Prepares data for propagation to listeners and afterwards
     * send them to peers
     *
     * @param connections SxpConnections on which the export will be performed
     */
    public <T extends SxpBindingFields> void propagateUpdate(List<T> deleteBindings, List<T> addBindings,
            List<SxpConnection> connections) {
        if ((deleteBindings == null || deleteBindings.isEmpty()) && (addBindings == null || addBindings.isEmpty())
                || (connections == null || connections.isEmpty())) {
            return;
        }

        Map<ExportKey, BiFunction<SxpConnection, SxpBindingFilter, ByteBuf>[]> dataPool = new HashMap<>(4);
        Map<ExportKey, ByteBuf[]> messagesPool = new HashMap<>(4);
        Map<ExportKey, AtomicInteger> releaseCounterPool = new HashMap<>(4);

        List<UpdateExportTask> exportTasks = new ArrayList<>();
        for (SxpConnection connection : connections) {
            if (!connection.isStateOn() || !connection.isModeSpeaker()) {
                continue;
            }
            ExportKey key = new ExportKey(connection);
            if (dataPool.get(key) == null) {
                List<BiFunction<SxpConnection, SxpBindingFilter, ByteBuf>> partitions = partitionBindings(
                        connection, deleteBindings, addBindings);
                //Set export data
                dataPool.put(key, partitions.toArray(new BiFunction[partitions.size()]));
                messagesPool.put(key, new ByteBuf[partitions.size()]);
                releaseCounterPool.put(key, new AtomicInteger(0));
            }

            AtomicInteger releaseCounter = releaseCounterPool.get(key);
            releaseCounter.incrementAndGet();
            exportTasks.add(
                    new UpdateExportTask(connection, messagesPool.get(key), dataPool.get(key), releaseCounter));
        }
        exportTasks.forEach(
                e -> worker.executeTaskInSequence(e, ThreadsWorker.WorkerType.OUTBOUND, e.getConnection()));
    }

    /**
     * Add PurgeAll to queue and afterwards sends it
     *
     * @param connection SxpConnection for which PurgeAll will be send
     */
    public static ListenableFuture<Boolean> sendPurgeAllMessage(final SxpConnection connection) {
        return Preconditions.checkNotNull(connection).getOwner().getWorker().executeTaskInSequence(
                () -> sendPurgeAllMessageSync(connection), ThreadsWorker.WorkerType.OUTBOUND, connection);
    }

    /**
     * Add PurgeAll to queue and afterwards sends it
     *
     * @param connection SxpConnection for which PurgeAll will be send
     */
    public static boolean sendPurgeAllMessageSync(final SxpConnection connection) {
        try {
            LOG.info("{} Sending PurgeAll {}", connection, connection.getNodeIdRemote());
            connection.getChannelHandlerContext(SxpConnection.ChannelHandlerContextType.SpeakerContext)
                    .writeAndFlush(MessageFactory.createPurgeAll());
            return true;
        } catch (ChannelHandlerContextNotFoundException | ChannelHandlerContextDiscrepancyException e) {
            LOG.error(connection + " Cannot send PURGE ALL message | {} | ", e.getClass().getSimpleName());
            return false;
        }

    }
}