/*
* 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.rpc.protocol;
import io.datakernel.eventloop.AsyncTcpSocket;
import io.datakernel.eventloop.Eventloop;
import io.datakernel.serializer.BufferSerializer;
import io.datakernel.stream.AbstractStreamConsumer;
import io.datakernel.stream.AbstractStreamProducer;
import io.datakernel.stream.StreamDataReceiver;
import io.datakernel.stream.StreamStatus;
import io.datakernel.stream.net.SocketStreamingConnection;
import io.datakernel.stream.processor.StreamBinaryDeserializer;
import io.datakernel.stream.processor.StreamBinarySerializer;
import io.datakernel.stream.processor.StreamLZ4Compressor;
import io.datakernel.stream.processor.StreamLZ4Decompressor;
@SuppressWarnings("unchecked")
public final class RpcStream {
public interface Listener extends StreamDataReceiver<RpcMessage> {
void onClosedWithError(Throwable exception);
void onReadEndOfStream();
}
private final Eventloop eventloop;
private Listener listener;
private final AbstractStreamProducer<RpcMessage> sender;
private final AbstractStreamConsumer<RpcMessage> receiver;
private final StreamBinarySerializer<RpcMessage> serializer;
private final StreamBinaryDeserializer<RpcMessage> deserializer;
private final boolean compression;
private final StreamLZ4Compressor compressor;
private final StreamLZ4Decompressor decompressor;
private final SocketStreamingConnection connection;
private StreamStatus producerStatus;
private StreamDataReceiver<RpcMessage> downstreamDataReceiver;
public RpcStream(Eventloop eventloop, AsyncTcpSocket asyncTcpSocket,
BufferSerializer<RpcMessage> messageSerializer,
int defaultPacketSize, int maxPacketSize,
int flushDelayMillis, boolean compression, boolean server,
StreamBinarySerializer.Inspector serializerInspector,
StreamBinaryDeserializer.Inspector deserializerInspector,
StreamLZ4Compressor.Inspector compressorInspector,
StreamLZ4Decompressor.Inspector decompressorInspector) {
this.eventloop = eventloop;
this.compression = compression;
connection = SocketStreamingConnection.createSocketStreamingConnection(eventloop, asyncTcpSocket);
if (server) {
sender = new AbstractStreamProducer<RpcMessage>(eventloop) {
@Override
protected void onDataReceiverChanged() {
RpcStream.this.downstreamDataReceiver = this.downstreamDataReceiver;
}
@Override
protected void onSuspended() {
receiver.suspend();
producerStatus = getProducerStatus();
}
@Override
protected void onResumed() {
receiver.resume();
producerStatus = getProducerStatus();
}
@Override
protected void onError(Exception e) {
RpcStream.this.listener.onClosedWithError(e);
producerStatus = getProducerStatus();
}
};
} else {
sender = new AbstractStreamProducer<RpcMessage>(eventloop) {
@Override
protected void onDataReceiverChanged() {
RpcStream.this.downstreamDataReceiver = this.downstreamDataReceiver;
}
@Override
protected void onSuspended() {
producerStatus = getProducerStatus();
}
@Override
protected void onResumed() {
producerStatus = getProducerStatus();
}
@Override
protected void onError(Exception e) {
RpcStream.this.listener.onClosedWithError(e);
producerStatus = getProducerStatus();
}
};
}
receiver = new AbstractStreamConsumer<RpcMessage>(eventloop) {
@Override
protected void onEndOfStream() {
RpcStream.this.listener.onReadEndOfStream();
}
@Override
public StreamDataReceiver<RpcMessage> getDataReceiver() {
return listener;
}
};
serializer = StreamBinarySerializer.create(eventloop, messageSerializer)
.withDefaultBufferSize(defaultPacketSize)
.withMaxMessageSize(maxPacketSize)
.withFlushDelay(flushDelayMillis)
.withSkipSerializationErrors()
.withInspector(serializerInspector);
deserializer = StreamBinaryDeserializer.create(eventloop, messageSerializer)
.withMaxMessageSize(maxPacketSize)
.withInspector(deserializerInspector);
if (compression) {
compressor = StreamLZ4Compressor.fastCompressor(eventloop).withInspector(compressorInspector);
decompressor = StreamLZ4Decompressor.create(eventloop).withInspector(decompressorInspector);
connection.receiveStreamTo(decompressor.getInput());
decompressor.getOutput().streamTo(deserializer.getInput());
serializer.getOutput().streamTo(compressor.getInput());
connection.sendStreamFrom(compressor.getOutput());
} else {
compressor = null;
decompressor = null;
connection.receiveStreamTo(deserializer.getInput());
connection.sendStreamFrom(serializer.getOutput());
}
}
public void setListener(Listener listener) {
this.listener = listener;
producerStatus = sender.getProducerStatus();
if (compression) {
connection.receiveStreamTo(decompressor.getInput());
decompressor.getOutput().streamTo(deserializer.getInput());
serializer.getOutput().streamTo(compressor.getInput());
connection.sendStreamFrom(compressor.getOutput());
} else {
connection.receiveStreamTo(deserializer.getInput());
connection.sendStreamFrom(serializer.getOutput());
}
deserializer.getOutput().streamTo(receiver);
sender.streamTo(serializer.getInput());
}
public void sendMessage(RpcMessage message) {
sendRpcMessage(message);
}
public void sendCloseMessage() {
sendRpcMessage(RpcMessage.of(-1, RpcControlMessage.CLOSE));
}
private void sendRpcMessage(RpcMessage message) {
if (producerStatus == StreamStatus.CLOSED_WITH_ERROR) {
return;
}
assert producerStatus != StreamStatus.END_OF_STREAM;
downstreamDataReceiver.onData(message);
}
public boolean isOverloaded() {
return producerStatus != StreamStatus.READY;
}
public AsyncTcpSocket.EventHandler getSocketEventHandler() {
return connection;
}
public void sendEndOfStream() {
sender.sendEndOfStream();
}
}