org.springframework.integration.amqp.outbound.AsyncAmqpOutboundGateway.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.integration.amqp.outbound.AsyncAmqpOutboundGateway.java

Source

/*
 * Copyright 2016-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.integration.amqp.outbound;

import org.springframework.amqp.core.AmqpMessageReturnedException;
import org.springframework.amqp.core.AmqpReplyTimeoutException;
import org.springframework.amqp.rabbit.AsyncRabbitTemplate;
import org.springframework.amqp.rabbit.AsyncRabbitTemplate.RabbitMessageFuture;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.integration.amqp.support.MappingUtils;
import org.springframework.integration.handler.ReplyRequiredException;
import org.springframework.integration.support.AbstractIntegrationMessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandlingException;
import org.springframework.messaging.MessagingException;
import org.springframework.util.Assert;
import org.springframework.util.concurrent.ListenableFutureCallback;

/**
 * An outbound gateway where the sending thread is released immediately and the reply
 * is sent on the async template's listener container thread.
 *
 * @author Gary Russell
 * @author Artem Bilan
 *
 * @since 4.3
 *
 */
public class AsyncAmqpOutboundGateway extends AbstractAmqpOutboundEndpoint {

    private final AsyncRabbitTemplate template;

    private final MessageConverter messageConverter;

    public AsyncAmqpOutboundGateway(AsyncRabbitTemplate template) {
        Assert.notNull(template, "AsyncRabbitTemplate cannot be null");
        this.template = template;
        this.messageConverter = template.getMessageConverter();
        Assert.notNull(this.messageConverter, "the template's message converter cannot be null");
        setConnectionFactory(this.template.getConnectionFactory());
        setAsync(true);
    }

    @Override
    public String getComponentType() {
        return "amqp:outbound-async-gateway";
    }

    @Override
    protected RabbitTemplate getRabbitTemplate() {
        return this.template.getRabbitTemplate();
    }

    @Override
    protected void doStart() {
        super.doStart();
        this.template.start();
    }

    @Override
    protected void doStop() {
        this.template.stop();
        super.doStop();
    }

    @Override
    protected Object handleRequestMessage(Message<?> requestMessage) {
        org.springframework.amqp.core.Message amqpMessage = MappingUtils.mapMessage(requestMessage,
                this.messageConverter, getHeaderMapper(), getDefaultDeliveryMode(), isHeadersMappedLast());
        addDelayProperty(requestMessage, amqpMessage);
        RabbitMessageFuture future = this.template.sendAndReceive(generateExchangeName(requestMessage),
                generateRoutingKey(requestMessage), amqpMessage);
        future.addCallback(new FutureCallback(requestMessage));
        CorrelationData correlationData = generateCorrelationData(requestMessage);
        if (correlationData != null && future.getConfirm() != null) {
            future.getConfirm().addCallback(new CorrelationCallback(correlationData, future));
        }
        return null;
    }

    private final class FutureCallback implements ListenableFutureCallback<org.springframework.amqp.core.Message> {

        private final Message<?> requestMessage;

        FutureCallback(Message<?> requestMessage) {
            this.requestMessage = requestMessage;
        }

        @Override
        public void onSuccess(org.springframework.amqp.core.Message result) {
            AbstractIntegrationMessageBuilder<?> replyMessageBuilder = null;
            try {
                replyMessageBuilder = buildReply(AsyncAmqpOutboundGateway.this.messageConverter, result);
                sendOutputs(replyMessageBuilder, this.requestMessage);
            } catch (Exception e) {
                Exception exceptionToLogAndSend = e;
                if (!(e instanceof MessagingException)) {
                    exceptionToLogAndSend = new MessageHandlingException(this.requestMessage,
                            "failed to handle a message in the [" + AsyncAmqpOutboundGateway.this + ']', e);
                    if (replyMessageBuilder != null) {
                        exceptionToLogAndSend = new MessagingException(replyMessageBuilder.build(),
                                exceptionToLogAndSend);
                    }
                }
                logger.error("Failed to send async reply: " + result.toString(), exceptionToLogAndSend);
                sendErrorMessage(this.requestMessage, exceptionToLogAndSend);
            }
        }

        @Override
        public void onFailure(Throwable ex) {
            Throwable exceptionToSend = ex;
            if (ex instanceof AmqpReplyTimeoutException) {
                if (getRequiresReply()) {
                    exceptionToSend = new ReplyRequiredException(this.requestMessage,
                            "Timeout on async request/reply", ex);
                } else {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Reply not required and async timeout for " + this.requestMessage);
                    }
                    return;
                }
            }
            if (ex instanceof AmqpMessageReturnedException) {
                if (getReturnChannel() == null) {
                    logger.error("Returned message received and no return channel "
                            + ((AmqpMessageReturnedException) ex).getReturnedMessage());
                } else {
                    AmqpMessageReturnedException amre = (AmqpMessageReturnedException) ex;
                    Message<?> returnedMessage = buildReturnedMessage(amre.getReturnedMessage(),
                            amre.getReplyCode(), amre.getReplyText(), amre.getExchange(), amre.getRoutingKey(),
                            AsyncAmqpOutboundGateway.this.messageConverter);
                    sendOutput(returnedMessage, getReturnChannel(), true);
                }
            } else {
                sendErrorMessage(this.requestMessage, exceptionToSend);
            }
        }

    }

    private final class CorrelationCallback implements ListenableFutureCallback<Boolean> {

        private final CorrelationData correlationData;

        private final RabbitMessageFuture replyFuture;

        CorrelationCallback(CorrelationData correlationData, RabbitMessageFuture replyFuture) {
            this.correlationData = correlationData;
            this.replyFuture = replyFuture;
        }

        @Override
        public void onSuccess(Boolean result) {
            try {
                handleConfirm(this.correlationData, result, this.replyFuture.getNackCause());
            } catch (Exception e) {
                logger.error("Failed to send publisher confirm");
            }
        }

        @Override
        public void onFailure(Throwable ex) {
        }

    }

}