/*
* Copyright 2013-2014 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.cloud.aws.messaging.core;
import com.amazonaws.services.sqs.AmazonSQS;
import com.amazonaws.services.sqs.AmazonSQSAsync;
import org.springframework.cloud.aws.core.env.ResourceIdResolver;
import org.springframework.cloud.aws.messaging.core.support.AbstractMessageChannelMessagingSendingTemplate;
import org.springframework.cloud.aws.messaging.support.destination.DynamicQueueUrlDestinationResolver;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.converter.CompositeMessageConverter;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.converter.StringMessageConverter;
import org.springframework.messaging.core.DestinationResolver;
import org.springframework.messaging.core.DestinationResolvingMessageReceivingOperations;
import org.springframework.util.ClassUtils;
import java.util.ArrayList;
import java.util.List;
/**
* <b>IMPORTANT</b>: For the message conversion this class always tries to first use the {@link StringMessageConverter}
* as it fits the underlying message channel type. If a message converter is set through the constructor then it is
* added to a composite converter already containing the {@link StringMessageConverter}.
* If {@link QueueMessagingTemplate#setMessageConverter(MessageConverter)} is used, then the {@link CompositeMessageConverter}
* containing the {@link StringMessageConverter} will not be used anymore and the {@code String} payloads are also going
* to be converted with the set converter.
*
* @author Agim Emruli
* @author Alain Sahli
* @since 1.0
*/
public class QueueMessagingTemplate extends AbstractMessageChannelMessagingSendingTemplate<QueueMessageChannel> implements DestinationResolvingMessageReceivingOperations<QueueMessageChannel> {
private final AmazonSQSAsync amazonSqs;
private static final boolean JACKSON_2_PRESENT = ClassUtils.isPresent(
"com.fasterxml.jackson.databind.ObjectMapper", QueueMessagingTemplate.class.getClassLoader());
public QueueMessagingTemplate(AmazonSQSAsync amazonSqs) {
this(amazonSqs, (ResourceIdResolver) null, null);
}
public QueueMessagingTemplate(AmazonSQSAsync amazonSqs, ResourceIdResolver resourceIdResolver) {
this(amazonSqs, resourceIdResolver, null);
}
/**
* Initializes the messaging template by configuring the destination resolver as well as the message
* converter. Uses the {@link DynamicQueueUrlDestinationResolver} with the default configuration to
* resolve destination names.
*
* @param amazonSqs
* The {@link AmazonSQS} client, cannot be {@code null}.
* @param resourceIdResolver
* The {@link ResourceIdResolver} to be used for resolving logical queue names.
* @param messageConverter
* A {@link MessageConverter} that is going to be added to the composite converter.
*/
public QueueMessagingTemplate(AmazonSQSAsync amazonSqs, ResourceIdResolver resourceIdResolver, MessageConverter messageConverter) {
this(amazonSqs, new DynamicQueueUrlDestinationResolver(amazonSqs, resourceIdResolver), messageConverter);
}
/**
* Inititalizes
*
* @param amazonSqs
* The {@link AmazonSQS} client, cannot be {@code null}.
* @param destinationResolver
* A destination resolver implementation to resolve queue names into queue urls. The
* destination resolver will be wrapped into a {@link org.springframework.messaging.core.CachingDestinationResolverProxy}
* to avoid duplicate queue url resolutions.
* @param messageConverter
* A {@link MessageConverter} that is going to be added to the composite converter.
*/
public QueueMessagingTemplate(AmazonSQSAsync amazonSqs, DestinationResolver<String> destinationResolver, MessageConverter messageConverter) {
super(destinationResolver);
this.amazonSqs = amazonSqs;
initMessageConverter(messageConverter);
}
private void initMessageConverter(MessageConverter messageConverter) {
StringMessageConverter stringMessageConverter = new StringMessageConverter();
stringMessageConverter.setSerializedPayloadClass(String.class);
List<MessageConverter> messageConverters = new ArrayList<>();
messageConverters.add(stringMessageConverter);
if (messageConverter != null) {
messageConverters.add(messageConverter);
} else if (JACKSON_2_PRESENT) {
MappingJackson2MessageConverter mappingJackson2MessageConverter = new MappingJackson2MessageConverter();
mappingJackson2MessageConverter.setSerializedPayloadClass(String.class);
messageConverters.add(mappingJackson2MessageConverter);
}
setMessageConverter(new CompositeMessageConverter(messageConverters));
}
@Override
protected QueueMessageChannel resolveMessageChannel(String physicalResourceIdentifier) {
return new QueueMessageChannel(this.amazonSqs, physicalResourceIdentifier);
}
@Override
public Message<?> receive() throws MessagingException {
return receive(getRequiredDefaultDestination());
}
@Override
public Message<?> receive(QueueMessageChannel destination) throws MessagingException {
return destination.receive();
}
@Override
public <T> T receiveAndConvert(Class<T> targetClass) throws MessagingException {
return receiveAndConvert(getRequiredDefaultDestination(), targetClass);
}
@SuppressWarnings("unchecked")
@Override
public <T> T receiveAndConvert(QueueMessageChannel destination, Class<T> targetClass) throws MessagingException {
Message<?> message = destination.receive();
if (message != null) {
return (T) getMessageConverter().fromMessage(message, targetClass);
} else {
return null;
}
}
@Override
public Message<?> receive(String destinationName) throws MessagingException {
return resolveMessageChannelByLogicalName(destinationName).receive();
}
@Override
public <T> T receiveAndConvert(String destinationName, Class<T> targetClass) throws MessagingException {
QueueMessageChannel channel = resolveMessageChannelByLogicalName(destinationName);
return receiveAndConvert(channel, targetClass);
}
}