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

io.reactivex.netty.protocol.tcp.client.TcpClientImpl Maven / Gradle / Ivy

There is a newer version: 0.5.3
Show newest version
/*
 * Copyright 2016 Netflix, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package io.reactivex.netty.protocol.tcp.client;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.handler.logging.LogLevel;
import io.netty.util.concurrent.EventExecutorGroup;
import io.reactivex.netty.HandlerNames;
import io.reactivex.netty.channel.ChannelSubscriberEvent;
import io.reactivex.netty.channel.Connection;
import io.reactivex.netty.channel.ConnectionImpl;
import io.reactivex.netty.channel.DetachedChannelPipeline;
import io.reactivex.netty.client.ChannelProvider;
import io.reactivex.netty.client.ChannelProviderFactory;
import io.reactivex.netty.client.ClientState;
import io.reactivex.netty.client.ConnectionProvider;
import io.reactivex.netty.client.ConnectionProviderFactory;
import io.reactivex.netty.client.ConnectionRequest;
import io.reactivex.netty.client.Host;
import io.reactivex.netty.client.HostConnector;
import io.reactivex.netty.client.internal.SingleHostConnectionProvider;
import io.reactivex.netty.events.EventSource;
import io.reactivex.netty.internal.InternalReadTimeoutHandler;
import io.reactivex.netty.protocol.tcp.client.events.TcpClientEventListener;
import io.reactivex.netty.protocol.tcp.client.events.TcpClientEventPublisher;
import io.reactivex.netty.protocol.tcp.client.internal.TcpChannelProviderFactory;
import io.reactivex.netty.ssl.SslCodec;
import rx.Observable;
import rx.Observable.OnSubscribe;
import rx.Subscriber;
import rx.Subscription;
import rx.functions.Action0;
import rx.functions.Action1;
import rx.functions.Func0;
import rx.functions.Func1;
import rx.subscriptions.Subscriptions;

import javax.net.ssl.SSLEngine;
import java.net.SocketAddress;
import java.util.concurrent.TimeUnit;

public final class TcpClientImpl extends TcpClient {

    private final ClientState state;
    private final TcpClientEventPublisher eventPublisher;
    private final InterceptingTcpClient interceptingTcpClient;
    private ConnectionRequestImpl requestSetLazily;

    private TcpClientImpl(ClientState state, TcpClientEventPublisher eventPublisher,
                          InterceptingTcpClient interceptingTcpClient) {
        this.state = state;
        this.eventPublisher = eventPublisher;
        this.interceptingTcpClient = interceptingTcpClient;
    }

    @Override
    public ConnectionRequest createConnectionRequest() {
        return requestSetLazily;
    }

    @Override
    public  TcpClient channelOption(ChannelOption option, T value) {
        return copy(state.channelOption(option, value), eventPublisher);
    }

    @Override
    public TcpClient readTimeOut(final int timeOut, final TimeUnit timeUnit) {
        return addChannelHandlerFirst(HandlerNames.ClientReadTimeoutHandler.getName(), new Func0() {
            @Override
            public ChannelHandler call() {
                return new InternalReadTimeoutHandler(timeOut, timeUnit);
            }
        });
    }

    @Override
    public  TcpClient addChannelHandlerFirst(String name, Func0 handlerFactory) {
        return copy(state.addChannelHandlerFirst(name, handlerFactory), eventPublisher);
    }

    @Override
    public  TcpClient addChannelHandlerFirst(EventExecutorGroup group, String name,
                                                             Func0 handlerFactory) {
        return copy(state.addChannelHandlerFirst(group, name, handlerFactory), eventPublisher);
    }

    @Override
    public  TcpClient addChannelHandlerLast(String name, Func0 handlerFactory) {
        return copy(state.addChannelHandlerLast(name, handlerFactory), eventPublisher);
    }

    @Override
    public  TcpClient addChannelHandlerLast(EventExecutorGroup group, String name,
                                                            Func0 handlerFactory) {
        return copy(state.addChannelHandlerLast(group, name, handlerFactory), eventPublisher);
    }

    @Override
    public  TcpClient addChannelHandlerBefore(String baseName, String name,
                                                              Func0 handlerFactory) {
        return copy(state.addChannelHandlerBefore(baseName, name, handlerFactory), eventPublisher);
    }

    @Override
    public  TcpClient addChannelHandlerBefore(EventExecutorGroup group, String baseName, String name,
                                                              Func0 handlerFactory) {
        return copy(state.addChannelHandlerBefore(group, baseName, name, handlerFactory), eventPublisher);
    }

    @Override
    public  TcpClient addChannelHandlerAfter(String baseName, String name,
                                                             Func0 handlerFactory) {
        return copy(state.addChannelHandlerAfter(baseName, name, handlerFactory), eventPublisher);
    }

    @Override
    public  TcpClient addChannelHandlerAfter(EventExecutorGroup group, String baseName, String name,
                                                             Func0 handlerFactory) {
        return copy(state.addChannelHandlerAfter(group, baseName, name, handlerFactory), eventPublisher);
    }

    @Override
    public  TcpClient pipelineConfigurator(Action1 pipelineConfigurator) {
        return copy(state.pipelineConfigurator(pipelineConfigurator), eventPublisher);
    }

    @Override
    @Deprecated
    public TcpClient enableWireLogging(LogLevel wireLoggingLevel) {
        return copy(state.enableWireLogging(wireLoggingLevel), eventPublisher);
    }

    @Override
    public TcpClient enableWireLogging(String name, LogLevel wireLoggingLevel) {
        return copy(state.enableWireLogging(name, wireLoggingLevel), eventPublisher);
    }

    @Override
    public TcpClient secure(Func1 sslEngineFactory) {
        return copy(state.secure(sslEngineFactory), eventPublisher);
    }

    @Override
    public TcpClient secure(SSLEngine sslEngine) {
        return copy(state.secure(sslEngine), eventPublisher);
    }

    @Override
    public TcpClient secure(SslCodec sslCodec) {
        return copy(state.secure(sslCodec), eventPublisher);
    }

    @Override
    public TcpClient unsafeSecure() {
        return copy(state.unsafeSecure(), eventPublisher);
    }

    @Override
    public TcpClient channelProvider(ChannelProviderFactory providerFactory) {
        return copy(state.channelProviderFactory(providerFactory), eventPublisher);
    }

    @Override
    public Subscription subscribe(TcpClientEventListener listener) {
        return interceptingTcpClient.subscribe(listener);
    }

    @Override
    public TcpClientInterceptorChain intercept() {
        return interceptingTcpClient.intercept();
    }

    /*Visible for testing*/ ClientState getClientState() {
        return state;
    }

    public static  TcpClientImpl create(SocketAddress socketAddress) {
        return create(socketAddress, ClientState.defaultEventloopGroup(), ClientState.defaultSocketChannelClass());
    }

    public static  TcpClientImpl create(SocketAddress socketAddress, EventLoopGroup eventLoopGroup,
                                                    Class channelClass) {
        final Host host = new Host(socketAddress);
        ConnectionProviderFactory factory = new ConnectionProviderFactory() {
            @Override
            public ConnectionProvider newProvider(Observable> hosts) {
                return new SingleHostConnectionProvider<>(hosts);
            }
        };
        Observable hostStream = Observable.just(host);
        ClientState state = ClientState.create(factory, hostStream, eventLoopGroup, channelClass);
        final TcpClientEventPublisher eventPublisher = new TcpClientEventPublisher();
        return _create(state, eventPublisher);
    }

    public static  TcpClientImpl create(ConnectionProviderFactory factory,
                                                    Observable hostStream) {
        ClientState state = ClientState.create(factory, hostStream);
        final TcpClientEventPublisher eventPublisher = new TcpClientEventPublisher();
        return _create(state, eventPublisher);
    }

    private static  TcpClientImpl copy(final ClientState state,
                                                   TcpClientEventPublisher eventPublisher) {
        return _create(state, eventPublisher);
    }

    /*Visible for testing*/ static  TcpClientImpl _create(ClientState state,
                                                                      TcpClientEventPublisher eventPublisher) {
        DetachedChannelPipeline channelPipeline = state.unsafeDetachedPipeline();
        state = state.channelProviderFactory(new TcpChannelProviderFactory(channelPipeline,
                                                                           state.getChannelProviderFactory()));

        HostConnectorFactory hostConnectorFactory = new HostConnectorFactory<>(state, eventPublisher);

        ConnectionProvider cp = state.getFactory()
                                             .newProvider(state.getHostStream().map(hostConnectorFactory));

        InterceptingTcpClient interceptingTcpClient = new InterceptingTcpClientImpl<>(cp, eventPublisher);
        TcpClientImpl client = new TcpClientImpl<>(state, eventPublisher, interceptingTcpClient);
        client.requestSetLazily = new ConnectionRequestImpl<>(cp);
        return client;
    }

    private static class HostConnectorFactory implements Func1> {

        private final ChannelProviderFactory channelProviderFactory;
        private final TcpClientEventPublisher clientEventPublisher;
        private final ClientState state;

        public HostConnectorFactory(ClientState state, TcpClientEventPublisher clientEventPublisher) {
            this.state = state;
            channelProviderFactory = state.getChannelProviderFactory();
            this.clientEventPublisher = clientEventPublisher;
        }

        @Override
        public HostConnector call(final Host host) {
            TcpClientEventPublisher hostEventPublisher = new TcpClientEventPublisher();
            @SuppressWarnings({"unchecked", "rawtypes"})
            EventSource eventSource = hostEventPublisher;
            hostEventPublisher.subscribe(clientEventPublisher);
            @SuppressWarnings("unchecked")
            ChannelProvider channelProvider = channelProviderFactory.newProvider(host, eventSource, hostEventPublisher,
                                                                                 hostEventPublisher);
            return new HostConnector<>(host, new TerminalConnectionProvider<>(hostEventPublisher, host,
                                                                              channelProvider, state),
                                       hostEventPublisher, hostEventPublisher, hostEventPublisher);
        }
    }

    private static class TerminalConnectionProvider implements ConnectionProvider {

        private final Host host;
        private final Bootstrap bootstrap;
        private final ChannelProvider channelProvider;

        public TerminalConnectionProvider(TcpClientEventPublisher hostEventPublisher,
                                          Host host, ChannelProvider channelProvider, ClientState state) {
            this.host = host;
            this.channelProvider = channelProvider;
            bootstrap = state.newBootstrap(hostEventPublisher, hostEventPublisher);
        }

        @Override
        public Observable> newConnectionRequest() {
            return channelProvider.newChannel(Observable.create(new OnSubscribe() {
                @Override
                public void call(final Subscriber s) {
                    final ChannelFuture cf = bootstrap.connect(host.getHost());
                    s.add(Subscriptions.create(new Action0() {
                        @Override
                        public void call() {
                            if (null != cf && !cf.isDone()) {
                                cf.cancel(false);
                            }
                        }
                    }));
                    cf.addListener(new ChannelFutureListener() {
                        @Override
                        public void operationComplete(final ChannelFuture future) throws Exception {
                            if (!future.isSuccess()) {
                                s.onError(future.cause());
                            } else {
                                s.onNext(cf.channel());
                                s.onCompleted();
                            }
                        }
                    });
                }
            })).switchMap(new Func1>() {
                @Override
                public Observable call(final Channel channel) {

                    /*
                     * If channel is unregistered, all handlers are removed and hence the event will not flow through
                     * to the handler for the subscriber to be notified.
                      * So, here the channel is directly passed through the chain if the channel isn't registered.
                     */
                    if (channel.eventLoop().inEventLoop()) {
                        if (channel.isRegistered()) {
                            return Observable.create(new OnSubscribe() {
                                @Override
                                public void call(Subscriber subscriber) {
                                    channel.pipeline().fireUserEventTriggered(new ChannelSubscriberEvent<>(subscriber));
                                }
                            });
                        } else {
                            return Observable.just(channel);
                        }
                    } else {
                        return Observable.create(new OnSubscribe() {
                            @Override
                            public void call(final Subscriber subscriber) {
                                channel.eventLoop().execute(new Runnable() {
                                    @Override
                                    public void run() {
                                        if (channel.isRegistered()) {
                                            channel.pipeline()
                                                   .fireUserEventTriggered(new ChannelSubscriberEvent<>(subscriber));
                                        } else {
                                            subscriber.onNext(channel);
                                            subscriber.onCompleted();
                                        }
                                    }
                                });
                            }
                        });
                    }
                }
            }).map(new Func1>() {
                @Override
                public Connection call(Channel channel) {
                    return ConnectionImpl.fromChannel(channel);
                }
            });
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy