package io.muoncore.transport.client;
import io.muoncore.Discovery;
import io.muoncore.ServiceDescriptor;
import io.muoncore.channel.ChannelConnection;
import io.muoncore.exception.NoSuchServiceException;
import io.muoncore.message.MuonInboundMessage;
import io.muoncore.message.MuonMessage;
import io.muoncore.message.MuonOutboundMessage;
import io.muoncore.transport.sharedsocket.client.SharedSocketRouter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.Dispatcher;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
class MultiTransportClientChannelConnection implements ChannelConnection<MuonOutboundMessage, MuonInboundMessage> {
private ChannelFunction<MuonInboundMessage> inbound;
private Dispatcher dispatcher;
private SharedSocketRouter router = null;
private Discovery discovery;
private TransportConnectionProvider transportConnectionProvider;
private Map<String, ChannelConnection<MuonOutboundMessage, MuonInboundMessage>> channelConnectionMap = new HashMap<>();
private Logger LOG = LoggerFactory.getLogger(MultiTransportClientChannelConnection.class.getCanonicalName());
public MultiTransportClientChannelConnection(Dispatcher dispatcher, SharedSocketRouter router, Discovery discovery,
TransportConnectionProvider transportConnectionProvider) {
this.dispatcher = dispatcher;
this.router = router;
this.transportConnectionProvider = transportConnectionProvider;
this.discovery = discovery;
}
@Override
public void receive(ChannelFunction<MuonInboundMessage> function) {
inbound = arg -> {
dispatcher.tryDispatch(arg, function::apply, Throwable::printStackTrace);
};
}
@Override
public synchronized void send(MuonOutboundMessage message) {
if (inbound == null) {
throw new IllegalStateException("Transport connection is not in a complete state can cannot send data. The receive function has not been set");
}
if (message == null) {
shutdown();
} else {
dispatcher.dispatch(message, msg -> {
ChannelConnection<MuonOutboundMessage, MuonInboundMessage> connection = channelConnectionMap.get(
key(message)
);
try {
if (connection == null) {
if (useSharedChannels(message)) {
connection = connectSharedChannel(message);
} else {
connection = transportConnectionProvider.connectChannel(
message.getTargetServiceName(), message.getProtocol(), inbound);
}
if (connection == null) {
LOG.warn("Cannot open channel to service " + message.getTargetServiceName() + ", no transport accepted the message");
inbound.apply(MuonInboundMessage.serviceNotFound(msg));
return;
} else {
channelConnectionMap.put(key(message), connection);
}
}
connection.send(message);
if (message.getChannelOperation() == MuonMessage.ChannelOperation.closed) {
inbound = null;
}
} catch (NoSuchServiceException ex) {
inbound.apply(MuonInboundMessage.serviceNotFound(msg));
}
}, Throwable::printStackTrace);
}
}
private boolean useSharedChannels(MuonOutboundMessage message) {
Optional<ServiceDescriptor> service = discovery.findService(
serviceDescriptor -> serviceDescriptor.getIdentifier().equals(message.getTargetServiceName()));
// if (service.isPresent()) {
// return service.get().getCapabilities().contains(SharedSocketRouter.PROTOCOL);
// }
return true;
}
@Override
public void shutdown() {
dispatcher.dispatch(null, msg -> {
channelConnectionMap.forEach((s, transportOutboundMessageTransportInboundMessageChannelConnection) -> {
transportOutboundMessageTransportInboundMessageChannelConnection.shutdown();
});
}, Throwable::printStackTrace);
}
private ChannelConnection<MuonOutboundMessage, MuonInboundMessage> connectSharedChannel(MuonOutboundMessage message) {
ChannelConnection<MuonOutboundMessage, MuonInboundMessage> connection = router.openClientChannel(message.getTargetServiceName());
connection.receive(inbound);
return connection;
}
private static String key(MuonOutboundMessage key) {
return key.getTargetServiceName() + "_" + key.getProtocol();
}
}