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

reactor.tcp.TcpServer Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2011-2013 GoPivotal, Inc. All Rights Reserved.
 *
 * 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 reactor.tcp;

import reactor.core.Environment;
import reactor.core.Reactor;
import reactor.core.composable.Promise;
import reactor.event.Event;
import reactor.event.registry.CachingRegistry;
import reactor.event.registry.Registration;
import reactor.event.registry.Registry;
import reactor.event.selector.Selector;
import reactor.event.selector.Selectors;
import reactor.function.Consumer;
import reactor.io.Buffer;
import reactor.tcp.config.ServerSocketOptions;
import reactor.tcp.config.SslOptions;
import reactor.tcp.encoding.Codec;
import reactor.tuple.Tuple2;
import reactor.util.Assert;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.Iterator;

/**
 * Base functionality needed by all servers that communicate with clients over TCP.
 *
 * @param  The type that will be received by this server
 * @param  The type that will be sent by this server
 *
 * @author Jon Brisbin
 */
public abstract class TcpServer {

	private final Event>        selfEvent   = Event.wrap(this);
	private final Tuple2         start       = Selectors.$();
	private final Tuple2         shutdown    = Selectors.$();
	private final Tuple2         open        = Selectors.$();
	private final Tuple2         close       = Selectors.$();
	private final Registry> connections = new CachingRegistry>();

	private final Reactor                reactor;
	private final Codec codec;

	protected final Environment env;

	protected TcpServer(@Nonnull Environment env,
											@Nonnull Reactor reactor,
											@Nullable InetSocketAddress listenAddress,
											ServerSocketOptions options,
											SslOptions sslOptions,
											@Nullable Codec codec,
											@Nonnull Collection>> connectionConsumers) {
		Assert.notNull(env, "A TcpServer cannot be created without a properly-configured Environment.");
		Assert.notNull(reactor, "A TcpServer cannot be created without a properly-configured Reactor.");
		this.env = env;
		this.reactor = reactor;
		this.codec = codec;

		Assert.notNull(connectionConsumers, "Connection Consumers cannot be null.");
		for (final Consumer> consumer : connectionConsumers) {
			this.reactor.on(open.getT1(), new Consumer>>() {
				@Override
				public void accept(Event> ev) {
					consumer.accept(ev.getData());
				}
			});
		}
	}

	/**
	 * Start this server.
	 *
	 * @return {@literal this}
	 */
	public TcpServer start() {
		return start(null);
	}

	/**
	 * Start this server, invoking the given callback when the server has started.
	 *
	 * @param started Callback to invoke when the server is started. May be {@literal null}.
	 * @return {@literal this}
	 */
	public abstract TcpServer start(@Nullable Consumer started);

	/**
	 * Shutdown this server.
	 *
	 * @return a Promise that will be completed with the shutdown outcome
	 */
	public abstract Promise shutdown();

	/**
	 * Subclasses should register the given channel and connection for later use.
	 *
	 * @param channel    The channel object.
	 * @param connection The {@link TcpConnection}.
	 * @param         The type of the channel object.
	 * @return {@link Registration} of this connection in the {@link Registry}.
	 */
	protected  Registration> register(@Nonnull C channel,
																																				@Nonnull TcpConnection connection) {
		Assert.notNull(channel, "Channel cannot be null.");
		Assert.notNull(connection, "TcpConnection cannot be null.");
		return connections.register(Selectors.$(channel), connection);
	}

	/**
	 * Find the {@link TcpConnection} for the given channel object.
	 *
	 * @param channel The channel object.
	 * @param      The type of the channel object.
	 * @return The {@link TcpConnection} associated with the given channel.
	 */
	protected  TcpConnection select(@Nonnull C channel) {
		Assert.notNull(channel, "Channel cannot be null.");
		Iterator>> conns = connections.select(channel).iterator();
		if (conns.hasNext()) {
			return conns.next().getObject();
		} else {
			TcpConnection conn = createConnection(channel);
			register(channel, conn);
			notifyOpen(conn);
			return conn;
		}
	}

	/**
	 * Close the given channel.
	 *
	 * @param channel The channel object.
	 * @param      The type of the channel object.
	 */
	protected  void close(@Nonnull C channel) {
		Assert.notNull(channel, "Channel cannot be null");
		for (Registration> reg : connections.select(channel)) {
			TcpConnection conn = reg.getObject();
			reg.getObject().close();
			notifyClose(conn);
			reg.cancel();
		}
	}

	/**
	 * Subclasses should implement this method and provide a {@link TcpConnection} object.
	 *
	 * @param channel The channel object to associate with this connection.
	 * @param      The type of the channel object.
	 * @return The new {@link TcpConnection} object.
	 */
	protected abstract  TcpConnection createConnection(C channel);

	/**
	 * Notify this server than a global error has occurred.
	 *
	 * @param error The error to notify.
	 */
	protected void notifyError(@Nonnull Throwable error) {
		Assert.notNull(error, "Error cannot be null.");
		reactor.notify(error.getClass(), Event.wrap(error));
	}

	/**
	 * Notify this server's consumers that the server has started.
	 *
	 * @param started An optional callback to invoke.
	 */
	protected void notifyStart(@Nullable final Consumer started) {
		if (null != started) {
			reactor.on(start.getT1(), new Consumer>() {
				@Override
				public void accept(Event ev) {
					started.accept(null);
				}
			});
		}
		reactor.notify(start.getT2(), selfEvent);
	}

	/**
	 * Notify this server's consumers that the server has stopped.
	 */
	protected void notifyShutdown() {
		reactor.notify(shutdown.getT2(), selfEvent);
	}

	/**
	 * Notify this server's consumers that the given connection has been opened.
	 *
	 * @param conn The {@link TcpConnection} that was opened.
	 */
	protected void notifyOpen(@Nonnull TcpConnection conn) {
		reactor.notify(open.getT2(), Event.wrap(conn));
	}

	/**
	 * Notify this server's consumers that the given connection has been closed.
	 *
	 * @param conn The {@link TcpConnection} that was closed.
	 */
	protected void notifyClose(@Nonnull TcpConnection conn) {
		reactor.notify(close.getT2(), Event.wrap(conn));
	}

	/**
	 * Get the {@link Codec} in use.
	 *
	 * @return The codec. May be {@literal null}.
	 */
	@Nullable
	protected Codec getCodec() {
		return codec;
	}

	protected Reactor getReactor() {
		return this.reactor;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy