package io.muoncore.channel.impl; import io.muoncore.channel.Channel; import io.muoncore.channel.ChannelConnection; import io.muoncore.channel.support.Scheduler; import io.muoncore.exception.MuonException; import io.muoncore.message.MuonInboundMessage; import io.muoncore.message.MuonMessageBuilder; import io.muoncore.message.MuonOutboundMessage; import reactor.core.Dispatcher; import java.util.concurrent.TimeUnit; public class TimeoutChannel implements Channel<MuonOutboundMessage, MuonInboundMessage> { public final static String TIMEOUT_STEP = "TIMEOUT"; private ChannelConnection<MuonOutboundMessage, MuonInboundMessage> left; private ChannelConnection<MuonInboundMessage, MuonOutboundMessage> right; private ChannelConnection.ChannelFunction<MuonOutboundMessage> leftFunction; private ChannelConnection.ChannelFunction<MuonInboundMessage> rightFunction; private final long timeout; private final Scheduler scheduler; private Scheduler.TimerControl timerControl; public TimeoutChannel(Dispatcher dispatcher, Scheduler scheduler, long timeout) { this.timeout = timeout; this.scheduler = scheduler; String leftname = "left"; String rightname = "right"; left = new ChannelConnection<MuonOutboundMessage, MuonInboundMessage>() { @Override public void receive(ChannelFunction<MuonInboundMessage> function) { rightFunction = function; } @Override public void send(MuonOutboundMessage message) { resetTimeout(); if (leftFunction == null) { throw new MuonException("Other side of the channel [" + rightname + "] is not connected to receive data"); } dispatcher.dispatch(message, msg -> { if (StandardAsyncChannel.echoOut) System.out.println("WiretapChannel[" + leftname + " >>>>> " + rightname + "]: Sending " + msg + " to " + leftFunction); leftFunction.apply(message); } , Throwable::printStackTrace); } @Override public void shutdown() { if (timerControl != null) timerControl.cancel(); leftFunction.apply(null); } }; right = new ChannelConnection<MuonInboundMessage, MuonOutboundMessage>() { @Override public void receive(ChannelFunction<MuonOutboundMessage> function) { leftFunction = function; } @Override public void send(MuonInboundMessage message) { if (rightFunction == null) { throw new MuonException("Other side of the channel [" + rightname + "] is not connected to receive data"); } dispatcher.dispatch(message, msg -> { if (StandardAsyncChannel.echoOut) System.out.println("WiretapChannel[" + leftname + " <<<< " + rightname + "]: " + msg + " to " + rightFunction); rightFunction.apply(message); }, Throwable::printStackTrace); } @Override public void shutdown() { timerControl.cancel(); rightFunction.apply(null); } }; } @Override public ChannelConnection<MuonInboundMessage, MuonOutboundMessage> right() { return right; } @Override public ChannelConnection<MuonOutboundMessage, MuonInboundMessage> left() { return left; } private void resetTimeout() { if (timerControl != null) { timerControl.cancel(); } timerControl = scheduler.executeIn(timeout, TimeUnit.MILLISECONDS, () -> { rightFunction.apply( MuonMessageBuilder.fromService("local") .protocol("timeout") .step(TIMEOUT_STEP).buildInbound()); }); } }