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

Java tutorial

Introduction

Here is the source code for org.opendaylight.usc.client.netconf.ReconnectPromise.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.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.concurrent.Promise;

import java.net.InetSocketAddress;

import org.opendaylight.netconf.client.NetconfClientSession;
import org.opendaylight.protocol.framework.ReconnectStrategy;
import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
import org.opendaylight.usc.client.netconf.UscNetconfClientDispatcherImpl.PipelineInitializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Preconditions;

final class ReconnectPromise extends DefaultPromise<Void> {
    private final Logger LOG = LoggerFactory.getLogger(ReconnectPromise.class);

    private final UscNetconfClientDispatcherImpl dispatcher;
    private final InetSocketAddress address;
    private final ReconnectStrategyFactory strategyFactory;
    private final Bootstrap b;
    private final PipelineInitializer<NetconfClientSession> initializer;
    private Future<?> pending;

    public ReconnectPromise(final EventExecutor executor, UscNetconfClientDispatcherImpl dispatcher,
            final InetSocketAddress address, final ReconnectStrategyFactory connectStrategyFactory,
            final Bootstrap b, final PipelineInitializer<NetconfClientSession> initializer) {
        super(executor);
        this.dispatcher = dispatcher;
        this.b = b;
        this.initializer = Preconditions.checkNotNull(initializer);
        this.address = Preconditions.checkNotNull(address);
        this.strategyFactory = Preconditions.checkNotNull(connectStrategyFactory);
    }

    synchronized void connect() {
        final ReconnectStrategy cs = this.strategyFactory.createReconnectStrategy();

        // Set up a client with pre-configured bootstrap, but add a closed channel handler into the pipeline to
        // support reconnect attempts
        pending = dispatcher.createClient(this.address, cs, b, new PipelineInitializer<NetconfClientSession>() {
            @Override
            public void initializeChannel(final Channel channel, final Promise<NetconfClientSession> promise) {
                initializer.initializeChannel(channel, promise);
                // add closed channel handler
                // This handler has to be added as last channel handler and the channel inactive event has
                // to be caught by it
                // Handlers in front of it can react to channelInactive event, but have to forward the event
                // or the reconnect will not work
                // This handler is last so all handlers in front of it can handle channel inactive (to e.g.
                // resource cleanup) before a new connection is started
                channel.pipeline().addLast(new ClosedChannelHandler(ReconnectPromise.this));
            }
        });

        pending.addListener(new GenericFutureListener<Future<Object>>() {
            @Override
            public void operationComplete(Future<Object> future) throws Exception {
                if (!future.isSuccess()) {
                    ReconnectPromise.this.setFailure(future.cause());
                }
            }
        });
    }

    /**
     *
     * @return true if initial connection was established successfully, false if initial connection failed due to e.g.
     *         Connection refused, Negotiation failed
     */
    private boolean isInitialConnectFinished() {
        Preconditions.checkNotNull(pending);
        return pending.isDone() && pending.isSuccess();
    }

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

        return false;
    }

    /**
     * Channel handler that responds to channelInactive event and reconnects the session. Only if the promise was not
     * canceled.
     */
    private final class ClosedChannelHandler extends ChannelInboundHandlerAdapter {
        private final ReconnectPromise promise;

        public ClosedChannelHandler(final ReconnectPromise promise) {
            this.promise = promise;
        }

        @Override
        public void channelInactive(final ChannelHandlerContext ctx) throws Exception {
            // This is the ultimate channel inactive handler, not forwarding
            if (promise.isCancelled()) {
                return;
            }

            if (promise.isInitialConnectFinished() == false) {
                LOG.debug("Connection to {} was dropped during negotiation, reattempting", promise.address);
            }

            LOG.debug("Reconnecting after connection to {} was dropped", promise.address);
            promise.connect();
        }
    }

}