package io.muoncore.protocol.event.client;
import io.muoncore.Discovery;
import io.muoncore.Muon;
import io.muoncore.ServiceDescriptor;
import io.muoncore.api.ChannelFutureAdapter;
import io.muoncore.api.ImmediateReturnFuture;
import io.muoncore.api.MuonFuture;
import io.muoncore.channel.Channel;
import io.muoncore.channel.ChannelConnection;
import io.muoncore.channel.Channels;
import io.muoncore.codec.Codecs;
import io.muoncore.config.AutoConfiguration;
import io.muoncore.exception.MuonException;
import io.muoncore.message.MuonInboundMessage;
import io.muoncore.message.MuonOutboundMessage;
import io.muoncore.protocol.event.ClientEvent;
import io.muoncore.protocol.event.Event;
import io.muoncore.protocol.reactivestream.client.ReactiveStreamClientProtocolStack;
import io.muoncore.protocol.reactivestream.client.StreamData;
import io.muoncore.transport.client.TransportClient;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
public class DefaultEventClient implements EventClient {
private AutoConfiguration config;
private Discovery discovery;
private Codecs codecs;
private TransportClient transportClient;
private ReactiveStreamClientProtocolStack reactiveStreamClientProtocolStack;
private ChannelConnection<MuonOutboundMessage, MuonInboundMessage> eventChannelConnection;
private Muon muon;
public DefaultEventClient(Muon muon) {
this.muon = muon;
this.config = muon.getConfiguration();
this.discovery = muon.getDiscovery();
this.codecs = muon.getCodecs();
this.transportClient = muon.getTransportClient();
this.reactiveStreamClientProtocolStack = muon;
}
public MuonFuture<EventResult> eventAsync(ClientEvent event) {
Channel<ClientEvent, EventResult> api2eventproto = Channels.channel("eventapi", "eventproto");
ChannelFutureAdapter<EventResult, ClientEvent> adapter =
new ChannelFutureAdapter<>(api2eventproto.left());
Channel<MuonOutboundMessage, MuonInboundMessage> timeoutChannel = Channels.timeout(muon.getScheduler(), 1000);
new EventClientProtocol<>(
config,
discovery,
codecs,
api2eventproto.right(),
timeoutChannel.left());
Channels.connect(timeoutChannel.right(), transportClient.openClientChannel());
return adapter.request(event);
}
@Override
public EventResult event(ClientEvent event) {
try {
Channel<ClientEvent, EventResult> api2eventproto = Channels.channel("eventapi", "eventproto");
ChannelFutureAdapter<EventResult, ClientEvent> adapter =
new ChannelFutureAdapter<>(api2eventproto.left());
Channel<MuonOutboundMessage, MuonInboundMessage> timeoutChannel = Channels.timeout(muon.getScheduler(), 1000);
new EventClientProtocol<>(
config,
discovery,
codecs,
api2eventproto.right(),
timeoutChannel.left());
Channels.connect(timeoutChannel.right(), transportClient.openClientChannel());
return adapter.request(event).get();
} catch (InterruptedException | ExecutionException e) {
throw new MuonException(e);
}
}
@Override
public <X> MuonFuture<EventReplayControl> replay(String streamName, EventReplayMode mode, Subscriber<Event> subscriber) {
return replay(streamName, mode, Collections.emptyMap(), subscriber);
}
@Override
public <X> MuonFuture<EventReplayControl> replay(String streamName, EventReplayMode mode, Map<String, Object> args, Subscriber<Event> subscriber) {
String replayType;
switch (mode) {
case LIVE_ONLY:
replayType = "hot";
break;
case REPLAY_ONLY:
replayType = "cold";
break;
case REPLAY_THEN_LIVE:
default:
replayType = "hot-cold";
}
//TODO, manage a params object and turn it into a querystring.
Map<String, Object> params = new HashMap<>();
params.put("stream-type", replayType);
params.put("stream-name", streamName);
params.putAll(args);
String query = params.entrySet()
.stream()
.map(entry -> entry.getKey() + "=" + entry.getValue())
.collect(Collectors.joining("&"));
Optional<ServiceDescriptor> eventStore = discovery.findService(svc -> svc.getTags().contains("eventstore"));
if (eventStore.isPresent()) {
String eventStoreName = eventStore.get().getIdentifier();
try {
reactiveStreamClientProtocolStack.subscribe(new URI("stream://" + eventStoreName + "/stream?" + query), new Subscriber<StreamData>() {
@Override
public void onSubscribe(Subscription s) {
subscriber.onSubscribe(s);
}
@Override
public void onNext(StreamData data) {
Event event = data.getPayload(Event.class);
event.setCodecs(codecs);
subscriber.onNext(event);
}
@Override
public void onError(Throwable t) {
subscriber.onError(t);
}
@Override
public void onComplete() {
subscriber.onComplete();
}
});
} catch (URISyntaxException e) {
throw new MuonException("The name provided [" + eventStoreName + "] is invalid", e);
}
} else {
throw new MuonException("There is no event store present in the distributed system, is Photon running?");
}
return null;
}
@Override
public <X> MuonFuture<EventProjectionControl<X>> getProjection(String name, Class<X> type) {
return new ImmediateReturnFuture<>(() -> {
try {
Map<String, Object> data = new HashMap<>();
data.put("projection-name", name);
return muon.request("request://photon/projection", data).get().getPayload(type);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return null;
}
});
}
@SuppressWarnings("unchecked")
@Override
public MuonFuture<List<EventProjectionDescriptor>> getProjectionList() {
try {
List<String> projectionKeys = (List<String>) muon.request("request://photon/projection-keys").get().getPayload(Map.class).get("projection-keys");
List<EventProjectionDescriptor> projections = projectionKeys.stream().map(EventProjectionDescriptor::new).collect(Collectors.toList());
return new ImmediateReturnFuture<>(projections);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return null;
}
}
}