org.opendaylight.usc.client.netconf.ProtocolSessionPromise.java Source code

Java tutorial

Introduction

Here is the source code for org.opendaylight.usc.client.netconf.ProtocolSessionPromise.java

Source

/*
 * Copyright (c) 2015 Huawei, 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.usc.client.netconf;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelOption;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.Promise;

import java.net.InetSocketAddress;

import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;

import org.opendaylight.protocol.framework.ReconnectStrategy;
import org.opendaylight.usc.plugin.UscPlugin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Preconditions;

@ThreadSafe
final class ProtocolSessionPromise<S> extends DefaultPromise<S> {
    private final Logger LOG = LoggerFactory.getLogger(ProtocolSessionPromise.class);
    private final UscPlugin plugin;
    private final ReconnectStrategy strategy;
    private final InetSocketAddress address;
    private final Bootstrap b;

    @GuardedBy("this")
    private Future<?> pending;

    ProtocolSessionPromise(UscPlugin plugin, final EventExecutor executor, final InetSocketAddress address,
            final ReconnectStrategy strategy, final Bootstrap b) {
        super(executor);
        this.plugin = plugin;
        this.strategy = Preconditions.checkNotNull(strategy);
        this.address = Preconditions.checkNotNull(address);
        this.b = Preconditions.checkNotNull(b);
    }

    synchronized void connect() {
        final Object lock = this;

        try {
            final int timeout = this.strategy.getConnectTimeout();

            LOG.debug("Promise {} attempting connect for {}ms", lock, timeout);

            this.b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, timeout);

            LOG.warn("About to connect");

            // final ChannelFuture connectFuture = this.b.connect(this.address);

            final ChannelFuture connectFuture = plugin.connect(b, this.address);
            LOG.warn("Connect finished");
            // Add listener that attempts reconnect by invoking this method again.
            connectFuture.addListener(new BootstrapConnectListener(lock));
            this.pending = connectFuture;
        } catch (final Exception e) {
            LOG.info("Failed to connect to {}", address, e);
            setFailure(e);
        }
    }

    @Override
    public synchronized boolean cancel(final boolean mayInterruptIfRunning) {
        if (super.cancel(mayInterruptIfRunning)) {
            this.pending.cancel(mayInterruptIfRunning);
            return true;
        }

        return false;
    }

    @Override
    public synchronized Promise<S> setSuccess(final S result) {
        LOG.debug("Promise {} completed", this);
        this.strategy.reconnectSuccessful();
        return super.setSuccess(result);
    }

    private class BootstrapConnectListener implements ChannelFutureListener {
        private final Object lock;

        public BootstrapConnectListener(final Object lock) {
            this.lock = lock;
        }

        @Override
        public void operationComplete(final ChannelFuture cf) throws Exception {
            synchronized (lock) {

                LOG.debug("Promise {} connection resolved", lock);

                // Triggered when a connection attempt is resolved.
                Preconditions.checkState(ProtocolSessionPromise.this.pending.equals(cf));

                /*
                 * The promise we gave out could have been cancelled, which cascades to the connect getting cancelled,
                 * but there is a slight race window, where the connect is already resolved, but the listener has not
                 * yet been notified -- cancellation at that point won't stop the notification arriving, so we have to
                 * close the race here.
                 */
                if (isCancelled()) {
                    if (cf.isSuccess()) {
                        LOG.debug("Closing channel for cancelled promise {}", lock);
                        cf.channel().close();
                    }
                    return;
                }

                if (cf.isSuccess()) {
                    LOG.debug("Promise {} connection successful", lock);
                    return;
                }

                LOG.debug("Attempt to connect to {} failed", ProtocolSessionPromise.this.address, cf.cause());

                final Future<Void> rf = ProtocolSessionPromise.this.strategy.scheduleReconnect(cf.cause());
                rf.addListener(new ReconnectingStrategyListener());
                ProtocolSessionPromise.this.pending = rf;
            }
        }

        private class ReconnectingStrategyListener implements FutureListener<Void> {
            @Override
            public void operationComplete(final Future<Void> sf) {
                synchronized (lock) {
                    // Triggered when a connection attempt is to be made.
                    Preconditions.checkState(ProtocolSessionPromise.this.pending.equals(sf));

                    /*
                     * The promise we gave out could have been cancelled, which cascades to the reconnect attempt
                     * getting cancelled, but there is a slight race window, where the reconnect attempt is already
                     * enqueued, but the listener has not yet been notified -- if cancellation happens at that point, we
                     * need to catch it here.
                     */
                    if (!isCancelled()) {
                        if (sf.isSuccess()) {
                            connect();
                        } else {
                            setFailure(sf.cause());
                        }
                    }
                }
            }
        }

    }

}