Created
November 25, 2013 18:31
-
-
Save billy-bacon/7646196 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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