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

reactor.tcp.netty.NettyTcpConnection 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.netty;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import reactor.core.Environment;
import reactor.core.composable.Deferred;
import reactor.core.composable.Promise;
import reactor.event.Event;
import reactor.event.dispatch.Dispatcher;
import reactor.io.Buffer;
import reactor.tcp.AbstractTcpConnection;
import reactor.tcp.encoding.Codec;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.concurrent.TimeUnit;

/**
 * A {@link reactor.tcp.TcpConnection} implementation that uses Netty.
 *
 * @author Jon Brisbin
 * @author Stephane Maldini
 */
public class NettyTcpConnection extends AbstractTcpConnection {

	private final SocketChannel                  channel;
	private final NettyTcpConnectionConsumerSpec consumerSpec;
	private final InetSocketAddress              remoteAddress;
	private volatile boolean closing = false;

	NettyTcpConnection(final Environment env,
	                   Codec codec,
	                   Dispatcher ioDispatcher,
	                   Dispatcher eventsDispatcher,
	                   SocketChannel channel) {
		this(env, codec, ioDispatcher, eventsDispatcher, channel, null);
	}

	NettyTcpConnection(final Environment env,
	                   Codec codec,
	                   Dispatcher ioDispatcher,
	                   Dispatcher eventsDispatcher,
	                   SocketChannel channel,
	                   InetSocketAddress remoteAddress) {
		super(env, codec, ioDispatcher, eventsDispatcher);
		this.channel = channel;
		this.consumerSpec = new NettyTcpConnectionConsumerSpec(channel);
		this.remoteAddress = remoteAddress;
	}

	/**
	 * Return the {@link SocketChannel} in use by this connection.
	 *
	 * @return the {@link SocketChannel} in use
	 */
	public SocketChannel channel() {
		return channel;
	}

	@Override
	public ConsumerSpec on() {
		return consumerSpec;
	}

	@Override
	public void close() {
		super.close();
		closing = true;
		channel.disconnect().awaitUninterruptibly();
		channel.close().awaitUninterruptibly();
	}

	@Override
	public boolean consumable() {
		return !channel.isInputShutdown();
	}

	@Override
	public boolean writable() {
		return !channel.isOutputShutdown();
	}

	@Override
	public InetSocketAddress remoteAddress() {
		return remoteAddress;
	}

	boolean isClosing() {
		return closing;
	}

	void notifyRead(Object obj) {
		eventsReactor.notify(read.getT2(), (Event.class.isInstance(obj) ? (Event)obj : Event.wrap(obj)));
	}

	void notifyError(Throwable throwable) {
		eventsReactor.notify(throwable.getClass(), Event.wrap(throwable));
	}

	@Override
	protected void write(Buffer data, final Deferred> onComplete, boolean flush) {
		write(data.byteBuffer(), onComplete, flush);
	}

	protected void write(ByteBuffer data, final Deferred> onComplete, boolean flush) {
		ByteBuf buf = channel.alloc().buffer(data.remaining());
		buf.writeBytes(data);
		write(buf, onComplete, flush);
	}

	@Override
	protected void write(Object data, final Deferred> onComplete, final boolean flush) {
		ChannelFuture writeFuture = (flush ? channel.writeAndFlush(data) : channel.write(data));
		writeFuture.addListener(new ChannelFutureListener() {
			@Override
			public void operationComplete(ChannelFuture future) throws Exception {
				boolean success = future.isSuccess();

				if(!success) {
					Throwable t = future.cause();
					eventsReactor.notify(t, Event.wrap(t));
					if(null != onComplete) {
						onComplete.accept(t);
					}
				} else if(null != onComplete) {
					onComplete.accept((Void)null);
				}
			}
		});
	}

	@Override
	protected void flush() {
		channel.flush();
	}

	@Override
	public String toString() {
		return "NettyTcpConnection{" +
				"channel=" + channel +
				", remoteAddress=" + remoteAddress +
				'}';
	}

	private static class ChannelCloseListener implements ChannelFutureListener {
		private final Runnable onClose;

		private ChannelCloseListener(Runnable onClose) {
			this.onClose = onClose;
		}

		@Override
		public void operationComplete(ChannelFuture future) throws Exception {
			if(null != onClose) {
				onClose.run();
			}
		}
	}

	private static class IdleReadListener extends IdleStateHandler {
		private final Runnable onReadIdle;

		private IdleReadListener(long readerIdleTimeSeconds, Runnable onReadIdle) {
			super(readerIdleTimeSeconds, 0, 0, TimeUnit.MILLISECONDS);
			this.onReadIdle = onReadIdle;
		}

		@Override
		protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception {
			if(evt.state() == IdleState.READER_IDLE) {
				onReadIdle.run();
			}
			super.channelIdle(ctx, evt);
		}
	}

	private static class IdleWriteListener extends IdleStateHandler {
		private final Runnable onWriteIdle;

		private IdleWriteListener(long writerIdleTimeSeconds, Runnable onWriteIdle) {
			super(0, writerIdleTimeSeconds, 0, TimeUnit.MILLISECONDS);
			this.onWriteIdle = onWriteIdle;
		}

		@Override
		protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception {
			if(evt.state() == IdleState.WRITER_IDLE) {
				onWriteIdle.run();
			}
			super.channelIdle(ctx, evt);
		}
	}

	private static class NettyTcpConnectionConsumerSpec implements ConsumerSpec {
		private final SocketChannel channel;

		private NettyTcpConnectionConsumerSpec(SocketChannel channel) {
			this.channel = channel;
		}

		@Override
		public ConsumerSpec close(final Runnable onClose) {
			channel.closeFuture().addListener(new ChannelCloseListener(onClose));
			return this;
		}

		@Override
		public ConsumerSpec readIdle(long idleTimeout, final Runnable onReadIdle) {
			final IdleReadListener irl = new IdleReadListener(idleTimeout, onReadIdle);
			channel.closeFuture().addListener(new ChannelFutureListener() {
				@Override
				public void operationComplete(ChannelFuture future) throws Exception {
					try {
						irl.channelInactive(null);
					} catch(NullPointerException ignored) {}
				}
			});
			channel.pipeline().addFirst(irl);
			return this;
		}

		@Override
		public ConsumerSpec writeIdle(long idleTimeout, final Runnable onWriteIdle) {
			final IdleWriteListener iwl = new IdleWriteListener(idleTimeout, onWriteIdle);
			channel.closeFuture().addListener(new ChannelFutureListener() {
				@Override
				public void operationComplete(ChannelFuture future) throws Exception {
					try {
						iwl.channelInactive(null);
					} catch(NullPointerException ignored) {}
				}
			});
			channel.pipeline().addLast(iwl);
			return this;
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy