io.datakernel.stream.net.TcpStreamSocketConnection Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of async-streams Show documentation
Show all versions of async-streams Show documentation
Composable asynchronous/reactive streams with powerful data processing capabilities.
The newest version!
/*
* Copyright (C) 2015 SoftIndex LLC.
*
* 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.datakernel.stream.net;
import io.datakernel.bytebuf.ByteBuf;
import io.datakernel.eventloop.Eventloop;
import io.datakernel.eventloop.NioEventloop;
import io.datakernel.eventloop.TcpSocketConnection;
import io.datakernel.stream.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.channels.SocketChannel;
/**
* Represent the TCP connection which processes received items with {@link StreamProducer} and {@link StreamConsumer},
* which organized by binary protocol. It is created with socketChannel and sides exchange ByteBufs.
*/
public abstract class TcpStreamSocketConnection extends TcpSocketConnection {
private static final Logger logger = LoggerFactory.getLogger(TcpStreamSocketConnection.class);
protected final class Reader extends AbstractStreamProducer {
public Reader(Eventloop eventloop) {
super(eventloop);
}
@Override
protected void onSuspended() {
readInterest(false);
}
@Override
protected void onResumed() {
readInterest(true);
}
@Override
protected void onClosed() {
closeIfDone();
}
@Override
protected void onClosedWithError(Exception e) {
TcpStreamSocketConnection.this.onInternalException(e);
}
}
protected final class Writer extends AbstractStreamConsumer implements StreamDataReceiver {
public Writer(Eventloop eventloop) {
super(eventloop);
}
@Override
public StreamDataReceiver getDataReceiver() {
return this;
}
@Override
public void onEndOfStream() {
if (writeQueue.isEmpty()) {
closeUpstream();
closeIfDone();
}
}
@Override
public void onError(Exception e) {
onInternalException(e);
}
/**
* Method which is called after each receiving result
*
* @param buf received item
*/
@Override
public void onData(ByteBuf buf) {
write(buf);
if (writeQueue.isEmpty()) {
resumeUpstream();
} else {
suspendUpstream();
}
}
}
public static final int DEFAULT_STREAM_BUFFER_SIZE = 256 * 1024;
protected final Reader socketReader;
protected final Writer socketWriter;
private String name;
/**
* Returns new instance of TcpStreamSocketConnection
*
* @param eventloop eventloop in with this connection will be handled
* @param socketChannel socketChannel for this connection
*/
public TcpStreamSocketConnection(NioEventloop eventloop, SocketChannel socketChannel) {
super(eventloop, socketChannel);
this.receiveBufferSize = DEFAULT_STREAM_BUFFER_SIZE;
this.socketReader = new Reader(eventloop);
this.socketWriter = new Writer(eventloop);
}
/**
* Is called after connection registration. Wires socketReader with StreamConsumer specified by,
* and socketWriter with StreamProducer, that are specified by overridden method {@code wire} of subclass.
* If StreamConsumer is null, items from socketReader are ignored. If StreamProducer is null, socketWriter
* gets EndOfStream signal.
*/
@Override
public void onRegistered() {
wire(socketReader, socketWriter);
if (socketReader.getDownstream() == null)
socketReader.streamTo(new StreamConsumers.Closing(eventloop));
if (socketWriter.getUpstream() == null)
new StreamProducers.EndOfStream(eventloop).streamTo(socketWriter);
}
/**
* Method should wire socketReader with appropriate {@link StreamConsumer} and socketWriter with appropriate
* {@link StreamProducer}.
* However one of them could be null. If both {@link StreamConsumer} and {@link StreamProducer} are null
* socket connection just won't do any work.
*
* @param socketReader producer that reads ByteBufs from socket and send them
* to StreamConsumer specified in overridden method in subclass
* @param socketWriter consumer that receive ByteBufs from StreamProducer specified in overridden method
* in subclass and writes them to socket
*/
protected abstract void wire(StreamProducer socketReader, StreamConsumer socketWriter);
@Override
protected void onReadEndOfStream() {
logger.trace("onReadEndOfStream for {}", this);
socketReader.sendEndOfStream();
}
private void closeIfDone() {
if (!isRegistered())
return;
if (socketReader.getStatus() >= StreamProducer.CLOSED && socketWriter.getUpstreamStatus() >= StreamProducer.CLOSED) {
logger.trace("done, closing {}", this);
close();
return;
}
if (socketReader.getStatus() >= StreamProducer.CLOSED) {
try {
channel.shutdownInput();
} catch (IOException e) {
logger.error("shutdownInput error {} for {}", e.toString(), this);
}
}
if (socketWriter.getUpstreamStatus() >= StreamProducer.CLOSED) {
try {
channel.shutdownOutput();
} catch (IOException e) {
logger.error("shutdownOutput error {} for {}", e.toString(), this);
}
}
}
/**
* Sends received bytes to StreamConsumer
*
* @param buf received ByteBuffer
*/
@Override
protected void onRead(ByteBuf buf) {
assert eventloop.inEventloopThread();
try {
socketReader.send(buf);
onRead();
} catch (Exception e) {
onInternalException(e);
}
}
@Override
protected void onRead() {
}
@Override
protected void onWriteFlushed() {
if (socketWriter.getUpstreamStatus() == StreamProducer.END_OF_STREAM) {
socketWriter.closeUpstream();
closeIfDone();
} else {
socketWriter.resumeUpstream();
}
}
@Override
protected void onReadException(Exception e) {
logger.warn("onReadException", e);
socketReader.closeWithError(e);
socketReader.sendError(e);
}
@Override
protected void onWriteException(Exception e) {
logger.warn("onWriteException", e);
socketWriter.closeUpstreamWithError(e);
closeIfDone();
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return name != null ? name : super.toString();
}
}