org.dasein.cloud.aws.platform.SNS.java Source code

Java tutorial

Introduction

Here is the source code for org.dasein.cloud.aws.platform.SNS.java

Source

/**
 * Copyright (C) 2009-2015 Dell, Inc.
 * See annotations for authorship information
 *
 * ====================================================================
 * 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.dasein.cloud.aws.platform;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Locale;
import java.util.Map;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.apache.http.HttpStatus;
import org.apache.log4j.Logger;
import org.dasein.cloud.CloudException;
import org.dasein.cloud.DataFormat;
import org.dasein.cloud.InternalException;
import org.dasein.cloud.ProviderContext;
import org.dasein.cloud.ResourceStatus;
import org.dasein.cloud.aws.AWSCloud;
import org.dasein.cloud.aws.compute.EC2Exception;
import org.dasein.cloud.aws.compute.EC2Method;
import org.dasein.cloud.identity.ServiceAction;
import org.dasein.cloud.platform.*;
import org.dasein.cloud.util.APITrace;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * AWS SNS Support
 *
 * @author George Reese
 * @author Stas Maksimov
 * @version 2014.08 deprecated methods moved to capabilities
 * @since ?
 */
public class SNS implements PushNotificationSupport {
    static private final Logger logger = AWSCloud.getLogger(SNS.class);

    static public final String SERVICE_ID = "sns";

    static public final String CONFIRM_SUBSCRIPTION = "ConfirmSubscription";
    static public final String CREATE_TOPIC = "CreateTopic";
    static public final String DELETE_TOPIC = "DeleteTopic";
    static public final String GET_TOPIC_ATTRIBUTES = "GetTopicAttributes";
    static public final String LIST_SUBSCRIPTIONS = "ListSubscriptions";
    static public final String LIST_SUBSCRIPTIONS_BY_TOPIC = "ListSubscriptionsByTopic";
    static public final String LIST_TOPICS = "ListTopics";
    static public final String PUBLISH = "Publish";
    static public final String SET_TOPIC_ATTRIBUTES = "SetTopicAttributes";
    static public final String SUBSCRIBE = "Subscribe";
    static public final String UNSUBSCRIBE = "Unsubscribe";

    private volatile transient SNSCapabilities capabilities;

    static public @Nonnull ServiceAction[] asSNSServiceAction(@Nonnull String action) {
        if (action.equals(CREATE_TOPIC)) {
            return new ServiceAction[] { PushNotificationSupport.CREATE_TOPIC };
        } else if (action.equals(DELETE_TOPIC)) {
            return new ServiceAction[] { PushNotificationSupport.REMOVE_TOPIC };
        } else if (action.equals(LIST_TOPICS)) {
            return new ServiceAction[] { PushNotificationSupport.GET_TOPIC, PushNotificationSupport.LIST_TOPIC };
        } else if (action.equals(PUBLISH)) {
            return new ServiceAction[] { PushNotificationSupport.PUBLISH };
        } else if (action.equals(SUBSCRIBE)) {
            return new ServiceAction[] { PushNotificationSupport.SUBSCRIBE };
        }
        return new ServiceAction[0];
    }

    private AWSCloud provider = null;

    SNS(AWSCloud provider) {
        this.provider = provider;
    }

    @Override
    public String confirmSubscription(String providerTopicId, String token, boolean authenticateUnsubscribe)
            throws CloudException, InternalException {
        APITrace.begin(provider, "Notifications.confirmSubscription");
        try {
            Map<String, String> parameters = provider.getStandardSnsParameters(provider.getContext(),
                    CONFIRM_SUBSCRIPTION);
            EC2Method method;
            NodeList blocks;
            Document doc;

            parameters.put("TopicArn", providerTopicId);
            parameters.put("Token", token);
            if (authenticateUnsubscribe) {
                parameters.put("AuthenticateOnUnsubscribe", "true");
            }
            method = new EC2Method(SERVICE_ID, provider, parameters);
            try {
                doc = method.invoke();
            } catch (EC2Exception e) {
                throw new CloudException(e);
            }
            blocks = doc.getElementsByTagName("ConfirmSubscriptionResult");
            for (int i = 0; i < blocks.getLength(); i++) {
                Node item = blocks.item(i);

                for (int j = 0; j < item.getChildNodes().getLength(); j++) {
                    Node attr = item.getChildNodes().item(j);
                    String name;

                    name = attr.getNodeName();
                    if (name.equals("SubscriptionArn")) {
                        return attr.getFirstChild().getNodeValue().trim();
                    }
                }
            }
            return null;
        } finally {
            APITrace.end();
        }
    }

