/*
* Copyright 2016 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.aws.inbound;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.cloud.aws.core.env.ResourceIdResolver;
import org.springframework.cloud.aws.messaging.config.SimpleMessageListenerContainerFactory;
import org.springframework.cloud.aws.messaging.listener.QueueMessageHandler;
import org.springframework.cloud.aws.messaging.listener.SimpleMessageListenerContainer;
import org.springframework.cloud.aws.messaging.listener.SqsMessageDeletionPolicy;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.integration.aws.support.AwsHeaders;
import org.springframework.integration.endpoint.MessageProducerSupport;
import org.springframework.integration.support.management.IntegrationManagedResource;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.core.DestinationResolver;
import org.springframework.messaging.handler.HandlerMethod;
import org.springframework.util.Assert;
import com.amazonaws.services.sqs.AmazonSQSAsync;
/**
* The {@link MessageProducerSupport} implementation for the Amazon SQS {@code receiveMessage}.
* Works in 'listener' manner and delegates hard to the {@link SimpleMessageListenerContainer}.
*
* @author Artem Bilan
* @author Patrick Fitzsimons
*
* @see SimpleMessageListenerContainerFactory
* @see SimpleMessageListenerContainer
* @see QueueMessageHandler
*/
@ManagedResource
@IntegrationManagedResource
public class SqsMessageDrivenChannelAdapter extends MessageProducerSupport implements DisposableBean {
private final SimpleMessageListenerContainerFactory simpleMessageListenerContainerFactory =
new SimpleMessageListenerContainerFactory();
private final String[] queues;
private SimpleMessageListenerContainer listenerContainer;
private Long queueStopTimeout;
private SqsMessageDeletionPolicy messageDeletionPolicy = SqsMessageDeletionPolicy.NO_REDRIVE;
public SqsMessageDrivenChannelAdapter(AmazonSQSAsync amazonSqs, String... queues) {
Assert.noNullElements(queues, "'queues' must not be empty");
this.simpleMessageListenerContainerFactory.setAmazonSqs(amazonSqs);
this.queues = Arrays.copyOf(queues, queues.length);
}
public void setTaskExecutor(AsyncTaskExecutor taskExecutor) {
this.simpleMessageListenerContainerFactory.setTaskExecutor(taskExecutor);
}
public void setMaxNumberOfMessages(Integer maxNumberOfMessages) {
this.simpleMessageListenerContainerFactory.setMaxNumberOfMessages(maxNumberOfMessages);
}
public void setVisibilityTimeout(Integer visibilityTimeout) {
this.simpleMessageListenerContainerFactory.setVisibilityTimeout(visibilityTimeout);
}
public void setWaitTimeOut(Integer waitTimeOut) {
this.simpleMessageListenerContainerFactory.setWaitTimeOut(waitTimeOut);
}
public void setResourceIdResolver(ResourceIdResolver resourceIdResolver) {
this.simpleMessageListenerContainerFactory.setResourceIdResolver(resourceIdResolver);
}
@Override
public void setAutoStartup(boolean autoStartUp) {
super.setAutoStartup(autoStartUp);
this.simpleMessageListenerContainerFactory.setAutoStartup(autoStartUp);
}
public void setDestinationResolver(DestinationResolver<String> destinationResolver) {
this.simpleMessageListenerContainerFactory.setDestinationResolver(destinationResolver);
}
public void setQueueStopTimeout(long queueStopTimeout) {
this.queueStopTimeout = queueStopTimeout;
}
public void setMessageDeletionPolicy(SqsMessageDeletionPolicy messageDeletionPolicy) {
Assert.notNull(messageDeletionPolicy, "'messageDeletionPolicy' must not be null.");
this.messageDeletionPolicy = messageDeletionPolicy;
}
@Override
protected void onInit() {
super.onInit();
this.listenerContainer = this.simpleMessageListenerContainerFactory.createSimpleMessageListenerContainer();
if (this.queueStopTimeout != null) {
this.listenerContainer.setQueueStopTimeout(this.queueStopTimeout);
}
this.listenerContainer.setMessageHandler(new IntegrationQueueMessageHandler());
try {
this.listenerContainer.afterPropertiesSet();
}
catch (Exception e) {
throw new BeanCreationException("Cannot instantiate 'SimpleMessageListenerContainer'", e);
}
}
@Override
public String getComponentType() {
return "aws:sqs-message-driven-channel-adapter";
}
@Override
protected void doStart() {
this.listenerContainer.start();
}
@Override
protected void doStop() {
this.listenerContainer.stop();
}
@ManagedOperation
public void stop(String logicalQueueName) {
this.listenerContainer.stop(logicalQueueName);
}
@ManagedOperation
public void start(String logicalQueueName) {
this.listenerContainer.start(logicalQueueName);
}
@ManagedOperation
public boolean isRunning(String logicalQueueName) {
return this.listenerContainer.isRunning(logicalQueueName);
}
@Override
public void destroy() throws Exception {
this.listenerContainer.destroy();
}
private class IntegrationQueueMessageHandler extends QueueMessageHandler {
@Override
public Map<MappingInformation, HandlerMethod> getHandlerMethods() {
Set<String> queues = new HashSet<>(Arrays.asList(SqsMessageDrivenChannelAdapter.this.queues));
MappingInformation mappingInformation = new MappingInformation(queues,
SqsMessageDrivenChannelAdapter.this.messageDeletionPolicy);
return Collections.singletonMap(mappingInformation, null);
}
@Override
protected void handleMessageInternal(Message<?> message, String lookupDestination) {
MessageHeaders headers = message.getHeaders();
Message<?> messageToSend = getMessageBuilderFactory()
.fromMessage(message)
.removeHeaders("LogicalResourceId",
"MessageId",
"ReceiptHandle",
"Acknowledgment")
.setHeader(AwsHeaders.MESSAGE_ID, headers.get("MessageId"))
.setHeader(AwsHeaders.RECEIPT_HANDLE, headers.get("ReceiptHandle"))
.setHeader(AwsHeaders.QUEUE, headers.get("LogicalResourceId"))
.setHeader(AwsHeaders.ACKNOWLEDGMENT, headers.get("Acknowledgment"))
.build();
sendMessage(messageToSend);
}
}
}