Skip to content

Instantly share code, notes, and snippets.

@billy-bacon
Created November 25, 2013 18:31
Show Gist options
  • Save billy-bacon/7646196 to your computer and use it in GitHub Desktop.
Save billy-bacon/7646196 to your computer and use it in GitHub Desktop.
package com.alex.integration.processor;
import com.alex.integration.bean.PatronDevice;
import com.alex.integration.bean.Platform;
import com.alex.integration.bean.PushNotificationMessage;
import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.services.sns.AmazonSNS;
import com.amazonaws.services.sns.model.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
@Component("pushNotificationMessageProcessor")
public class PushNotificationMessageProcessor {
private static final Logger log = LoggerFactory.getLogger(PushNotificationMessageProcessor.class);
// This message is delivered if a platform specific message is not
// specified for the end point. It must be set. It is received by the device
// as the value of the key "default".
private static final String defaultMessage = "WTF";
private static final String applicationName = "hoopla_digital";
private final AmazonSNS snsClient;
private final String serverApiKey;
// (ANDROID) not applicable; this will be null;
// (iOS) the certificate in pem format with \n at the end of each line.
private String principal;
// (ANDROID) the server api key from the google api console
// (iOS) the private key in pem format with \n at the end of each line.
private String credential;
@Autowired
public PushNotificationMessageProcessor(final AmazonSNS snsClient,
@Value("${GOOGLE_SERVER_API_KEY}") final String serverApiKey,
@Value("${APPLE_CERT_FILE}") final String appleCertFile,
@Value("${APPLE_PRIVATE_KEY_FILE}") final String applePrivateKeyFile) {
this.snsClient = snsClient;
this.serverApiKey = serverApiKey;
principal = loadFileFromClasspath(appleCertFile);
credential = loadFileFromClasspath(appleCertFile);
}
/**
* Method which will publish a push notification for the given patron device object.
*
* @param patronDevice
*/
public void processNotification(final PatronDevice patronDevice) {
//todo: find out what to do
snsClient.setEndpoint("https://sns.us-west-2.amazonaws.com");
log.info("start processing push notifications...");
// todo: Use a READ-ONLY data source to postgres for retrieving remaining circs for the month
// retrieve the Account associated to the given patrons library to determine the library max circs per month;
// retrieve patrons circs they've accumulated this calendar month.
// The difference is what they have left.
final Integer patronId = patronDevice.getPatronId();
final String deviceId = patronDevice.getDeviceId();
final Platform platform = patronDevice.getPlatform();
log.info("patronId: {}", patronDevice.getPatronId());
log.info("deviceId: {}", patronDevice.getDeviceId());
log.info("platform: {}", patronDevice.getPlatform());
try {
PushNotificationMessage notification;
switch (platform) {
case GCM:
notification = new PushNotificationMessage(patronDevice.getDeviceId(), defaultMessage);
sendAndroidAppNotification(notification);
break;
case APNS:
notification = new PushNotificationMessage(platform, patronDevice.getDeviceId(), defaultMessage);
sendAppleAppNotification(notification);
break;
case APNS_SANDBOX:
notification = new PushNotificationMessage(platform, patronDevice.getDeviceId(), defaultMessage);
sendAppleAppNotification(notification);
break;
case ADM:
throw new UnsupportedOperationException("Platform not supported");
}
} catch (AmazonServiceException ase) {
log.error("Caught an AmazonServiceException, which means your request made it "
+ "to Amazon SNS, but was rejected with an error response for some reason.");
log.error("Error Message: " + ase.getMessage());
log.error("HTTP Status Code: " + ase.getStatusCode());
log.error("AWS Error Code: " + ase.getErrorCode());
log.error("Error Type: " + ase.getErrorType());
log.error("Request ID: " + ase.getRequestId());
} catch (AmazonClientException ace) {
log.error("Caught an AmazonClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with SNS, such as not "
+ "being able to access the network.");
log.error("Error Message: " + ace.getMessage());
} catch (Throwable t) {
log.error("Error thrown: ", t);
}
log.info("Done processing notification for deviceId {}", deviceId);
}
public void sendAndroidAppNotification(final PushNotificationMessage notification) throws JsonProcessingException {
// we clear the principal here since it doesn't apply for android push notifications and SNS will fail if we send it anything but blank.
principal = "";
sendNotification(Platform.GCM, null, serverApiKey, notification.getDeviceToken(), notification.getMessage());
}
public void sendAppleAppNotification(final PushNotificationMessage notification) throws JsonProcessingException {
sendNotification(notification.getPlatform(), principal, credential, notification.getDeviceToken(), notification.getMessage());
}
private void sendNotification(final Platform platform,
final String principal,
final String credential,
final String platformToken,
final String message) throws JsonProcessingException {
// Create Platform Application. This corresponds to an app on a platform.
CreatePlatformApplicationResult platformApplicationResult = createPlatformApplication(applicationName, platform, principal, credential);
log.info("platformApplicationResult: {}", platformApplicationResult.toString());
// The Platform Application Arn can be used to uniquely identify the Platform Application.
String platformApplicationArn = platformApplicationResult.getPlatformApplicationArn();
// Create an Endpoint. This corresponds to an app on a device.
CreatePlatformEndpointResult platformEndpointResult = createPlatformEndpoint(
"CustomData - Useful to store endpoint specific data", platformToken, platformApplicationArn);
log.info(platformEndpointResult.toString());
// Publish a push notification to an Endpoint.
PublishResult publishResult = publish(platformEndpointResult.getEndpointArn(), platform, message);
log.info("Published. MessageId=" + publishResult.getMessageId());
// Delete the Platform Application since we will no longer be using it.
deletePlatformApplication(platformApplicationArn);
}
private CreatePlatformApplicationResult createPlatformApplication(String applicationName, Platform platform, String principal, String credential) {
CreatePlatformApplicationRequest platformApplicationRequest = new CreatePlatformApplicationRequest();
Map<String, String> attributes = new HashMap<String, String>();
attributes.put("PlatformPrincipal", principal);
attributes.put("PlatformCredential", credential);
platformApplicationRequest.setAttributes(attributes);
platformApplicationRequest.setName(applicationName);
platformApplicationRequest.setPlatform(platform.name());
return snsClient.createPlatformApplication(platformApplicationRequest);
}
private CreatePlatformEndpointResult createPlatformEndpoint(String customData, String platformToken, String applicationArn) {
CreatePlatformEndpointRequest platformEndpointRequest = new CreatePlatformEndpointRequest();
platformEndpointRequest.setCustomUserData(customData);
platformEndpointRequest.setToken(platformToken);
platformEndpointRequest.setPlatformApplicationArn(applicationArn);
return snsClient.createPlatformEndpoint(platformEndpointRequest);
}
private void deletePlatformApplication(String applicationArn) {
DeletePlatformApplicationRequest request = new DeletePlatformApplicationRequest();
request.setPlatformApplicationArn(applicationArn);
snsClient.deletePlatformApplication(request);
}
private PublishResult publish(final String endpointArn,
final Platform platform,
final String message) throws JsonProcessingException {
PublishRequest publishRequest = new PublishRequest();
Map<String, String> messageMap = new HashMap<String, String>();
messageMap.put("default", message);
messageMap.put(platform.name(), getPlatformMessage(platform, message));
// For direct publish to mobile end points, topicArn is not relevant.
publishRequest.setTargetArn(endpointArn);
publishRequest.setMessageStructure("json");
String jsonMessage = jsonify(messageMap);
// Display the message that will be sent to the endpoint/
log.info("jsonMessage: {}", jsonMessage);
publishRequest.setMessage(jsonMessage);
return snsClient.publish(publishRequest);
}
private String getAppleMessage(final String message) throws JsonProcessingException {
Map<String, Object> appleMessageMap = new HashMap<String, Object>();
Map<String, Object> appMessageMap = new HashMap<String, Object>();
appMessageMap.put("alert", message);
appMessageMap.put("badge", 9);
appMessageMap.put("sound", "default");
appleMessageMap.put("aps", appMessageMap);
return jsonify(appleMessageMap);
}
private String getAndroidMessage(final String message) throws JsonProcessingException {
Map<String, String> data = new HashMap<String, String>();
data.put("message", message);
Map<String, Object> androidMessageMap = new HashMap<String, Object>();
androidMessageMap.put("collapse_key", "Welcome");
androidMessageMap.put("data", data);
androidMessageMap.put("delay_while_idle", true);
androidMessageMap.put("time_to_live", 125);
androidMessageMap.put("dry_run", false);
return jsonify(androidMessageMap);
}
private String getPlatformMessage(final Platform platform, final String message) throws JsonProcessingException {
String platformMessage = null;
switch (platform) {
case APNS:
case APNS_SANDBOX:
platformMessage = getAppleMessage(message);
break;
case GCM:
platformMessage = getAndroidMessage(message);
break;
}
return platformMessage;
}
private String jsonify(Object message) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.writeValueAsString(message);
}
private String loadFileFromClasspath(final String filename) {
InputStream is = null;
StringWriter writer = new StringWriter();
try {
is = PushNotificationMessageProcessor.class.getClassLoader().getResourceAsStream(filename);
IOUtils.copy(is, writer, "UTF-8");
return writer.toString();
} catch (Exception e) {
log.error("Error reading file from classpath", e);
return null;
} finally {
try { is.close(); } catch(Exception e) { }
try { writer.close(); } catch(Exception e) {}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment