|
19 | 19 | import java.time.Duration; |
20 | 20 | import java.util.Arrays; |
21 | 21 | import java.util.Collection; |
22 | | -import java.util.Map; |
23 | 22 |
|
24 | | -import com.rabbitmq.client.amqp.Consumer; |
25 | 23 | import com.rabbitmq.client.amqp.Resource; |
26 | 24 | import org.aopalliance.aop.Advice; |
27 | 25 | import org.jspecify.annotations.Nullable; |
28 | 26 |
|
29 | 27 | import org.springframework.amqp.core.Address; |
30 | | -import org.springframework.amqp.core.AmqpAcknowledgment; |
31 | 28 | import org.springframework.amqp.core.MessagePostProcessor; |
32 | 29 | import org.springframework.amqp.core.MessageProperties; |
33 | 30 | import org.springframework.amqp.rabbit.listener.adapter.ReplyPostProcessor; |
34 | | -import org.springframework.amqp.rabbit.support.ListenerExecutionFailedException; |
35 | 31 | import org.springframework.amqp.rabbitmq.client.AmqpConnectionFactory; |
36 | 32 | import org.springframework.amqp.rabbitmq.client.RabbitAmqpTemplate; |
37 | | -import org.springframework.amqp.rabbitmq.client.RabbitAmqpUtils; |
38 | 33 | import org.springframework.amqp.rabbitmq.client.listener.RabbitAmqpListenerContainer; |
39 | | -import org.springframework.amqp.rabbitmq.client.listener.RabbitAmqpMessageListener; |
40 | 34 | import org.springframework.amqp.support.converter.MessageConverter; |
41 | 35 | import org.springframework.amqp.support.converter.SimpleMessageConverter; |
42 | 36 | import org.springframework.amqp.support.postprocessor.MessagePostProcessorUtils; |
43 | | -import org.springframework.integration.IntegrationMessageHeaderAccessor; |
44 | | -import org.springframework.integration.acks.AcknowledgmentCallback; |
45 | 37 | import org.springframework.integration.amqp.support.AmqpHeaderMapper; |
46 | 38 | import org.springframework.integration.amqp.support.DefaultAmqpHeaderMapper; |
47 | 39 | import org.springframework.integration.core.Pausable; |
@@ -185,7 +177,10 @@ public String getComponentType() { |
185 | 177 | protected void onInit() { |
186 | 178 | super.onInit(); |
187 | 179 | this.listenerContainer.setBeanName(getComponentName() + ".listenerContainer"); |
188 | | - this.listenerContainer.setupMessageListener(new IntegrationRabbitAmqpMessageListener()); |
| 180 | + IntegrationRabbitAmqpMessageListener messageListener = |
| 181 | + new IntegrationRabbitAmqpMessageListener(this, this::processRequest, this.headerMapper, |
| 182 | + this.messageConverter, this.afterReceivePostProcessors); |
| 183 | + this.listenerContainer.setupMessageListener(messageListener); |
189 | 184 | this.listenerContainer.afterPropertiesSet(); |
190 | 185 | } |
191 | 186 |
|
@@ -225,146 +220,89 @@ public boolean isPaused() { |
225 | 220 | return this.paused; |
226 | 221 | } |
227 | 222 |
|
228 | | - private final class IntegrationRabbitAmqpMessageListener implements RabbitAmqpMessageListener { |
229 | | - |
230 | | - @Override |
231 | | - public void onAmqpMessage(com.rabbitmq.client.amqp.Message amqpMessage, Consumer.@Nullable Context context) { |
232 | | - org.springframework.amqp.core.Message message = RabbitAmqpUtils.fromAmqpMessage(amqpMessage, context); |
233 | | - Message<?> messageToSend = toSpringMessage(message); |
234 | | - try { |
235 | | - Message<?> receivedMessage = sendAndReceiveMessage(messageToSend); |
236 | | - if (receivedMessage != null) { |
237 | | - org.springframework.amqp.core.Message replyMessage = fromSpringMessage(receivedMessage, message); |
238 | | - publishReply(message, replyMessage); |
239 | | - } |
240 | | - else { |
241 | | - logger.warn(() -> "No reply received for message: " + amqpMessage); |
242 | | - } |
243 | | - } |
244 | | - catch (Exception ex) { |
245 | | - throw new ListenerExecutionFailedException(getComponentName() + ".onAmqpMessage() failed", ex, message); |
246 | | - } |
| 223 | + /** |
| 224 | + * Use as {@link java.util.function.BiConsumer} for the {@link IntegrationRabbitAmqpMessageListener}. |
| 225 | + * @param messageToSend the message to produce from this endpoint. |
| 226 | + * @param requestMessage the request AMQP message. |
| 227 | + */ |
| 228 | + private void processRequest(Message<?> messageToSend, org.springframework.amqp.core.Message requestMessage) { |
| 229 | + Message<?> receivedMessage = sendAndReceiveMessage(messageToSend); |
| 230 | + if (receivedMessage != null) { |
| 231 | + org.springframework.amqp.core.Message replyMessage = fromSpringMessage(receivedMessage, requestMessage); |
| 232 | + publishReply(requestMessage, replyMessage); |
247 | 233 | } |
248 | | - |
249 | | - private Message<?> toSpringMessage(org.springframework.amqp.core.Message message) { |
250 | | - if (AmqpClientInboundGateway.this.afterReceivePostProcessors != null) { |
251 | | - for (MessagePostProcessor processor : AmqpClientInboundGateway.this.afterReceivePostProcessors) { |
252 | | - message = processor.postProcessMessage(message); |
253 | | - } |
254 | | - } |
255 | | - MessageProperties messageProperties = message.getMessageProperties(); |
256 | | - AmqpAcknowledgment amqpAcknowledgment = messageProperties.getAmqpAcknowledgment(); |
257 | | - AmqpAcknowledgmentCallback acknowledgmentCallback = null; |
258 | | - if (amqpAcknowledgment != null) { |
259 | | - acknowledgmentCallback = new AmqpAcknowledgmentCallback(amqpAcknowledgment); |
260 | | - } |
261 | | - |
262 | | - Object payload = message; |
263 | | - Map<String, @Nullable Object> headers = null; |
264 | | - if (AmqpClientInboundGateway.this.messageConverter != null) { |
265 | | - payload = AmqpClientInboundGateway.this.messageConverter.fromMessage(message); |
266 | | - headers = AmqpClientInboundGateway.this.headerMapper.toHeadersFromRequest(messageProperties); |
267 | | - } |
268 | | - |
269 | | - return getMessageBuilderFactory() |
270 | | - .withPayload(payload) |
271 | | - .copyHeaders(headers) |
272 | | - .setHeader(IntegrationMessageHeaderAccessor.ACKNOWLEDGMENT_CALLBACK, acknowledgmentCallback) |
273 | | - .build(); |
| 234 | + else { |
| 235 | + this.logger.warn(() -> "No reply received for message: " + requestMessage); |
274 | 236 | } |
| 237 | + } |
275 | 238 |
|
276 | | - private org.springframework.amqp.core.Message fromSpringMessage(Message<?> receivedMessage, |
277 | | - org.springframework.amqp.core.Message requestMessage) { |
| 239 | + private org.springframework.amqp.core.Message fromSpringMessage(Message<?> receivedMessage, |
| 240 | + org.springframework.amqp.core.Message requestMessage) { |
278 | 241 |
|
279 | | - org.springframework.amqp.core.Message replyMessage; |
280 | | - MessageProperties messageProperties = new MessageProperties(); |
281 | | - Object payload = receivedMessage.getPayload(); |
282 | | - if (payload instanceof org.springframework.amqp.core.Message amqpMessage) { |
283 | | - replyMessage = amqpMessage; |
284 | | - } |
285 | | - else { |
286 | | - Assert.state(AmqpClientInboundGateway.this.messageConverter != null, |
287 | | - "If reply payload is not an 'org.springframework.amqp.core.Message', " + |
288 | | - "the 'messageConverter' must be provided."); |
| 242 | + org.springframework.amqp.core.Message replyMessage; |
| 243 | + MessageProperties messageProperties = new MessageProperties(); |
| 244 | + Object payload = receivedMessage.getPayload(); |
| 245 | + if (payload instanceof org.springframework.amqp.core.Message amqpMessage) { |
| 246 | + replyMessage = amqpMessage; |
| 247 | + } |
| 248 | + else { |
| 249 | + Assert.state(this.messageConverter != null, |
| 250 | + "If reply payload is not an 'org.springframework.amqp.core.Message', " + |
| 251 | + "the 'messageConverter' must be provided."); |
| 252 | + |
| 253 | + replyMessage = this.messageConverter.toMessage(payload, messageProperties); |
| 254 | + this.headerMapper.fromHeadersToReply(receivedMessage.getHeaders(), |
| 255 | + messageProperties); |
| 256 | + } |
289 | 257 |
|
290 | | - replyMessage = AmqpClientInboundGateway.this.messageConverter.toMessage(payload, messageProperties); |
291 | | - AmqpClientInboundGateway.this.headerMapper.fromHeadersToReply(receivedMessage.getHeaders(), |
292 | | - messageProperties); |
293 | | - } |
| 258 | + postProcessResponse(requestMessage, replyMessage); |
| 259 | + if (this.replyPostProcessor != null) { |
| 260 | + replyMessage = this.replyPostProcessor.apply(requestMessage, replyMessage); |
| 261 | + } |
294 | 262 |
|
295 | | - postProcessResponse(requestMessage, replyMessage); |
296 | | - if (AmqpClientInboundGateway.this.replyPostProcessor != null) { |
297 | | - replyMessage = AmqpClientInboundGateway.this.replyPostProcessor.apply(requestMessage, replyMessage); |
298 | | - } |
| 263 | + return replyMessage; |
| 264 | + } |
299 | 265 |
|
300 | | - return replyMessage; |
301 | | - } |
| 266 | + private void publishReply(org.springframework.amqp.core.Message requestMessage, |
| 267 | + org.springframework.amqp.core.Message replyMessage) { |
302 | 268 |
|
303 | | - private void publishReply(org.springframework.amqp.core.Message requestMessage, |
304 | | - org.springframework.amqp.core.Message replyMessage) { |
305 | | - |
306 | | - Address replyTo = requestMessage.getMessageProperties().getReplyToAddress(); |
307 | | - if (replyTo != null) { |
308 | | - String exchangeName = replyTo.getExchangeName(); |
309 | | - String routingKey = replyTo.getRoutingKey(); |
310 | | - if (StringUtils.hasText(exchangeName)) { |
311 | | - AmqpClientInboundGateway.this.replyTemplate.send(exchangeName, routingKey, replyMessage).join(); |
312 | | - } |
313 | | - else { |
314 | | - Assert.hasText(routingKey, "A 'replyTo' property must be provided in the requestMessage."); |
315 | | - String queue = routingKey.replaceFirst("queues/", ""); |
316 | | - AmqpClientInboundGateway.this.replyTemplate.send(queue, replyMessage).join(); |
317 | | - } |
| 269 | + Address replyTo = requestMessage.getMessageProperties().getReplyToAddress(); |
| 270 | + if (replyTo != null) { |
| 271 | + String exchangeName = replyTo.getExchangeName(); |
| 272 | + String routingKey = replyTo.getRoutingKey(); |
| 273 | + if (StringUtils.hasText(exchangeName)) { |
| 274 | + this.replyTemplate.send(exchangeName, routingKey, replyMessage).join(); |
318 | 275 | } |
319 | 276 | else { |
320 | | - AmqpClientInboundGateway.this.replyTemplate.send(replyMessage).join(); |
| 277 | + Assert.hasText(routingKey, "A 'replyTo' property must be provided in the requestMessage."); |
| 278 | + String queue = routingKey.replaceFirst("queues/", ""); |
| 279 | + this.replyTemplate.send(queue, replyMessage).join(); |
321 | 280 | } |
322 | 281 | } |
323 | | - |
324 | | - @Override |
325 | | - public void onMessage(org.springframework.amqp.core.Message message) { |
326 | | - throw new UnsupportedOperationException("The 'RabbitAmqpMessageListener' does not implement 'onMessage()'"); |
327 | | - } |
328 | | - |
329 | | - /** |
330 | | - * Post-process the given response message before it will be sent. |
331 | | - * The default implementation sets the response's correlation id to the request message's correlation id, if any; |
332 | | - * otherwise to the request message id. |
333 | | - * @param request the original incoming Rabbit message |
334 | | - * @param response the outgoing Rabbit message about to be sent |
335 | | - */ |
336 | | - private static void postProcessResponse(org.springframework.amqp.core.Message request, |
337 | | - org.springframework.amqp.core.Message response) { |
338 | | - |
339 | | - String correlation = request.getMessageProperties().getCorrelationId(); |
340 | | - |
341 | | - if (correlation == null) { |
342 | | - String messageId = request.getMessageProperties().getMessageId(); |
343 | | - if (messageId != null) { |
344 | | - correlation = messageId; |
345 | | - } |
346 | | - } |
347 | | - response.getMessageProperties().setCorrelationId(correlation); |
| 282 | + else { |
| 283 | + this.replyTemplate.send(replyMessage).join(); |
348 | 284 | } |
349 | | - |
350 | 285 | } |
351 | 286 |
|
352 | 287 | /** |
353 | | - * The {@link AcknowledgmentCallback} adapter for an {@link AmqpAcknowledgment}. |
354 | | - * @param delegate the {@link AmqpAcknowledgment} to delegate to. |
| 288 | + * Post-process the given response message before it will be sent. |
| 289 | + * The default implementation sets the response's correlation id to the request message's correlation id, if any; |
| 290 | + * otherwise to the request message id. |
| 291 | + * @param request the original incoming Rabbit message |
| 292 | + * @param response the outgoing Rabbit message about to be sent |
355 | 293 | */ |
356 | | - private record AmqpAcknowledgmentCallback(AmqpAcknowledgment delegate) implements AcknowledgmentCallback { |
| 294 | + private static void postProcessResponse(org.springframework.amqp.core.Message request, |
| 295 | + org.springframework.amqp.core.Message response) { |
357 | 296 |
|
358 | | - @Override |
359 | | - public void acknowledge(Status status) { |
360 | | - this.delegate.acknowledge(AmqpAcknowledgment.Status.valueOf(status.name())); |
361 | | - } |
| 297 | + String correlation = request.getMessageProperties().getCorrelationId(); |
362 | 298 |
|
363 | | - @Override |
364 | | - public boolean isAutoAck() { |
365 | | - return false; |
| 299 | + if (correlation == null) { |
| 300 | + String messageId = request.getMessageProperties().getMessageId(); |
| 301 | + if (messageId != null) { |
| 302 | + correlation = messageId; |
| 303 | + } |
366 | 304 | } |
367 | | - |
| 305 | + response.getMessageProperties().setCorrelationId(correlation); |
368 | 306 | } |
369 | 307 |
|
370 | 308 | } |
0 commit comments