    @Override
    public Topic createTopic(String name) throws CloudException, InternalException {
        APITrace.begin(provider, "Notifications.createTopic");
        try {
            Map<String, String> parameters = provider.getStandardSnsParameters(provider.getContext(), CREATE_TOPIC);
            Topic topic = null;
            EC2Method method;
            NodeList blocks;
            Document doc;

            parameters.put("Name", name);
            method = new EC2Method(SERVICE_ID, provider, parameters);
            try {
                doc = method.invoke();
            } catch (EC2Exception e) {
                throw new CloudException(e);
            }
            blocks = doc.getElementsByTagName("CreateTopicResult");
            for (int i = 0; i < blocks.getLength(); i++) {
                Node item = blocks.item(i);

                topic = toTopic(item);
                if (topic != null) {
                    try {
                        setTopicAttribute(topic, "DisplayName", name);
                    } catch (Throwable t) {
                        logger.warn("Unable to set DisplayName for " + name + " [#" + topic.getProviderTopicId()
                                + "]: " + t.getMessage());
                    }
                    topic.setName(name);
                    return topic;
                }
            }
            return topic;
        } finally {
            APITrace.end();
        }
    }

    @Override
    @Deprecated
    public String getProviderTermForSubscription(Locale locale) {
        try {
            return getCapabilities().getProviderTermForSubscription(locale);
        } catch (InternalException e) {
        } catch (CloudException e) {
        }
        return "subscription"; // legacy
    }

    @Override
    @Deprecated
    public String getProviderTermForTopic(Locale locale) {
        try {
            return getCapabilities().getProviderTermForTopic(locale);
        } catch (InternalException e) {
        } catch (CloudException e) {
        }
        return "topic"; // legacy
    }

    @Override
    public @Nonnull PushNotificationCapabilities getCapabilities() throws InternalException, CloudException {
        if (capabilities == null) {
            capabilities = new SNSCapabilities(provider);
        }
        return capabilities;
    }

    public String getSNSUrl() throws InternalException, CloudException {
        return ("https://sns." + provider.getContext().getRegionId() + ".amazonaws.com");
    }

    @Override
    public boolean isSubscribed() throws CloudException, InternalException {
        APITrace.begin(provider, "Notifications.isSubscribed");
        try {
            Map<String, String> parameters = provider.getStandardSnsParameters(provider.getContext(), LIST_TOPICS);
            EC2Method method;

            method = new EC2Method(SERVICE_ID, provider, parameters);
            try {
                method.invoke();
                return true;
            } catch (EC2Exception e) {
                if (e.getStatus() == HttpStatus.SC_UNAUTHORIZED || e.getStatus() == HttpStatus.SC_FORBIDDEN) {
                    return false;
                }
                String code = e.getCode();

                if (code != null && (code.equals("SubscriptionCheckFailed") || code.equals("AuthFailure")
                        || code.equals("SignatureDoesNotMatch") || code.equals("InvalidClientTokenId")
                        || code.equals("OptInRequired"))) {
                    return false;
                }
                logger.warn(e.getSummary());
                if (logger.isDebugEnabled()) {
                    e.printStackTrace();
                }
                throw new CloudException(e);
            }
        } finally {
            APITrace.end();
        }
    }

    @Override
    public Collection<Subscription> listSubscriptions(String optionalTopicId)
            throws CloudException, InternalException {
        APITrace.begin(provider, "Notifications.listSubscriptions");
        try {
            Map<String, String> parameters = provider.getStandardSnsParameters(provider.getContext(),
                    optionalTopicId == null ? LIST_SUBSCRIPTIONS : LIST_SUBSCRIPTIONS_BY_TOPIC);
            ArrayList<Subscription> list = new ArrayList<Subscription>();
            EC2Method method;
            NodeList blocks;
            Document doc;

            if (optionalTopicId != null) {
                parameters.put("TopicArn", optionalTopicId);
            }
            method = new EC2Method(SERVICE_ID, provider, parameters);
            try {
                doc = method.invoke();
            } catch (EC2Exception e) {
                throw new CloudException(e);
            }
            blocks = doc.getElementsByTagName("Subscriptions");
            for (int i = 0; i < blocks.getLength(); i++) {
                NodeList items = blocks.item(i).getChildNodes();

                for (int j = 0; j < items.getLength(); j++) {
                    Node item = items.item(j);

                    if (item.getNodeName().equals("member")) {
                        Subscription subscription = toSubscription(item);

                        if (subscription != null) {
                            list.add(subscription);
                        }
                    }
                }
            }
            return list;
        } finally {
            APITrace.end();
        }
    }

    @Override
    public @Nonnull Iterable<ResourceStatus> listTopicStatus() throws CloudException, InternalException {
        APITrace.begin(provider, "Notifications.listTopicStatus");
        try {
            Map<String, String> parameters = provider.getStandardSnsParameters(provider.getContext(), LIST_TOPICS);
            ArrayList<ResourceStatus> list = new ArrayList<ResourceStatus>();
            EC2Method method;
            NodeList blocks;
            Document doc;

            method = new EC2Method(SERVICE_ID, provider, parameters);
            try {
                doc = method.invoke();
            } catch (EC2Exception e) {
                throw new CloudException(e);
            }
            blocks = doc.getElementsByTagName("Topics");
            for (int i = 0; i < blocks.getLength(); i++) {
                NodeList items = blocks.item(i).getChildNodes();

                for (int j = 0; j < items.getLength(); j++) {
                    Node item = items.item(j);

                    if (item.getNodeName().equals("member")) {
                        ResourceStatus status = toStatus(item);

                        if (status != null) {
                            list.add(status);
                        }
                    }
                }
            }
            return list;
        } finally {
            APITrace.end();
        }
    }

    @Override
    public Collection<Topic> listTopics() throws CloudException, InternalException {
        APITrace.begin(provider, "Notifications.listTopics");
        try {
            Map<String, String> parameters = provider.getStandardSnsParameters(provider.getContext(), LIST_TOPICS);
            ArrayList<Topic> list = new ArrayList<Topic>();
            EC2Method method;
            NodeList blocks;
            Document doc;

            method = new EC2Method(SERVICE_ID, provider, parameters);
            try {
                doc = method.invoke();
            } catch (EC2Exception e) {
                throw new CloudException(e);
            }
            blocks = doc.getElementsByTagName("Topics");
            for (int i = 0; i < blocks.getLength(); i++) {
                NodeList items = blocks.item(i).getChildNodes();

                for (int j = 0; j < items.getLength(); j++) {
                    Node item = items.item(j);

                    if (item.getNodeName().equals("member")) {
                        Topic topic = toTopic(item);

                        if (topic != null) {
                            list.add(topic);
                        }
                    }
                }
            }
            return list;
        } finally {
            APITrace.end();
        }
    }

    @Override
    public @Nonnull String[] mapServiceAction(@Nonnull ServiceAction action) {
        return new String[0]; // TODO: implement me
    }

    @Override
    public String publish(String providerTopicId, String subject, String message)
            throws CloudException, InternalException {
        APITrace.begin(provider, "Notifications.publish");
        try {
            Map<String, String> parameters = provider.getStandardSnsParameters(provider.getContext(), PUBLISH);
            EC2Method method;
            NodeList blocks;
            Document doc;

            parameters.put("TopicArn", providerTopicId);
            parameters.put("Subject", subject);
            parameters.put("Message", message);
            method = new EC2Method(SERVICE_ID, provider, parameters);
            try {
                doc = method.invoke();
            } catch (EC2Exception e) {
                throw new CloudException(e);
            }
            blocks = doc.getElementsByTagName("PublishResult");
            for (int i = 0; i < blocks.getLength(); i++) {
                Node item = blocks.item(i);

                for (int j = 0; j < item.getChildNodes().getLength(); j++) {
                    Node attr = item.getChildNodes().item(j);
                    String name;

                    name = attr.getNodeName();
                    if (name.equals("MessageId")) {
                        return attr.getFirstChild().getNodeValue().trim();
                    }
                }
            }
            return null;
        } finally {
            APITrace.end();
        }
    }

    @Override
    public void removeTopic(String providerTopicId) throws CloudException, InternalException {
        APITrace.begin(provider, "Notifications.removeTopic");
        try {
            Map<String, String> parameters = provider.getStandardSnsParameters(provider.getContext(), DELETE_TOPIC);
            EC2Method method;

            parameters.put("TopicArn", providerTopicId);
            method = new EC2Method(SERVICE_ID, provider, parameters);
            try {
                method.invoke();
            } catch (EC2Exception e) {
                throw new CloudException(e);
            }
        } finally {
            APITrace.end();
        }
    }

    @Override
    public @Nullable Topic getTopic(@Nonnull String providerTopicId) throws CloudException, InternalException {
        try {
            Topic topic = new Topic();
            topic.setProviderTopicId(providerTopicId);
            setTopicAttributes(topic);
            return topic;
        } catch (CloudException e) {
            Throwable cause = e.getCause();
            if (cause instanceof EC2Exception) {
                String code = ((EC2Exception) cause).getCode();
                if ("NotFound".equals(code) || "InvalidParameter".equals(code)) {
                    return null;
                }

            }
            throw e;
        }
    }

    private void setTopicAttribute(Topic topic, final String attributeName, final String attributeValue)
            throws InternalException, CloudException {
        APITrace.begin(provider, "Notifications.setTopicAttributes");
        try {
            Map<String, String> parameters = provider.getStandardSnsParameters(provider.getContext(),
                    SET_TOPIC_ATTRIBUTES);
            EC2Method method;

            parameters.put("TopicArn", topic.getProviderTopicId());
            parameters.put("AttributeName", attributeName);
            parameters.put("AttributeValue", attributeValue);

            method = new EC2Method(SERVICE_ID, provider, parameters);
            try {
                method.invoke();
            } catch (EC2Exception e) {
                throw new CloudException(e);
            }
        } finally {
            APITrace.end();
        }
    }

    private void setTopicAttributes(Topic topic) throws InternalException, CloudException {
        ProviderContext ctx = provider.getContext();

        if (ctx == null) {
            throw new CloudException("No context was set for this request");
        }
        APITrace.begin(provider, "Notifications.getTopicAttributes");
        try {
            topic.setProviderRegionId(ctx.getRegionId());
            topic.setProviderOwnerId(ctx.getAccountNumber());
            topic.setActive(true);

            Map<String, String> parameters = provider.getStandardSnsParameters(provider.getContext(),
                    GET_TOPIC_ATTRIBUTES);
            EC2Method method;
            NodeList blocks;
            Document doc;

            parameters.put("TopicArn", topic.getProviderTopicId());
            method = new EC2Method(SERVICE_ID, provider, parameters);
            try {
                doc = method.invoke();
            } catch (EC2Exception e) {
                throw new CloudException(e);
            }
            blocks = doc.getElementsByTagName("Attributes");
            for (int i = 0; i < blocks.getLength(); i++) {
                NodeList items = blocks.item(i).getChildNodes();

                for (int j = 0; j < items.getLength(); j++) {
                    Node item = items.item(j);

                    if (item.getNodeName().equals("entry")) {
                        NodeList parts = item.getChildNodes();
                        String name = null;
                        String value = null;

                        if (parts != null) {
                            for (int k = 0; k < parts.getLength(); k++) {
                                Node node = parts.item(k);

                                if (node != null) {
                                    if (node.getNodeName().equals("key")) {
                                        name = node.getFirstChild().getNodeValue().trim();
                                    } else if (node.getNodeName().equals("value")) {
                                        if (node.getFirstChild() != null) {
                                            value = node.getFirstChild().getNodeValue().trim();
                                        } else {
                                            value = node.getNodeValue();
                                        }
                                    }
                                }
                            }
                        }
                        if (name != null) {
                            if (name.equals("DisplayName")) {
                                if (value != null) {
                                    topic.setName(value);
                                    topic.setDescription(value + " (" + topic.getProviderTopicId() + ")");
                                }
                            } else if (name.equals("Owner") && value != null) {
                                topic.setProviderOwnerId(value);
                            }
                        }
                    }
                }
            }
            if (topic.getName() == null) {
                String id = topic.getProviderTopicId();
                int idx = id.lastIndexOf(":");

                if (idx > 0 && idx < id.length() - 1) {
                    id = id.substring(idx + 1);
                }
                topic.setName(id);
            }
            if (topic.getDescription() == null) {
                topic.setDescription(topic.getName() + " (" + topic.getProviderTopicId() + ")");
            }
        } finally {
            APITrace.end();
        }
    }

    @Override
    public void subscribe(String providerTopicId, EndpointType endpointType, DataFormat dataFormat, String endpoint)
            throws CloudException, InternalException {
        APITrace.begin(provider, "Notifications.subscribe");
        try {
            Map<String, String> parameters = provider.getStandardSnsParameters(provider.getContext(), SUBSCRIBE);
            EC2Method method;

            parameters.put("TopicArn", providerTopicId);
            switch (endpointType) {
            case HTTP:
                parameters.put("Protocol", "http");
                break;
            case HTTPS:
                parameters.put("Protocol", "https");
                break;
            case EMAIL:
                if (dataFormat.equals(DataFormat.JSON)) {
                    parameters.put("Protocol", "email-json");
                } else {
                    parameters.put("Protocol", "email");
                }
                break;
            case AWS_SQS:
                parameters.put("Protocol", "sqs");
                break;
            case SMS:
                parameters.put("Protocol", "sms");
                break;
            }
            parameters.put("Endpoint", endpoint);
            method = new EC2Method(SERVICE_ID, provider, parameters);
            try {
                method.invoke();
            } catch (EC2Exception e) {
                throw new CloudException(e);
            }
        } finally {
            APITrace.end();
        }
    }

