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

io.reactivex.netty.client.RxClientImpl Maven / Gradle / Ivy

There is a newer version: 0.3.18
Show newest version
/*
 * Copyright 2014 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.client;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.reactivex.netty.channel.ObservableConnection;
import io.reactivex.netty.metrics.MetricEventsListener;
import io.reactivex.netty.metrics.MetricEventsSubject;
import io.reactivex.netty.pipeline.PipelineConfigurator;
import io.reactivex.netty.pipeline.PipelineConfigurators;
import rx.Observable;
import rx.Observable.OnSubscribe;
import rx.Subscriber;
import rx.Subscription;

import java.util.concurrent.atomic.AtomicBoolean;

/**
 * The base class for all connection oriented clients inside RxNetty.
 * 
 * @param  The request object type for this client.
 * @param  The response object type for this client.
 */
public class RxClientImpl implements RxClient {

    protected final String name;
    protected final ServerInfo serverInfo;
    protected final Bootstrap clientBootstrap;
    protected final PipelineConfigurator pipelineConfigurator;
    protected final ClientChannelFactory channelFactory;
    protected final ClientConnectionFactory> connectionFactory;
    protected final ClientConfig clientConfig;
    protected final MetricEventsSubject> eventsSubject;
    protected final ConnectionPool pool;
    private final AtomicBoolean isShutdown = new AtomicBoolean();

    public RxClientImpl(String name, ServerInfo serverInfo, Bootstrap clientBootstrap,
                        PipelineConfigurator pipelineConfigurator,
                        ClientConfig clientConfig, ClientChannelFactory channelFactory,
                        ClientConnectionFactory> connectionFactory,
                        MetricEventsSubject> eventsSubject) {
        if (null == name) {
            throw new NullPointerException("Name can not be null.");
        }
        if (null == clientBootstrap) {
            throw new NullPointerException("Client bootstrap can not be null.");
        }
        if (null == serverInfo) {
            throw new NullPointerException("Server info can not be null.");
        }
        if (null == clientConfig) {
            throw new NullPointerException("Client config can not be null.");
        }
        if (null == connectionFactory) {
            throw new NullPointerException("Connection factory can not be null.");
        }
        if (null == channelFactory) {
            throw new NullPointerException("Channel factory can not be null.");
        }
        this.name = name;
        pool = null;
        this.eventsSubject = eventsSubject;
        this.clientConfig = clientConfig;
        this.serverInfo = serverInfo;
        this.clientBootstrap = clientBootstrap;
        this.connectionFactory = connectionFactory;
        this.connectionFactory.useMetricEventsSubject(eventsSubject);
        this.channelFactory = channelFactory;
        this.channelFactory.useMetricEventsSubject(eventsSubject);
        this.pipelineConfigurator = pipelineConfigurator;
        final PipelineConfigurator configurator = adaptPipelineConfigurator(pipelineConfigurator, clientConfig,
                                                                                  eventsSubject);
        this.clientBootstrap.handler(new ChannelInitializer() {
            @Override
            public void initChannel(Channel ch) throws Exception {
                configurator.configureNewPipeline(ch.pipeline());
            }
        });
    }

    public RxClientImpl(String name, ServerInfo serverInfo, Bootstrap clientBootstrap,
                        PipelineConfigurator pipelineConfigurator,
                        ClientConfig clientConfig, ConnectionPoolBuilder poolBuilder,
                        MetricEventsSubject> eventsSubject) {
        if (null == name) {
            throw new NullPointerException("Name can not be null.");
        }
        if (null == clientBootstrap) {
            throw new NullPointerException("Client bootstrap can not be null.");
        }
        if (null == serverInfo) {
            throw new NullPointerException("Server info can not be null.");
        }
        if (null == clientConfig) {
            throw new NullPointerException("Client config can not be null.");
        }
        if (null == poolBuilder) {
            throw new NullPointerException("Pool builder can not be null.");
        }
        this.name = name;
        this.eventsSubject = eventsSubject;
        this.clientConfig = clientConfig;
        this.serverInfo = serverInfo;
        this.clientBootstrap = clientBootstrap;
        this.pipelineConfigurator = pipelineConfigurator;
        final PipelineConfigurator configurator = adaptPipelineConfigurator(pipelineConfigurator, clientConfig,
                                                                                  eventsSubject);
        this.clientBootstrap.handler(new ChannelInitializer() {
            @Override
            public void initChannel(Channel ch) throws Exception {
                configurator.configureNewPipeline(ch.pipeline());
            }
        });
        pool = poolBuilder.build();
        channelFactory = poolBuilder.getChannelFactory();
        connectionFactory = poolBuilder.getConnectionFactory();
    }

    /**
     * A lazy connect to the {@link RxClient.ServerInfo} for this client. Every subscription to the returned {@link Observable}
     * will create a fresh connection.
     *
     * @return Observable for the connect. Every new subscription will create a fresh connection.
     */
    @Override
    public Observable> connect() {
        if (isShutdown.get()) {
            return Observable.error(new IllegalStateException("Client is already shutdown."));
        }

        Observable> toReturn;
        if (null != pool) {
            toReturn = pool.acquire();
        } else {
            toReturn = Observable.create(new OnSubscribe>() {
                @Override
                public void call(final Subscriber> subscriber) {
                    try {
                        channelFactory.connect(subscriber, serverInfo, connectionFactory);
                    } catch (Throwable throwable) {
                        subscriber.onError(throwable);
                    }
                }
            });
        }

        return toReturn.take(1); // We only need one connection, even if the underlying source emits multiple.
    }

    @Override
    public void shutdown() {
        if (!isShutdown.compareAndSet(false, true)) {
            return;
        }

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

    @Override
    public String name() {
        return name;
    }

    protected PipelineConfigurator adaptPipelineConfigurator(PipelineConfigurator pipelineConfigurator,
                                                                   ClientConfig clientConfig,
                                                                   MetricEventsSubject> eventsSubject) {
        return PipelineConfigurators.createClientConfigurator(pipelineConfigurator, clientConfig, eventsSubject);
    }

    @Override
    public Subscription subscribe(MetricEventsListener> listener) {
        return eventsSubject.subscribe(listener);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy