All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.eureka2.client.service.AbstractChannel Maven / Gradle / Ivy

package com.netflix.eureka2.client.service;

import com.netflix.eureka2.client.transport.TransportClient;
import com.netflix.eureka2.service.AbstractServiceChannel;
import com.netflix.eureka2.service.ServiceChannel;
import com.netflix.eureka2.transport.MessageConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rx.Observable;
import rx.functions.Action1;
import rx.functions.Func1;
import rx.subjects.ReplaySubject;
import rx.subjects.Subject;

import java.util.concurrent.atomic.AtomicBoolean;

/**
 * An abstract {@link ServiceChannel} implementation for common methods.
 *
 * @author Nitesh Kant
 */
public abstract class AbstractChannel extends AbstractServiceChannel {

    private static final Logger logger = LoggerFactory.getLogger(AbstractChannel.class);

    protected final TransportClient client;

    /**
     * There can only ever be one connection associated with a channel. This subject provides access to that connection
     * after a call is made to {@link #connect()}
     *
     * Why is this a {@link ReplaySubject}?
     *
     * Since there is always every a single connection created by this channel, everyone needs to get the same
     * connection. Now, the connection creation is lazy (in {@link #connect()} so we need a way to update this
     * {@link Observable}. Hence a {@link Subject} and one that replays the single connection created.
     */
    private ReplaySubject singleConnectionSubject;

    private volatile MessageConnection connectionIfConnected; // External callers should use "singleConnectionSubject"
    private final AtomicBoolean connectionRequestedOnce = new AtomicBoolean();

    protected AbstractChannel(final STATE initState, final TransportClient client) {
        super(initState);
        this.client = client;
        singleConnectionSubject = ReplaySubject.create();
    }

    @Override
    public Observable asLifecycleObservable() {
        return lifecycle;
    }

    @Override
    protected void _close() {
        if (logger.isDebugEnabled()) {
            logger.debug("Closing client interest channel with state: " + state.get());
        }

        if (null != connectionIfConnected) {
            connectionIfConnected.shutdown();
        }
    }

    /**
     * Idempotent method that returns the one and only connection associated with this channel.
     *
     * @return The one and only connection associated with this channel.
     */
    protected Observable connect() {
        if (connectionRequestedOnce.compareAndSet(false, true)) {
            return client.connect()
                    .take(1)
                    .map(new Func1() {
                        @Override
                        public MessageConnection call(final MessageConnection serverConnection) {
                            // Guarded by the connection state, so it will only be invoked once.
                            connectionIfConnected = serverConnection;
                            singleConnectionSubject.onNext(serverConnection);
                            singleConnectionSubject.onCompleted();
                            return serverConnection;
                        }
                    });
        } else {
            return singleConnectionSubject;
        }

    }

    protected void sendErrorOnConnection(MessageConnection connection, Throwable throwable) {
        if (logger.isDebugEnabled()) {
            logger.debug("Sending error to the server.", throwable);
        }
        subscribeToTransportSend(connection.onError(throwable), "error");
    }

    protected void sendAckOnConnection(MessageConnection connection) {
        if (logger.isDebugEnabled()) {
            logger.debug("Sending acknowledgment to the server.");
        }
        subscribeToTransportSend(connection.acknowledge(), "acknowledgment");
    }

    protected void subscribeToTransportSend(Observable sendResult, final String sendType) {
        sendResult.subscribe(new Action1() {
            @Override
            public void call(Void aVoid) {
                // Nothing to do for a void.
            }
        }, new Action1() {
            @Override
            public void call(Throwable throwable) {
                logger.warn("Failed to send " + sendType + " to the server. Closing the channel.", throwable);
                close();
            }
        });
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy