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

com.amazonaws.mobileconnectors.pinpoint.targeting.TargetingClient Maven / Gradle / Ivy

Go to download

The AWS Android SDK for Amazon Pinpoint module holds the client classes that are used for communicating with Amazon Pinpoint Service

There is a newer version: 2.77.0
Show newest version
/**
 * Copyright 2016-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file 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.amazonaws.mobileconnectors.pinpoint.targeting;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.mobileconnectors.pinpoint.PinpointManager;
import com.amazonaws.mobileconnectors.pinpoint.internal.core.PinpointContext;
import com.amazonaws.mobileconnectors.pinpoint.targeting.endpointProfile.EndpointProfile;
import com.amazonaws.services.pinpoint.model.EndpointDemographic;
import com.amazonaws.services.pinpoint.model.EndpointLocation;
import com.amazonaws.services.pinpoint.model.EndpointRequest;
import com.amazonaws.services.pinpoint.model.EndpointUser;
import com.amazonaws.services.pinpoint.model.UpdateEndpointRequest;
import com.amazonaws.util.DateUtils;
import com.amazonaws.util.StringUtils;
import com.amazonaws.util.VersionInfoUtils;

import static com.amazonaws.mobileconnectors.pinpoint.internal.core.util.Preconditions.checkNotNull;

/**
 * Client to manage updating the endpoint profile
 */
public class TargetingClient {
    private static final String USER_AGENT = PinpointManager.class.getName() + "/" + VersionInfoUtils.getVersion();

    private static final Log log = LogFactory.getLog(TargetingClient.class);
    private static final int MAX_EVENT_OPERATIONS = 1000;
    private static final String CUSTOM_ATTRIBUTES_KEY = "ENDPOINT_PROFILE_CUSTOM_ATTRIBUTES";
    private static final String CUSTOM_METRICS_KEY = "ENDPOINT_PROFILE_CUSTOM_METRICS";

    private final PinpointContext context;
    private final Map> globalAttributes;
    private final Map globalMetrics;
    private final ExecutorService endpointRunnableQueue;
    private final EndpointProfile endpointProfile;

    //For testing

    /**
     * Initializes a targetingClient used for testing only
     *
     * @param context  The {@link PinpointContext}
     * @param executor A thread pool executor
     */
    public TargetingClient(final PinpointContext context,
                           ExecutorService executor) {
        checkNotNull(context, "A valid pinpointContext must be provided");
        this.endpointRunnableQueue = executor;
        this.context = context;
        this.endpointProfile = new EndpointProfile(this.context);
        globalAttributes = loadAttributes();
        globalMetrics = loadMetrics();
    }

    /**
     * Initializes a client to manage updating the endpoint profile
     *
     * @param context The {@link PinpointContext}
     */
    public TargetingClient(final PinpointContext context) {
        this(context, new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(MAX_EVENT_OPERATIONS),
                                             new ThreadPoolExecutor.DiscardPolicy()));
    }

    /**
     * Returns the device endpoint profile.
     * TargetingClient attributes and Metrics are added to the endpoint profile.
     *
     * @return The current device endpoint profile
     */
    public EndpointProfile currentEndpoint() {
        // Add global  attributes.
        if (!this.globalAttributes.isEmpty()) {
            for (final Map.Entry> pair : globalAttributes.entrySet()) {
                this.endpointProfile.addAttribute(pair.getKey(), pair.getValue());
            }
        }

        if (!this.globalMetrics.isEmpty()) {
            for (final Map.Entry pair : globalMetrics.entrySet()) {
                this.endpointProfile.addMetric(pair.getKey(), pair.getValue());
            }
        }
        return this.endpointProfile;
    }

    /**
     * Register the current endpoint with the Pinpoint service.
     * TargetingClient attributes and Metrics are added to the endpoint profile.
     */
    public void updateEndpointProfile() {
        this.executeUpdate(this.currentEndpoint());
    }

    /**
     * Register the provided endpoint with the Pinpoint service.
     * TargetingClient attributes and Metrics are added to the endpoint profile.
     *
     * @param endpointProfile An instance of an EndpointProfile to be updated
     */
    @SuppressWarnings("checkstyle:hiddenfield")
    public void updateEndpointProfile(EndpointProfile endpointProfile) {
        // Add global  attributes.
        if (!this.globalAttributes.isEmpty()) {
            for (final Map.Entry> pair : globalAttributes.entrySet()) {
                endpointProfile.addAttribute(pair.getKey(), pair.getValue());
            }
        }

        if (!this.globalMetrics.isEmpty()) {
            for (final Map.Entry pair : globalMetrics.entrySet()) {
                endpointProfile.addMetric(pair.getKey(), pair.getValue());
            }
        }
        this.executeUpdate(endpointProfile);
    }

    @SuppressWarnings("checkstyle:hiddenfield")
    private void executeUpdate(EndpointProfile endpointProfile) {
        if (endpointProfile == null) {
            log.error("EndpointProfile is null, failed to update profile.");
            return;
        }

        final EndpointDemographic demographic = new EndpointDemographic()
            .withAppVersion(endpointProfile.getDemographic().getAppVersion())
            .withLocale(endpointProfile.getDemographic().getLocale().toString())
            .withTimezone(endpointProfile.getDemographic().getTimezone())
            .withMake(endpointProfile.getDemographic().getMake())
            .withModel(endpointProfile.getDemographic().getModel())
            .withPlatform(endpointProfile.getDemographic().getPlatform())
            .withPlatformVersion(endpointProfile.getDemographic().getPlatformVersion());

        final EndpointLocation location = new EndpointLocation()
            .withLatitude(endpointProfile.getLocation().getLatitude())
            .withLongitude(endpointProfile.getLocation().getLongitude())
            .withPostalCode(endpointProfile.getLocation().getPostalCode())
            .withCity(endpointProfile.getLocation().getCity())
            .withRegion(endpointProfile.getLocation().getRegion())
            .withCountry(endpointProfile.getLocation().getCountry());

        final EndpointUser user;
        if (endpointProfile.getUser().getUserId() == null) {
            user = null;
        } else {
            user = new EndpointUser();
            user.setUserId(endpointProfile.getUser().getUserId());
        }

        final EndpointRequest endpointRequest = new EndpointRequest().withChannelType(endpointProfile.getChannelType())
                                                                     .withAddress(endpointProfile.getAddress())
                                                                     .withLocation(location)
                                                                     .withDemographic(demographic)
                                                                     .withEffectiveDate(DateUtils.formatISO8601Date(
                                                                         new Date(endpointProfile.getEffectiveDate())))
                                                                     .withOptOut(endpointProfile.getOptOut())
                                                                     .withAttributes(endpointProfile.getAllAttributes())
                                                                     .withMetrics(endpointProfile.getAllMetrics())
                                                                     .withUser(user);
        final UpdateEndpointRequest updateEndpointRequest = new UpdateEndpointRequest().withApplicationId(
            endpointProfile.getApplicationId()).withEndpointId(endpointProfile.getEndpointId()).withEndpointRequest(endpointRequest);

        updateEndpointRequest.getRequestClientOptions().appendUserAgent(USER_AGENT);

        endpointRunnableQueue.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    log.info("Updating EndpointProfile.");
                    context.getPinpointServiceClient().updateEndpoint(updateEndpointRequest);
                    log.info("EndpointProfile updated successfully.");
                } catch (final AmazonServiceException e) {
                    log.error("AmazonServiceException occurred during endpoint update:", e);
                } catch (final AmazonClientException e) {
                    log.info("AmazonClientException occurred during endpoint update:", e);
                }
            }
        });
    }

    private void saveAttributes() {
        final JSONObject jsonObject = new JSONObject(globalAttributes);
        final String jsonString = jsonObject.toString();
        this.context.getSystem().getPreferences().putString(CUSTOM_ATTRIBUTES_KEY, jsonString);
    }

    private Map> loadAttributes() {
        final Map> outputMap = new ConcurrentHashMap>();
        final String jsonString = this.context.getSystem().getPreferences().getString(CUSTOM_ATTRIBUTES_KEY, null);
        if (StringUtils.isBlank(jsonString)) {
            return outputMap;
        }
        try {
            final JSONObject jsonObject = new JSONObject(jsonString);
            final Iterator keysItr = jsonObject.keys();
            while (keysItr.hasNext()) {
                final String key = keysItr.next();
                final JSONArray jsonArray = jsonObject.getJSONArray(key);
                final List listValues = new ArrayList();
                for (int i = 0; i < jsonArray.length(); i++) {
                    listValues.add(jsonArray.getString(i));
                }
                outputMap.put(key, listValues);
            }
        } catch (final JSONException e) {
            e.printStackTrace();
        }
        return outputMap;
    }

    private void saveMetrics() {
        final JSONObject jsonObject = new JSONObject(globalMetrics);
        final String jsonString = jsonObject.toString();
        this.context.getSystem().getPreferences().putString(CUSTOM_METRICS_KEY, jsonString);
    }

    private Map loadMetrics() {
        final Map outputMap = new ConcurrentHashMap();
        final String jsonString = this.context.getSystem().getPreferences().getString(CUSTOM_METRICS_KEY, null);
        if (StringUtils.isBlank(jsonString)) {
            return outputMap;
        }
        try {
            final JSONObject jsonObject = new JSONObject(jsonString);
            final Iterator keysItr = jsonObject.keys();
            while (keysItr.hasNext()) {
                final String key = keysItr.next();
                final Double value = jsonObject.getDouble(key);
                outputMap.put(key, value);
            }
        } catch (final JSONException e) {
            e.printStackTrace();
        }
        return outputMap;
    }

    /**
     * Adds the specified attribute to the current endpoint profile generated by this client.
     * Note: The maximum allowed attributes/metrics is 20. Attempts to add more may be ignored
     *
     * @param attributeName   the name of the  attribute to add
     * @param attributeValues the value of the  attribute
     */
    public void addAttribute(final String attributeName, final List attributeValues) {
        if (attributeName == null) {
            log.debug("Null attribute name provided to addGlobalAttribute.");
            return;
        }

        if (attributeValues == null) {
            log.debug("Null attribute values provided to addGlobalAttribute.");
            return;
        }
        globalAttributes.put(attributeName, attributeValues);
        saveAttributes();
    }

    /**
     * Removes the specified attribute. All subsequently created events will no
     * longer have this global attribute. from the current endpoint profile generated by this client.
     *
     * @param attributeName the name of the attribute to remove
     */
    public void removeAttribute(final String attributeName) {
        if (attributeName == null) {
            log.warn("Null attribute name provided to removeGlobalAttribute.");
            return;
        }
        this.endpointProfile.addAttribute(attributeName, null);
        globalAttributes.remove(attributeName);
        saveAttributes();
    }

    /**
     * Adds the specified metric to the current endpoint profile generated by this client. Note: The
     * maximum allowed attributes and metrics on an endpoint update is 20. Attempts
     * to add more may be ignored
     *
     * @param metricName  the name of the metric to add
     * @param metricValue the value of the metric
     */
    public void addMetric(String metricName, Double metricValue) {
        if (metricName == null) {
            log.warn("Null metric name provided to addGlobalMetric.");
            return;
        }

        if (metricValue == null) {
            log.warn("Null metric value provided to addGlobalMetric.");
            return;
        }

        globalMetrics.put(metricName, metricValue);
        saveMetrics();
    }

    /**
     * Removes the specified metric from the current endpoint profile generated by this client.
     *
     * @param metricName the name of the metric to remove
     */
    public void removeMetric(String metricName) {
        if (metricName == null) {
            log.warn("Null metric name provided to removeGlobalMetric.");
            return;
        }
        this.endpointProfile.addMetric(metricName, null);
        globalMetrics.remove(metricName);
        saveMetrics();
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy