All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.duracloud.common.sns.SnsSubscriptionManager Maven / Gradle / Ivy

There is a newer version: 4.3.8
Show newest version
/*
 * The contents of this file are subject to the license and copyright
 * detailed in the LICENSE and NOTICE files at the root of the source
 * tree and available online at
 *
 *     http://duracloud.org/license/
 */
package org.duracloud.common.sns;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.duracloud.common.error.DuraCloudRuntimeException;
import org.duracloud.common.util.WaitUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.amazonaws.services.sns.AmazonSNSClient;
import com.amazonaws.services.sns.model.SubscribeResult;
import com.amazonaws.services.sqs.AmazonSQSClient;
import com.amazonaws.services.sqs.model.CreateQueueRequest;
import com.amazonaws.services.sqs.model.CreateQueueResult;
import com.amazonaws.services.sqs.model.GetQueueAttributesResult;
import com.amazonaws.services.sqs.model.GetQueueUrlResult;
import com.amazonaws.services.sqs.model.Message;
import com.amazonaws.services.sqs.model.QueueNameExistsException;
import com.amazonaws.services.sqs.model.ReceiveMessageResult;
import com.amazonaws.services.sqs.model.SetQueueAttributesRequest;

/**
 * 
 * @author Daniel Bernstein
 *
 */
public class SnsSubscriptionManager {
    private Logger log = LoggerFactory.getLogger(SnsSubscriptionManager.class);

    private AmazonSQSClient sqsClient;
    private AmazonSNSClient snsClient;
    private String topicArn;
    private String queueName;
    private String queueUrl;
    private String subscriptionArn;
    private boolean initialized = false;
    private List messageListeners = new ArrayList<>();
    public SnsSubscriptionManager(AmazonSQSClient sqsClient,
                                  AmazonSNSClient snsClient,
                                  String topicArn,
                                  String queueName) {
        this.topicArn = topicArn;
        this.queueName = queueName;
        this.sqsClient = sqsClient;
        this.snsClient = snsClient;
    }
    
    public void addListener(MessageListener listener){
        this.messageListeners.add(listener);
    }
    
    public synchronized void connect(){
        if(initialized){
            throw new DuraCloudRuntimeException("this manager is already connected");
        }
        
        //create sqs queue
        log.info("creating sqs queue");
        CreateQueueRequest request = new CreateQueueRequest(this.queueName);
        Map attributes = new HashMap();
        attributes.put("ReceiveMessageWaitTimeSeconds", "20");
        request.setAttributes(attributes);
        CreateQueueResult result;
        try { 
            result = sqsClient.createQueue(request);
            this.queueUrl = result.getQueueUrl();
            log.info("sqs queue created: {}", this.queueUrl);
        }catch(QueueNameExistsException ex){
            log.info("queue with name {} already exists.");
            GetQueueUrlResult queueUrlResult = sqsClient.getQueueUrl(this.queueName);
            this.queueUrl = queueUrlResult.getQueueUrl();
            log.info("sqs queue url retrieved: {}", this.queueUrl);
        }
        
        String queueArnKey = "QueueArn";
        GetQueueAttributesResult getQueueAttrResult =
            sqsClient.getQueueAttributes(this.queueUrl,
                                         Arrays.asList(queueArnKey));
        log.info("subscribing {} to {}", queueUrl, topicArn);
        
        String queueArn = getQueueAttrResult.getAttributes().get(queueArnKey);
        
        SubscribeResult subscribeResult = this.snsClient.subscribe(topicArn,
                                 "sqs",
                                 queueArn);
        this.subscriptionArn = subscribeResult.getSubscriptionArn();
        
        Map queueAttributes = new HashMap(); 
        queueAttributes.put("Policy", generateSqsPolicyForTopic(queueArn, topicArn)); 
        
        sqsClient.setQueueAttributes(new SetQueueAttributesRequest(queueUrl, queueAttributes)); 
      
        log.info("subscription complete: {}", this.subscriptionArn);
        
        //subscribe queue to topic
        this.initialized = true;
        
        startPolling();
        
        
    }
    
    private  String generateSqsPolicyForTopic(String queueArn, String topicArn) { 
        String policy = 
            "{ " + 
            "  \"Version\":\"2008-10-17\"," + 
            "  \"Id\":\"" + queueArn + "/policyId\"," + 
            "  \"Statement\": [" + 
            "    {" + 
            "        \"Sid\":\"" + queueArn + "/statementId\"," + 
            "        \"Effect\":\"Allow\"," + 
            "        \"Principal\":{\"AWS\":\"*\"}," + 
            "        \"Action\":\"SQS:SendMessage\"," + 
            "        \"Resource\": \"" + queueArn + "\"," + 
            "        \"Condition\":{" + 
            "            \"StringEquals\":{\"aws:SourceArn\":\"" + topicArn + "\"}" + 
            "        }" + 
            "    }" + 
            "  ]" + 
            "}"; 
 
        return policy; 
    } 

    
    private void startPolling() {
        new Thread(new Runnable(){
            @Override
            public void run() {
                while(initialized){
                    try {
                        ReceiveMessageResult result = sqsClient.receiveMessage(queueUrl);
                        List messages = result.getMessages();
                        for(Message message : messages){
                            dispatch(message);
                            log.debug("{} dispatched", message);
                            sqsClient.deleteMessage(queueUrl,message.getReceiptHandle());
                            log.debug("{} deleted", message);
                        }
                        
                    }catch(Exception ex){
                        log.warn("failed to poll queue: " + ex.getMessage(), ex);
                    }
                }
            }

        }, "sqs-long-poller").start();
    }

    private void dispatch(Message message) {
        log.debug("dispatching message {}", message);
        for(MessageListener listener : messageListeners){
            try{
                listener.onMessage(message);
            }catch(Exception ex){
                log.error("failed to dispatch message " + message
                          + " to "
                          + listener
                          + "due to "
                          + ex.getMessage(),
                          ex);
            }
        }
    }

    public void disconnect(){
        if(!this.initialized){
            throw new DuraCloudRuntimeException("this manager is already disconnected");
        }
        
        log.info("disconnecting");
        log.info("unsubscribing {}", this.subscriptionArn);
        this.snsClient.unsubscribe(this.subscriptionArn);
        log.info("unsubscribed {}", this.subscriptionArn);
        log.info("deleting queue {}", this.subscriptionArn);
        this.sqsClient.deleteQueue(this.queueUrl);
        log.info("deleted queue {}", this.subscriptionArn);
        this.initialized = false;
        //Redeploys will fail due to amazon sqs requirement to wait
        //60 seconds before recreating a queue exit without waiting.
        WaitUtil.wait(60);
        log.info("disconnection complete");
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy