package io.muoncore.protocol.reactivestream.server;
import io.muoncore.Discovery;
import io.muoncore.channel.ChannelConnection;
import io.muoncore.codec.Codecs;
import io.muoncore.config.AutoConfiguration;
import io.muoncore.exception.MuonException;
import io.muoncore.message.MuonInboundMessage;
import io.muoncore.message.MuonMessage;
import io.muoncore.message.MuonMessageBuilder;
import io.muoncore.message.MuonOutboundMessage;
import io.muoncore.protocol.reactivestream.ProtocolMessages;
import io.muoncore.protocol.reactivestream.messages.ReactiveStreamSubscriptionRequest;
import io.muoncore.protocol.reactivestream.messages.RequestMessage;
import io.muoncore.transport.TransportEvents;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import java.util.*;
public class ReactiveStreamServerChannel implements ChannelConnection<MuonInboundMessage, MuonOutboundMessage> {
private PublisherLookup publisherLookup;
private Subscription subscription;
private String subscribingServiceName;
private ChannelFunction<MuonOutboundMessage> function;
private Codecs codecs;
private AutoConfiguration configuration;
private Discovery discovery;
private List<String> acceptedContentTypes;
public ReactiveStreamServerChannel(
PublisherLookup publisherLookup,
Codecs codecs,
AutoConfiguration configuration, Discovery discovery) {
this.publisherLookup = publisherLookup;
this.codecs = codecs;
this.configuration = configuration;
this.discovery = discovery;
}
@Override
public void receive(ChannelFunction<MuonOutboundMessage> function) {
this.function = function;
}
@Override
public void send(MuonInboundMessage message) {
if (message == null) {
handleError();
return;
}
switch(message.getStep()) {
case ProtocolMessages.SUBSCRIBE:
handleSubscribe(message);
break;
case ProtocolMessages.REQUEST:
handleRequest(message);
break;
case ProtocolMessages.CANCEL:
handleCancel(message);
break;
case TransportEvents.CONNECTION_FAILURE:
handleError();
break;
case "ChannelShutdown":
handleError();
break;
default:
sendProtocolFailureException(message);
}
}
private void sendProtocolFailureException(MuonInboundMessage msg) {
System.out.println("Don't understand " + msg.getStep());
Map<String, String> meta = new HashMap<>();
meta.put("SourceMessage", msg.getId());
meta.put("SourceType", msg.getSourceServiceName());
Codecs.EncodingResult result = codecs.encode(meta,
discovery.getCodecsForService(msg.getSourceServiceName()));
function.apply(MuonMessageBuilder
.fromService(configuration.getServiceName())
.step(ProtocolMessages.PROTOCOL_FAILURE)
.protocol(ReactiveStreamServerStack.REACTIVE_STREAM_PROTOCOL)
.toService(subscribingServiceName)
.payload(result.getPayload())
.contentType(result.getContentType())
.status(MuonMessage.Status.error)
.operation(MuonMessage.ChannelOperation.closed)
.build()
);
}
private void sendNack(MuonInboundMessage msg) {
Map<String, String> meta = new HashMap<>();
Codecs.EncodingResult result = codecs.encode(meta,
discovery.getCodecsForService(msg.getSourceServiceName()));
function.apply(MuonMessageBuilder
.fromService(configuration.getServiceName())
.step(ProtocolMessages.NACK)
.protocol(ReactiveStreamServerStack.REACTIVE_STREAM_PROTOCOL)
.toService(subscribingServiceName)
.payload(result.getPayload())
.contentType(result.getContentType())
.status(MuonMessage.Status.error)
.build()
);
function.apply(MuonMessageBuilder
.fromService(configuration.getServiceName())
.step(ProtocolMessages.NACK)
.protocol(ReactiveStreamServerStack.REACTIVE_STREAM_PROTOCOL)
.toService(subscribingServiceName)
.payload(result.getPayload())
.contentType(result.getContentType())
.operation(MuonMessage.ChannelOperation.closed)
.build()
);
}
private void sendAck(MuonInboundMessage msg) {
Map<String, String> meta = new HashMap<>();
Codecs.EncodingResult result = codecs.encode(meta,
discovery.getCodecsForService(msg.getSourceServiceName()));
function.apply(MuonMessageBuilder
.fromService(configuration.getServiceName())
.step(ProtocolMessages.ACK)
.protocol(ReactiveStreamServerStack.REACTIVE_STREAM_PROTOCOL)
.toService(subscribingServiceName)
.payload(result.getPayload())
.contentType(result.getContentType())
.build()
);
}
@SuppressWarnings("unchecked")
private void handleSubscribe(MuonInboundMessage msg) {
ReactiveStreamSubscriptionRequest subscriptionMessage = codecs.decode(msg.getPayload(), msg.getContentType(), ReactiveStreamSubscriptionRequest.class);
Optional<PublisherLookup.PublisherRecord> pub = publisherLookup.lookupPublisher(subscriptionMessage.getStreamName());
if (!pub.isPresent()) {
sendNack(msg);
} else {
acceptedContentTypes = Arrays.asList(discovery.getCodecsForService(msg.getSourceServiceName()));
subscribingServiceName = msg.getSourceServiceName();
pub.get().getPublisher().generatePublisher(subscriptionMessage).subscribe(new Subscriber() {
@Override
public void onSubscribe(Subscription s) {
subscription = s;
sendAck(msg);
}
@Override
public void onNext(Object o) {
Codecs.EncodingResult result = codecs.encode(o, acceptedContentTypes.toArray(new String[0]));
function.apply(MuonMessageBuilder
.fromService(configuration.getServiceName())
.step(ProtocolMessages.DATA)
.protocol(ReactiveStreamServerStack.REACTIVE_STREAM_PROTOCOL)
.toService(subscribingServiceName)
.payload(result.getPayload())
.contentType(result.getContentType())
.build()
);
}
@Override
public void onError(Throwable t) {
Map<String, String> meta = new HashMap<>();
meta.put("error", t.getMessage());
Codecs.EncodingResult result = codecs.encode(meta,
discovery.getCodecsForService(msg.getSourceServiceName()));
function.apply(MuonMessageBuilder
.fromService(configuration.getServiceName())
.step(ProtocolMessages.ERROR)
.protocol(ReactiveStreamServerStack.REACTIVE_STREAM_PROTOCOL)
.toService(subscribingServiceName)
.payload(result.getPayload())
.contentType(result.getContentType())
.operation(MuonMessage.ChannelOperation.closed)
.build()
);
}
@Override
public void onComplete() {
sendComplete();
}
});
}
}
private void sendComplete() {
Map<String, String> meta = new HashMap<>();
Codecs.EncodingResult result = codecs.encode(meta,
discovery.getCodecsForService(subscribingServiceName));
function.apply(MuonMessageBuilder
.fromService(configuration.getServiceName())
.step(ProtocolMessages.COMPLETE)
.protocol(ReactiveStreamServerStack.REACTIVE_STREAM_PROTOCOL)
.toService(subscribingServiceName)
.payload(result.getPayload())
.contentType(result.getContentType())
.operation(MuonMessage.ChannelOperation.normal)
.build()
);
function.apply(MuonMessageBuilder
.fromService(configuration.getServiceName())
.step(ProtocolMessages.COMPLETE)
.protocol(ReactiveStreamServerStack.REACTIVE_STREAM_PROTOCOL)
.toService(subscribingServiceName)
.payload(result.getPayload())
.contentType(result.getContentType())
.operation(MuonMessage.ChannelOperation.closed)
.build()
);
}
@Override
public void shutdown() {
sendComplete();
}
private void handleRequest(MuonInboundMessage msg) {
if (subscription == null) {
throw new MuonException("Unable to handle, subscription is not yet set");
}
RequestMessage request = codecs.decode(msg.getPayload(), msg.getContentType(), RequestMessage.class);
subscription.request(request.getRequest());
}
private void handleCancel(MuonInboundMessage msg) {
if (subscription != null) {
subscription.cancel();
}
}
private void handleError() {
if (subscription != null) {
subscription.cancel();
}
}
}