1717import reactor .rabbitmq .BindingSpecification ;
1818import reactor .rabbitmq .ExchangeSpecification ;
1919
20+ import java .time .Duration ;
21+ import java .time .Instant ;
2022import java .util .HashMap ;
2123import java .util .Optional ;
2224import java .util .function .Function ;
25+ import java .util .logging .Level ;
2326
2427import static java .util .Optional .ofNullable ;
2528import static org .reactivecommons .async .commons .Headers .*;
@@ -37,12 +40,14 @@ public class ApplicationQueryListener extends GenericMessageListener {
3740 private final boolean withDLQRetry ;
3841 private final int retryDelay ;
3942 private final Optional <Integer > maxLengthBytes ;
43+ private final boolean discardTimeoutQueries ;
4044
4145
4246 public ApplicationQueryListener (ReactiveMessageListener listener , String queueName , HandlerResolver resolver ,
4347 ReactiveMessageSender sender , String directExchange , MessageConverter converter ,
4448 String replyExchange , boolean withDLQRetry , long maxRetries , int retryDelay ,
45- Optional <Integer > maxLengthBytes , DiscardNotifier discardNotifier , CustomReporter errorReporter ) {
49+ Optional <Integer > maxLengthBytes , boolean discardTimeoutQueries ,
50+ DiscardNotifier discardNotifier , CustomReporter errorReporter ) {
4651 super (queueName , listener , withDLQRetry , maxRetries , discardNotifier , "query" , errorReporter );
4752 this .retryDelay = retryDelay ;
4853 this .withDLQRetry = withDLQRetry ;
@@ -52,6 +57,7 @@ public ApplicationQueryListener(ReactiveMessageListener listener, String queueNa
5257 this .replyExchange = replyExchange ;
5358 this .directExchange = directExchange ;
5459 this .maxLengthBytes = maxLengthBytes ;
60+ this .discardTimeoutQueries = discardTimeoutQueries ;
5561 }
5662
5763 @ Override
@@ -69,11 +75,11 @@ protected Function<Message, Mono<Object>> rawMessageHandler(String executorPath)
6975 protected Mono <Void > setUpBindings (TopologyCreator creator ) {
7076 final Mono <AMQP .Exchange .DeclareOk > declareExchange = creator .declare (ExchangeSpecification .exchange (directExchange ).durable (true ).type ("direct" ));
7177 if (withDLQRetry ) {
72- final Mono <AMQP .Exchange .DeclareOk > declareExchangeDLQ = creator .declare (ExchangeSpecification .exchange (directExchange + ".DLQ" ).durable (true ).type ("direct" ));
73- final Mono <AMQP .Queue .DeclareOk > declareQueue = creator .declareQueue (queueName , directExchange + ".DLQ" , maxLengthBytes );
78+ final Mono <AMQP .Exchange .DeclareOk > declareExchangeDLQ = creator .declare (ExchangeSpecification .exchange (directExchange + ".DLQ" ).durable (true ).type ("direct" ));
79+ final Mono <AMQP .Queue .DeclareOk > declareQueue = creator .declareQueue (queueName , directExchange + ".DLQ" , maxLengthBytes );
7480 final Mono <AMQP .Queue .DeclareOk > declareDLQ = creator .declareDLQ (queueName , directExchange , retryDelay , maxLengthBytes );
7581 final Mono <AMQP .Queue .BindOk > binding = creator .bind (BindingSpecification .binding (directExchange , queueName , queueName ));
76- final Mono <AMQP .Queue .BindOk > bindingDLQ = creator .bind (BindingSpecification .binding (directExchange + ".DLQ" , queueName , queueName + ".DLQ" ));
82+ final Mono <AMQP .Queue .BindOk > bindingDLQ = creator .bind (BindingSpecification .binding (directExchange + ".DLQ" , queueName , queueName + ".DLQ" ));
7783 return declareExchange .then (declareExchangeDLQ ).then (declareQueue ).then (declareDLQ ).then (binding ).then (bindingDLQ ).then ();
7884 } else {
7985 final Mono <AMQP .Queue .DeclareOk > declareQueue = creator .declareQueue (queueName , maxLengthBytes );
@@ -82,6 +88,45 @@ protected Mono<Void> setUpBindings(TopologyCreator creator) {
8288 }
8389 }
8490
91+ @ Override
92+ protected Mono <AcknowledgableDelivery > handle (AcknowledgableDelivery msj , Instant initTime ) {
93+ AMQP .BasicProperties messageProperties = msj .getProperties ();
94+
95+ boolean messageDoesNotContainTimeoutMetadata = messageProperties .getTimestamp () == null ||
96+ !messageProperties .getHeaders ().containsKey (REPLY_TIMEOUT_MILLIS );
97+
98+ if (messageDoesNotContainTimeoutMetadata || !discardTimeoutQueries ) {
99+ return super .handle (msj , initTime );
100+ }
101+
102+ return handleWithTimeout (msj , initTime , messageProperties );
103+ }
104+
105+ private Mono <AcknowledgableDelivery > handleWithTimeout (AcknowledgableDelivery msj ,
106+ Instant initTime ,
107+ AMQP .BasicProperties messageProperties ) {
108+ long messageTimestamp = msj .getProperties ().getTimestamp ().getTime ();
109+ long replyTimeoutMillis = (int ) messageProperties .getHeaders ().get (REPLY_TIMEOUT_MILLIS );
110+ long millisUntilTimeout = (messageTimestamp + replyTimeoutMillis ) - currentTimestamp ().toEpochMilli ();
111+ String executorPath = getExecutorPath (msj );
112+
113+ if (millisUntilTimeout > 0 ) {
114+ return super .handle (msj , initTime )
115+ .timeout (Duration .ofMillis (millisUntilTimeout ), buildTimeOutFallback (executorPath ));
116+ }
117+
118+ return buildTimeOutFallback (executorPath );
119+ }
120+
121+ private Instant currentTimestamp () {
122+ return Instant .now ();
123+ }
124+
125+ private Mono <AcknowledgableDelivery > buildTimeOutFallback (String executorPath ) {
126+ return Mono .fromRunnable (() -> log .log (Level .WARNING , String .format ("query with path %s discarded by timeout" ,
127+ executorPath )));
128+ }
129+
85130 @ Override
86131 protected String getExecutorPath (AcknowledgableDelivery msj ) {
87132 return msj .getProperties ().getHeaders ().get (SERVED_QUERY_ID ).toString ();
0 commit comments