/*
* 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.async.CompletionCallback;
import io.datakernel.bytebuf.ByteBuf;
import io.datakernel.eventloop.AsyncTcpSocket;
import io.datakernel.eventloop.Eventloop;
import io.datakernel.stream.StreamConsumer;
import io.datakernel.stream.StreamConsumers;
import io.datakernel.stream.StreamProducer;
import io.datakernel.stream.StreamProducers;
/**
* 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 final class SocketStreamingConnection implements AsyncTcpSocket.EventHandler, SocketStreaming {
private final Eventloop eventloop;
private final AsyncTcpSocket asyncTcpSocket;
private final SocketStreamProducer socketReader;
private final SocketStreamConsumer socketWriter;
// region creators
private SocketStreamingConnection(Eventloop eventloop, final AsyncTcpSocket asyncTcpSocket) {
this.eventloop = eventloop;
this.asyncTcpSocket = asyncTcpSocket;
this.socketWriter = SocketStreamConsumer.create(eventloop, asyncTcpSocket, new CompletionCallback() {
@Override
public void onException(Exception e) {
socketReader.closeWithError(e);
asyncTcpSocket.close();
}
@Override
public void onComplete() {
}
});
this.socketReader = SocketStreamProducer.create(eventloop, asyncTcpSocket, new CompletionCallback() {
@Override
public void onException(Exception e) {
socketWriter.closeWithError(e);
asyncTcpSocket.close();
}
@Override
public void onComplete() {
}
});
socketReader.streamTo(StreamConsumers.<ByteBuf>idle(eventloop));
new StreamProducers.EndOfStream<ByteBuf>(eventloop).streamTo(socketWriter);
}
public static SocketStreamingConnection createSocketStreamingConnection(Eventloop eventloop,
final AsyncTcpSocket asyncTcpSocket) {
return new SocketStreamingConnection(eventloop, asyncTcpSocket);
}
// endregion
@Override
public void sendStreamFrom(StreamProducer<ByteBuf> producer) {
producer.streamTo(socketWriter);
}
@Override
public void receiveStreamTo(StreamConsumer<ByteBuf> consumer) {
socketReader.streamTo(consumer);
}
/**
* 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() {
}
/**
* Sends received bytes to StreamConsumer
*/
@Override
public void onRead(ByteBuf buf) {
assert eventloop.inEventloopThread();
socketReader.onRead(buf);
closeIfDone();
}
@Override
public void onReadEndOfStream() {
socketReader.onReadEndOfStream();
closeIfDone();
}
@Override
public void onWrite() {
socketWriter.onWrite();
closeIfDone();
}
private void closeIfDone() {
if (socketReader.isClosed() && socketWriter.isClosed()) {
asyncTcpSocket.close();
}
}
@Override
public void onClosedWithError(Exception e) {
socketReader.closeWithError(e);
socketWriter.closeWithError(e);
}
@Override
public String toString() {
return "{asyncTcpSocket=" + asyncTcpSocket + '}';
}
}