package io.muoncore.protocol.reactivestream.client;
import io.muoncore.Discovery;
import io.muoncore.channel.ChannelConnection;
import io.muoncore.codec.Codecs;
import io.muoncore.config.AutoConfiguration;
import io.muoncore.exception.MuonEncodingException;
import io.muoncore.exception.MuonException;
import io.muoncore.message.MuonInboundMessage;
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.protocol.reactivestream.server.ReactiveStreamServerStack;
import io.muoncore.transport.TransportEvents;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.net.URI;
import java.net.URLDecoder;
import java.util.LinkedHashMap;
import java.util.Map;
public class ReactiveStreamClientProtocol {
private ChannelConnection<MuonOutboundMessage, MuonInboundMessage> transportConnection;
private Subscriber<StreamData> subscriber;
private URI uri;
private AutoConfiguration configuration;
private Codecs codecs;
private Discovery discovery;
public ReactiveStreamClientProtocol(URI uri,
ChannelConnection<MuonOutboundMessage, MuonInboundMessage> transportConnection,
Subscriber<StreamData> subscriber,
Codecs codecs,
AutoConfiguration configuration,
Discovery discovery) {
this.uri = uri;
this.transportConnection = transportConnection;
this.subscriber = subscriber;
this.codecs = codecs;
this.configuration = configuration;
this.discovery = discovery;
}
public void start() {
transportConnection.receive(this::handleMessage);
ReactiveStreamSubscriptionRequest request = new ReactiveStreamSubscriptionRequest(uri.getPath());
splitQuery(uri).forEach(request::arg);
sendSubscribe(request);
}
public static Map<String, String> splitQuery(URI url) {
Map<String, String> query_pairs = new LinkedHashMap<>();
String query = url.getQuery();
if (query == null || query.trim().length() == 0) return query_pairs;
String[] pairs = query.split("&");
for (String pair : pairs) {
int idx = pair.indexOf("=");
try {
query_pairs.put(URLDecoder.decode(pair.substring(0, idx), "UTF-8"), URLDecoder.decode(pair.substring(idx + 1), "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new MuonEncodingException("Unable to decode data " + e.getMessage(), e);
}
}
return query_pairs;
}
private void handleMessage(MuonInboundMessage msg) {
if (msg == null) {
subscriber.onComplete();
return;
}
switch(msg.getStep()) {
case ProtocolMessages.ACK:
subscriber.onSubscribe(new Subscription() {
@Override
public void request(long n) {
sendRequest(n);
}
@Override
public void cancel() {
sendCancel();
}
});
break;
case ProtocolMessages.NACK:
subscriber.onError(new MuonException("Stream does not exist"));
break;
case ProtocolMessages.DATA:
StreamData data = new StreamData(msg);
data.setCodecs(codecs);
subscriber.onNext(data);
break;
case ProtocolMessages.ERROR:
subscriber.onError(new MuonException());
break;
case ProtocolMessages.COMPLETE:
subscriber.onComplete();
break;
case TransportEvents.SERVICE_NOT_FOUND:
subscriber.onError(new MuonException("Service " + msg.getSourceServiceName() + " does not exist"));
break;
case TransportEvents.CONNECTION_FAILURE:
subscriber.onError(new MuonException("Connection lost to remote service, the channel has shut down due to a transport failure"));
break;
default:
subscriber.onError(new MuonException("An unknown step in the protocol [" + msg.getStep() + "] has been sent, this is a bug"));
break;
}
}
private void sendRequest(long n) {
RequestMessage requestMessage = new RequestMessage(n);
Codecs.EncodingResult result = codecs.encode(requestMessage, discovery.getCodecsForService(uri.getHost()));
transportConnection.send(MuonMessageBuilder
.fromService(configuration.getServiceName())
.step(ProtocolMessages.REQUEST)
.protocol(ReactiveStreamServerStack.REACTIVE_STREAM_PROTOCOL)
.toService(uri.getHost())
.payload(result.getPayload())
.contentType(result.getContentType())
.build()
);
}
private void sendCancel() {
Codecs.EncodingResult result = codecs.encode(new Object(), discovery.getCodecsForService(uri.getHost()));
transportConnection.send(MuonMessageBuilder
.fromService(configuration.getServiceName())
.step(ProtocolMessages.CANCEL)
.protocol(ReactiveStreamServerStack.REACTIVE_STREAM_PROTOCOL)
.toService(uri.getHost())
.payload(result.getPayload())
.contentType(result.getContentType())
.build()
);
}
private void sendSubscribe(ReactiveStreamSubscriptionRequest subscriptionRequest) {
Codecs.EncodingResult result = codecs.encode(subscriptionRequest, discovery.getCodecsForService(uri.getHost()));
transportConnection.send(MuonMessageBuilder
.fromService(configuration.getServiceName())
.step(ProtocolMessages.SUBSCRIBE)
.protocol(ReactiveStreamServerStack.REACTIVE_STREAM_PROTOCOL)
.toService(uri.getHost())
.payload(result.getPayload())
.contentType(result.getContentType())
.build()
);
}
}