    private @Nullable ResourceStatus toStatus(@Nullable Node fromAws) throws InternalException, CloudException {
        if (fromAws == null) {
            return null;
        }
        NodeList attrs = fromAws.getChildNodes();
        String topicId = null;

        for (int i = 0; i < attrs.getLength(); i++) {
            Node attr = attrs.item(i);
            String name;

            name = attr.getNodeName();
            if (name.equals("TopicArn")) {
                topicId = attr.getFirstChild().getNodeValue().trim();
            }
        }
        return new ResourceStatus(topicId, true);
    }

    private Subscription toSubscription(Node fromAws) throws InternalException, CloudException {
        Subscription subscription = new Subscription();
        NodeList attrs = fromAws.getChildNodes();

        subscription.setProviderRegionId(provider.getContext().getRegionId());
        for (int i = 0; i < attrs.getLength(); i++) {
            Node attr = attrs.item(i);
            String name;

            name = attr.getNodeName();
            if (name.equals("TopicArn")) {
                String id = attr.getFirstChild().getNodeValue().trim();

                subscription.setProviderTopicId(id);
            } else if (name.equals("SubscriptionArn")) {
                String id = attr.getFirstChild().getNodeValue().trim();

                subscription.setProviderSubscriptionId(id);
                subscription.setDescription(id);
                subscription.setName(id);
            } else if (name.equals("Owner")) {
                String id = attr.getFirstChild().getNodeValue().trim();

                subscription.setProviderOwnerId(id);
            } else if (name.equals("Endpoint")) {
                String endpoint = attr.getFirstChild().getNodeValue().trim();

                subscription.setEndpoint(endpoint);
            } else if (name.equals("Protocol")) {
                String proto = attr.getFirstChild().getNodeValue().trim();

                if (proto != null) {
                    if (proto.equals("email")) {
                        subscription.setEndpointType(EndpointType.EMAIL);
                        subscription.setDataFormat(DataFormat.PLAINTEXT);
                    } else if (proto.equals("email-json")) {
                        subscription.setEndpointType(EndpointType.EMAIL);
                        subscription.setDataFormat(DataFormat.JSON);
                    } else if (proto.equals("http")) {
                        subscription.setEndpointType(EndpointType.HTTP);
                        subscription.setDataFormat(DataFormat.JSON);
                    } else if (proto.equals("https")) {
                        subscription.setEndpointType(EndpointType.HTTPS);
                        subscription.setDataFormat(DataFormat.JSON);
                    } else if (proto.equals("sqs")) {
                        subscription.setEndpointType(EndpointType.AWS_SQS);
                        subscription.setDataFormat(DataFormat.JSON);
                    } else if (proto.equals("sms")) {
                        subscription.setEndpointType(EndpointType.SMS);
                        subscription.setDataFormat(DataFormat.PLAINTEXT);
                    }
                }
            }
        }
        if (subscription.getProviderSubscriptionId() == null) {
            return null;
        }
        return subscription;
    }

    private Topic toTopic(Node fromAws) throws InternalException, CloudException {
        NodeList attrs = fromAws.getChildNodes();
        Topic topic = new Topic();

        topic.setProviderOwnerId(provider.getContext().getAccountNumber());
        topic.setProviderRegionId(provider.getContext().getRegionId());
        topic.setActive(true);
        for (int i = 0; i < attrs.getLength(); i++) {
            Node attr = attrs.item(i);
            String name;

            name = attr.getNodeName();
            if (name.equals("TopicArn")) {
                String id = attr.getFirstChild().getNodeValue().trim();

                topic.setProviderTopicId(id);
            }
        }
        if (topic.getProviderTopicId() == null) {
            return null;
        }
        setTopicAttributes(topic);
        return topic;
    }

    @Override
    public void unsubscribe(String providerSubscriptionId) throws CloudException, InternalException {
        APITrace.begin(provider, "Notifications.unsubscribe");
        try {
            Map<String, String> parameters = provider.getStandardSnsParameters(provider.getContext(), UNSUBSCRIBE);
            EC2Method method;

            parameters.put("SubscriptionArn", providerSubscriptionId);
            method = new EC2Method(SERVICE_ID, provider, parameters);
            try {
                method.invoke();
            } catch (EC2Exception e) {
                throw new CloudException(e);
            }
        } finally {
            APITrace.end();
        }
    }
}