package org.apache.usergrid.persistence.queue.util; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.usergrid.persistence.queue.LegacyQueueFig; import com.amazonaws.auth.policy.Condition; import com.amazonaws.auth.policy.Policy; import com.amazonaws.auth.policy.Principal; import com.amazonaws.auth.policy.Resource; import com.amazonaws.auth.policy.Statement; import com.amazonaws.auth.policy.actions.SQSActions; import com.amazonaws.auth.policy.conditions.ConditionFactory; import com.amazonaws.services.sns.AmazonSNSClient; import com.amazonaws.services.sns.model.CreateTopicResult; import com.amazonaws.services.sns.model.ListTopicsResult; import com.amazonaws.services.sns.model.Topic; 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.GetQueueAttributesRequest; import com.amazonaws.services.sqs.model.GetQueueAttributesResult; import com.amazonaws.services.sqs.model.GetQueueUrlResult; import com.amazonaws.services.sqs.model.QueueDoesNotExistException; import com.amazonaws.services.sqs.model.SetQueueAttributesRequest; /** * Created by Jeff West on 5/25/15. */ public class AmazonNotificationUtils { private static final Logger logger = LoggerFactory.getLogger( AmazonNotificationUtils.class ); public static String createQueue( final AmazonSQSClient sqs, final String queueName, final LegacyQueueFig fig ) throws Exception { final String deadletterQueueName = String.format( "%s_dead", queueName ); final Map<String, String> deadLetterAttributes = new HashMap<>( 2 ); deadLetterAttributes.put( "MessageRetentionPeriod", fig.getDeadletterRetentionPeriod() ); CreateQueueRequest createDeadLetterQueueRequest = new CreateQueueRequest().withQueueName( deadletterQueueName ).withAttributes( deadLetterAttributes ); final CreateQueueResult deadletterResult = sqs.createQueue( createDeadLetterQueueRequest ); logger.info( "Created deadletter queue with url {}", deadletterResult.getQueueUrl() ); final String deadletterArn = AmazonNotificationUtils.getQueueArnByName( sqs, deadletterQueueName ); String redrivePolicy = String .format( "{\"maxReceiveCount\":\"%s\"," + " \"deadLetterTargetArn\":\"%s\"}", fig.getQueueDeliveryLimit(), deadletterArn ); final String visibilityTimeoutInSeconds = String.valueOf(Math.max(1, fig.getVisibilityTimeout() / 1000)); final Map<String, String> queueAttributes = new HashMap<>( 2 ); queueAttributes.put( "MessageRetentionPeriod", fig.getRetentionPeriod() ); queueAttributes.put( "RedrivePolicy", redrivePolicy ); queueAttributes.put( "VisibilityTimeout", visibilityTimeoutInSeconds ); CreateQueueRequest createQueueRequest = new CreateQueueRequest(). withQueueName( queueName ) .withAttributes( queueAttributes ); CreateQueueResult result = sqs.createQueue( createQueueRequest ); String url = result.getQueueUrl(); logger.info( "Created SQS queue with url {}", url ); return url; } public static void setQueuePermissionsToReceive( final AmazonSQSClient sqs, final String queueUrl, final List<String> topicARNs ) throws Exception { String queueARN = getQueueArnByUrl( sqs, queueUrl ); Statement statement = new Statement( Statement.Effect.Allow ).withActions( SQSActions.SendMessage ) .withPrincipals( new Principal( "*" ) ) .withResources( new Resource( queueARN ) ); List<Condition> conditions = new ArrayList<>(); for ( String topicARN : topicARNs ) { conditions.add( ConditionFactory.newSourceArnCondition( topicARN ) ); } statement.setConditions( conditions ); Policy policy = new Policy( "SubscriptionPermission" ).withStatements( statement ); final Map<String, String> queueAttributes = new HashMap<>(); queueAttributes.put( "Policy", policy.toJson() ); SetQueueAttributesRequest queueAttributesRequest = new SetQueueAttributesRequest( queueUrl, queueAttributes ); try { sqs.setQueueAttributes( queueAttributesRequest ); } catch ( Exception e ) { logger.error( "Failed to set permissions on QUEUE ARN=[{}] for TOPIC ARNs=[{}]", queueARN, topicARNs.toString(), e ); } } public static String getQueueArnByName( final AmazonSQSClient sqs, final String queueName ) throws Exception { String queueUrl = null; try { GetQueueUrlResult result = sqs.getQueueUrl( queueName ); queueUrl = result.getQueueUrl(); } catch ( QueueDoesNotExistException queueDoesNotExistException ) { //no op, swallow logger.warn( "Queue {} does not exist", queueName ); return null; } catch ( Exception e ) { logger.error( "Failed to get URL for Queue [{}] from SQS", queueName, e ); throw e; } if ( queueUrl != null ) { try { GetQueueAttributesRequest queueAttributesRequest = new GetQueueAttributesRequest( queueUrl ).withAttributeNames( "All" ); GetQueueAttributesResult queueAttributesResult = sqs.getQueueAttributes( queueAttributesRequest ); Map<String, String> sqsAttributeMap = queueAttributesResult.getAttributes(); return sqsAttributeMap.get( "QueueArn" ); } catch ( Exception e ) { logger.error( "Failed to get queue URL from service", e ); throw e; } } return null; } public static String getQueueArnByUrl( final AmazonSQSClient sqs, final String queueUrl ) throws Exception { try { GetQueueAttributesRequest queueAttributesRequest = new GetQueueAttributesRequest( queueUrl ).withAttributeNames( "All" ); GetQueueAttributesResult queueAttributesResult = sqs.getQueueAttributes( queueAttributesRequest ); Map<String, String> sqsAttributeMap = queueAttributesResult.getAttributes(); return sqsAttributeMap.get( "QueueArn" ); } catch ( Exception e ) { logger.error( "Failed to get queue URL from service", e ); throw e; } } public static String getTopicArn( final AmazonSNSClient sns, final String queueName, final boolean createOnMissing ) throws Exception { if ( logger.isTraceEnabled() ) { logger.trace( "Looking up Topic ARN: {}", queueName ); } ListTopicsResult listTopicsResult = sns.listTopics(); String topicArn = null; for ( Topic topic : listTopicsResult.getTopics() ) { String arn = topic.getTopicArn(); if ( queueName.equals( arn.substring( arn.lastIndexOf( ':' ) ) ) ) { topicArn = arn; if (logger.isTraceEnabled()) { logger.trace( "Found existing topic arn=[{}] for queue=[{}]", topicArn, queueName ); } } } if ( topicArn == null && createOnMissing ) { if (logger.isTraceEnabled()) { logger.trace("Creating topic for queue=[{}]...", queueName); } CreateTopicResult createTopicResult = sns.createTopic( queueName ); topicArn = createTopicResult.getTopicArn(); if (logger.isTraceEnabled()) { logger.trace("Successfully created topic with name {} and arn {}", queueName, topicArn); } } else { logger.error( "Error looking up topic ARN for queue=[{}] and createOnMissing=[{}]", queueName, createOnMissing ); } if ( logger.isTraceEnabled() ) { logger.trace( "Returning Topic ARN=[{}] for Queue=[{}]", topicArn, queueName ); } return topicArn; } public static String getQueueUrlByName( final AmazonSQSClient sqs, final String queueName ) { try { GetQueueUrlResult result = sqs.getQueueUrl( queueName ); return result.getQueueUrl(); } catch ( QueueDoesNotExistException e ) { //no op, return null logger.error( "Queue {} does not exist", queueName ); return null; } catch ( Exception e ) { logger.error( "failed to get queue from service", e ); throw e; } } }