org.opendaylight.sxp.route.core.RouteReactorZipImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.opendaylight.sxp.route.core.RouteReactorZipImpl.java

Source

/*
 * Copyright (c) 2016 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.route.core;

import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.Objects;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.annotation.Nullable;
import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.opendaylight.sxp.core.threading.ThreadsWorker;
import org.opendaylight.sxp.route.api.RouteReactor;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.cluster.route.rev161212.SxpClusterRoute;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.cluster.route.rev161212.SxpClusterRouteBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Purpose: update route change on system level, expect single thread involvement
 */
public class RouteReactorZipImpl implements RouteReactor {

    private static final Logger LOG = LoggerFactory.getLogger(RouteReactorZipImpl.class);

    private static final ListenableFuture<Void> COMPRESSED_FUTURE_RESULT = Futures.immediateFuture(null);

    private static final SxpClusterRoute WIPE_ROUTING_MARK = new SxpClusterRouteBuilder().build();

    private final ListeningExecutorService servicePool = MoreExecutors
            .listeningDecorator(ThreadsWorker.generateExecutor(1, "route-reactor"));

    private final ArrayBlockingQueue<MutablePair<SxpClusterRoute, SxpClusterRoute>> compressionQueue;
    private final Semaphore queueGuard;

    private final RouteReactor delegate;
    private final Callable<Void> updateRouteTask;

    /**
     * @param delegate service used for delegation
     */
    public RouteReactorZipImpl(RouteReactor delegate) {
        this.delegate = Objects.requireNonNull(delegate);
        compressionQueue = new ArrayBlockingQueue<>(1, true);
        queueGuard = new Semaphore(1, true);
        updateRouteTask = createUpdateRoutingTask();
    }

    @Override
    public ListenableFuture<Void> updateRouting(@Nullable final SxpClusterRoute oldRoute,
            @Nullable final SxpClusterRoute newRoute) {
        // with state compression
        ListenableFuture<Void> futureResult;
        try {
            queueGuard.acquire();

            if (compressionQueue.isEmpty()) {
                compressionQueue.add(new MutablePair<>(oldRoute, newRoute));
                // schedule task
                futureResult = servicePool.submit(updateRouteTask);
            } else {
                // compress, expect that task is already scheduled
                compressionQueue.peek().setRight(newRoute);
                futureResult = COMPRESSED_FUTURE_RESULT;
                LOG.trace("route update request got state compressed - firing immediate future");
            }

            queueGuard.release();
        } catch (Exception e) {
            LOG.warn("failed to get lock upon compression queue: {}", e.getMessage());
            futureResult = Futures.immediateFailedFuture(e);
        }
        return futureResult;
    }

    /**
     * @return callback containing logic for mapping configuration into system routing
     */
    private Callable<Void> createUpdateRoutingTask() {
        return () -> {
            try {
                queueGuard.acquire();

                final Pair<SxpClusterRoute, SxpClusterRoute> latestPair = compressionQueue.poll();
                final ListenableFuture<Void> futureResult;
                if (WIPE_ROUTING_MARK == latestPair.getRight()) {
                    futureResult = delegate.wipeRouting();
                } else {
                    futureResult = delegate.updateRouting(latestPair.getLeft(), latestPair.getRight());
                }

                queueGuard.release();

                futureResult.get(60, TimeUnit.SECONDS);
                LOG.debug("Route update was finished");
            } catch (InterruptedException e) {
                LOG.warn("failed to get lock upon compression queue: {}", e.getMessage());
            } catch (ExecutionException | TimeoutException e) {
                LOG.warn("failed to propagate route update: {}", e.getMessage());
            }

            return null;
        };
    }

    @Override
    public ListenableFuture<Void> wipeRouting() {
        ListenableFuture<Void> futureResult;
        try {
            queueGuard.acquire();

            if (compressionQueue.isEmpty()) {
                compressionQueue.add(new MutablePair<>(null, WIPE_ROUTING_MARK));
                // schedule task
                futureResult = servicePool.submit(updateRouteTask);
            } else {
                // compress, expect that task is already scheduled
                compressionQueue.peek().setRight(WIPE_ROUTING_MARK);
                futureResult = COMPRESSED_FUTURE_RESULT;
                LOG.trace("route update request got state compressed - firing immediate future");
            }

            queueGuard.release();
        } catch (Exception e) {
            LOG.warn("failed to get lock upon compression queue: {}", e.getMessage());
            futureResult = Futures.immediateFailedFuture(e);
        }
        return futureResult;
    }
}