com.amazonaws.mobileconnectors.pinpoint.targeting.TargetingClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aws-android-sdk-pinpoint Show documentation
Show all versions of aws-android-sdk-pinpoint Show documentation
The AWS Android SDK for Amazon Pinpoint module holds the client classes that are used for communicating with Amazon Pinpoint Service
/**
* 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();
}
}