/*
* Copyright 2002-2017 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
*
* http://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.AmqpTemplate;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.core.RabbitTemplate.ReturnCallback;
import org.springframework.amqp.rabbit.support.CorrelationData;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.Lifecycle;
import org.springframework.integration.amqp.support.MappingUtils;
import org.springframework.integration.support.AbstractIntegrationMessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.util.Assert;
/**
* Adapter that converts and sends Messages to an AMQP Exchange.
*
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Artem Bilan
*
* @since 2.1
*/
public class AmqpOutboundEndpoint extends AbstractAmqpOutboundEndpoint
implements RabbitTemplate.ConfirmCallback, ReturnCallback {
private final AmqpTemplate amqpTemplate;
private volatile boolean expectReply;
public AmqpOutboundEndpoint(AmqpTemplate amqpTemplate) {
Assert.notNull(amqpTemplate, "amqpTemplate must not be null");
this.amqpTemplate = amqpTemplate;
if (amqpTemplate instanceof RabbitTemplate) {
setConnectionFactory(((RabbitTemplate) amqpTemplate).getConnectionFactory());
}
}
public void setExpectReply(boolean expectReply) {
this.expectReply = expectReply;
}
@Override
public String getComponentType() {
return this.expectReply ? "amqp:outbound-gateway" : "amqp:outbound-channel-adapter";
}
@Override
protected void endpointInit() {
if (getConfirmCorrelationExpression() != null) {
Assert.isInstanceOf(RabbitTemplate.class, this.amqpTemplate,
"RabbitTemplate implementation is required for publisher confirms");
((RabbitTemplate) this.amqpTemplate).setConfirmCallback(this);
}
if (getReturnChannel() != null) {
Assert.isInstanceOf(RabbitTemplate.class, this.amqpTemplate,
"RabbitTemplate implementation is required for publisher confirms");
((RabbitTemplate) this.amqpTemplate).setReturnCallback(this);
}
}
@Override
protected void doStop() {
if (this.amqpTemplate instanceof Lifecycle) {
((Lifecycle) this.amqpTemplate).stop();
}
}
@Override
protected Object handleRequestMessage(Message<?> requestMessage) {
CorrelationData correlationData = generateCorrelationData(requestMessage);
String exchangeName = generateExchangeName(requestMessage);
String routingKey = generateRoutingKey(requestMessage);
if (this.expectReply) {
return this.sendAndReceive(exchangeName, routingKey, requestMessage, correlationData);
}
else {
this.send(exchangeName, routingKey, requestMessage, correlationData);
return null;
}
}
private void send(String exchangeName, String routingKey,
final Message<?> requestMessage, CorrelationData correlationData) {
if (this.amqpTemplate instanceof RabbitTemplate) {
MessageConverter converter = ((RabbitTemplate) this.amqpTemplate).getMessageConverter();
org.springframework.amqp.core.Message amqpMessage = MappingUtils.mapMessage(requestMessage, converter,
getHeaderMapper(), getDefaultDeliveryMode(), isHeadersMappedLast());
addDelayProperty(requestMessage, amqpMessage);
((RabbitTemplate) this.amqpTemplate).send(exchangeName, routingKey, amqpMessage, correlationData);
}
else {
this.amqpTemplate.convertAndSend(exchangeName, routingKey, requestMessage.getPayload(),
message -> {
getHeaderMapper().fromHeadersToRequest(requestMessage.getHeaders(),
message.getMessageProperties());
return message;
});
}
}
private AbstractIntegrationMessageBuilder<?> sendAndReceive(String exchangeName, String routingKey,
Message<?> requestMessage, CorrelationData correlationData) {
Assert.isInstanceOf(RabbitTemplate.class, this.amqpTemplate,
"RabbitTemplate implementation is required for publisher confirms");
MessageConverter converter = ((RabbitTemplate) this.amqpTemplate).getMessageConverter();
org.springframework.amqp.core.Message amqpMessage = MappingUtils.mapMessage(requestMessage, converter,
getHeaderMapper(), getDefaultDeliveryMode(), isHeadersMappedLast());
addDelayProperty(requestMessage, amqpMessage);
org.springframework.amqp.core.Message amqpReplyMessage =
((RabbitTemplate) this.amqpTemplate).sendAndReceive(exchangeName, routingKey, amqpMessage,
correlationData);
if (amqpReplyMessage == null) {
return null;
}
return buildReply(converter, amqpReplyMessage);
}
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
handleConfirm(correlationData, ack, cause);
}
@Override
public void returnedMessage(org.springframework.amqp.core.Message message, int replyCode, String replyText,
String exchange, String routingKey) {
// safe to cast; we asserted we have a RabbitTemplate in doInit()
MessageConverter converter = ((RabbitTemplate) this.amqpTemplate).getMessageConverter();
Message<?> returned = buildReturnedMessage(message, replyCode, replyText, exchange,
routingKey, converter);
getReturnChannel().send(returned);
}
}