com.symphony.oss.fugue.aws.sns.SnsPublisherBase Maven / Gradle / Ivy
/*
*
*
* Copyright 2018 Symphony Communication Services, LLC.
*
* Licensed to The Symphony Software Foundation (SSF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 com.symphony.oss.fugue.aws.sns;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.services.sns.AmazonSNS;
import com.amazonaws.services.sns.AmazonSNSClientBuilder;
import com.amazonaws.services.sns.model.MessageAttributeValue;
import com.amazonaws.services.sns.model.PublishRequest;
import com.google.common.collect.ImmutableList;
import com.symphony.oss.commons.fault.FaultAccumulator;
import com.symphony.oss.commons.type.provider.IIntegerProvider;
import com.symphony.oss.fugue.config.IConfiguration;
import com.symphony.oss.fugue.naming.TopicName;
import com.symphony.oss.fugue.pubsub.AbstractPublisherManager;
import com.symphony.oss.fugue.pubsub.IPubSubMessage;
import com.symphony.oss.fugue.pubsub.IPublisher;
import com.symphony.oss.fugue.trace.ITraceContext;
/**
* Amazon SNS implementation of PublisherManager.
*
* @author Bruce Skingle
*
* @param Type of concrete manager, needed for fluent methods.
*
*/
public abstract class SnsPublisherBase> extends AbstractPublisherManager
{
private static final Logger log_ = LoggerFactory.getLogger(SnsPublisherBase.class);
protected static final int MAX_MESSAGE_SIZE = 256 * 1024; // 256K
protected static final int BILLABLE_MESSAGE_SIZE = 64 * 1024;
protected final Map publisherNameMap_ = new HashMap<>();
protected final List publishers_ = new ArrayList<>();
protected final String region_;
protected final String accountId_;
protected final AmazonSNS snsClient_;
protected final ImmutableList subscriberAccountIds_;
protected SnsPublisherBase(Class type, Builder,T> builder)
{
super(type, builder);
region_ = builder.region_;
accountId_ = builder.accountId_;
snsClient_ = builder.snsBuilder_.build();
subscriberAccountIds_ = ImmutableList.copyOf(
builder.config_.getConfiguration("amazon").getListOfString("subscriberAccountIds", new ArrayList<>()));
int errorCnt = 0;
for(TopicName topicName : builder.topicNames_)
{
if(!validateTopic(topicName))
errorCnt++;
}
if(errorCnt > 0)
{
throw new IllegalStateException("There are " + errorCnt + " topic validation errors");
}
for(TopicName topicName : builder.topicNames_)
{
publisherNameMap_.put(topicName, new SnsPublisher(topicName, getTopicARN(topicName), this));
}
}
/**
* Builder.
*
* @author Bruce Skingle
*
* @param The concrete type returned by fluent methods.
* @param The concrete type of the built object.
*/
public static abstract class Builder, B extends AbstractPublisherManager>
extends AbstractPublisherManager.Builder
{
protected final AmazonSNSClientBuilder snsBuilder_;
protected final Set topicNames_ = new HashSet<>();
protected IConfiguration config_;
protected String region_;
protected String accountId_;
protected Builder(Class type)
{
super(type);
snsBuilder_ = AmazonSNSClientBuilder.standard();
}
/**
* Set the global application configuration.
*
* @param config The global application configuration.
*
* @return this (fluent method)
*/
public T withConfig(IConfiguration config)
{
config_ = config;
return self();
}
/**
* Set the AWS region.
*
* @param region The AWS region in which to operate.
*
* @return this (fluent method)
*/
public T withRegion(String region)
{
region_ = region;
snsBuilder_.withRegion(region_);
return self();
}
/**
* Set the AWS account ID.
*
* @param accountId The ID of the AWS account in which to operate.
*
* @return this (fluent method)
*/
public T withAccountId(String accountId)
{
accountId_ = accountId;
return self();
}
/**
* Set the AWS credentials provider.
*
* @param credentialsProvider An AWS credentials provider.
*
* @return this (fluent method)
*/
public T withCredentials(AWSCredentialsProvider credentialsProvider)
{
snsBuilder_.withCredentials(credentialsProvider);
return self();
}
@Override
public T withTopic(TopicName name)
{
topicNames_.add(name);
return self();
}
@Override
public synchronized void validate(FaultAccumulator faultAccumulator)
{
super.validate(faultAccumulator);
faultAccumulator.checkNotNull(config_, "config");
faultAccumulator.checkNotNull(region_, "region");
faultAccumulator.checkNotNull(accountId_, "accountId");
IConfiguration snsConfig = config_.getConfiguration("org/symphonyoss/s2/fugue/aws/sns");
ClientConfiguration clientConfig = new ClientConfiguration()
.withMaxConnections( snsConfig.getInt( "maxConnections", ClientConfiguration.DEFAULT_MAX_CONNECTIONS))
.withClientExecutionTimeout( snsConfig.getInt( "clientExecutionTimeout", ClientConfiguration.DEFAULT_CLIENT_EXECUTION_TIMEOUT))
.withConnectionMaxIdleMillis( snsConfig.getLong("connectionMaxIdleMillis", ClientConfiguration.DEFAULT_CONNECTION_MAX_IDLE_MILLIS))
.withConnectionTimeout( snsConfig.getInt( "connectionTimeout", ClientConfiguration.DEFAULT_CONNECTION_TIMEOUT))
;
log_.info("Starting SNSPublisherManager in " + region_ + " with " + clientConfig.getMaxConnections() + " max connections...");
snsBuilder_
.withClientConfiguration(clientConfig)
;
}
}
// /**
// * Constructor.
// *
// * @param config The configuration provider.
// * @param nameFactory A name factory.
// * @param region The AWS region to use.
// * @param accountId The AWS numeric account ID
// */
// public SnsPublisherManager(IConfiguration config, INameFactory nameFactory, String region, String accountId)
// {
// this(config, nameFactory, region, accountId, null, false);
// }
//
// /**
// * Constructor.
// *
// * @param config The configuration provider.
// * @param nameFactory A name factory.
// * @param region The AWS region to use.
// * @param accountId The AWS numeric account ID
// * @param credentials AWS credentials.
// */
// public SnsPublisherManager(IConfiguration config, INameFactory nameFactory, String region, String accountId, AWSCredentialsProvider credentials)
// {
// this(config, nameFactory, region, accountId, credentials, false);
// }
//
// protected SnsPublisherManager(IConfiguration config, INameFactory nameFactory, String region, String accountId, AWSCredentialsProvider credentials, boolean initialize)
// {
// super(nameFactory, SnsPublisherManager.class);
//
// region_ = region;
// accountId_ = accountId;
// initialize_ = initialize;
//
// IConfiguration snsConfig = config.getConfiguration("org/symphonyoss/s2/fugue/aws/sns");
//
// ClientConfiguration clientConfig = new ClientConfiguration()
// .withMaxConnections( snsConfig.getInt( "maxConnections", ClientConfiguration.DEFAULT_MAX_CONNECTIONS))
// .withClientExecutionTimeout( snsConfig.getInt( "clientExecutionTimeout", ClientConfiguration.DEFAULT_CLIENT_EXECUTION_TIMEOUT))
// .withConnectionMaxIdleMillis( snsConfig.getLong("connectionMaxIdleMillis", ClientConfiguration.DEFAULT_CONNECTION_MAX_IDLE_MILLIS))
// .withConnectionTimeout( snsConfig.getInt( "connectionTimeout", ClientConfiguration.DEFAULT_CONNECTION_TIMEOUT))
// ;
//
// log_.info("Starting SNSPublisherManager in " + region_ + " with " + clientConfig.getMaxConnections() + " max connections...");
//
// AmazonSNSClientBuilder builder = AmazonSNSClientBuilder.standard()
// .withRegion(region_)
// .withClientConfiguration(clientConfig);
//
// if(credentials != null)
// {
// builder.withCredentials(credentials);
// }
//
// snsClient_ = builder.build();
//
// }
/**
* Topic-arns can be constructed if the region, accountId, and topic name is known.
*
* $topicArn = 'arn:aws:sns:REGION:ACCOUNT-ID:TOPIC-NAME'
*
* @param topicName - name of topic
*
* @return The topic ARN
*/
public String getTopicARN(TopicName topicName)
{
return "arn:aws:sns:" + region_ + ":" + accountId_ + ":" + topicName;
}
@Override
public void start()
{
}
/**
* Validate the given topic name.
*
* @param topicName The name of a topic.
*
* @return true if the topic is valid.
*/
protected abstract boolean validateTopic(TopicName topicName);
@Override
public void stop()
{
snsClient_.shutdown();
for(SnsPublisher publisher : publishers_)
{
publisher.close();
}
}
@Override
public synchronized IPublisher getPublisherByName(TopicName topicName)
{
SnsPublisher publisher = publisherNameMap_.get(topicName);
if(publisher == null)
{
throw new IllegalArgumentException("Unregistered topic \"" + topicName + "\"");
}
return publisher;
}
protected void send(String topicName, String topicArn, IPubSubMessage pubSubMessage, ITraceContext trace)
{
trace.trace("ABOUT-TO-PUBLISH", "SNS_TOPIC", topicName);
PublishRequest publishRequest = new PublishRequest(topicArn, pubSubMessage.getPayload());
if(!pubSubMessage.getAttributes().isEmpty())
{
Map messageAttributes = new HashMap<>();
for(Entry entry : pubSubMessage.getAttributes().entrySet())
{
messageAttributes.put(entry.getKey(), getAttribute(entry.getValue()));
}
publishRequest.withMessageAttributes(messageAttributes);
}
snsClient_.publish(publishRequest);
trace.trace("PUBLISHED", "SNS_TOPIC", topicName);
}
private static MessageAttributeValue getAttribute(Object value)
{
if(value instanceof Number || value instanceof IIntegerProvider)
{
return new MessageAttributeValue()
.withDataType("Number")
.withStringValue(value.toString());
}
return new MessageAttributeValue()
.withDataType("String")
.withStringValue(value.toString());
}
@Override
public int getMaximumMessageSize()
{
return MAX_MESSAGE_SIZE;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy