/** * Returns the ApnsService, based on the required profile (production VS sandbox/test). Null is * returned if there is no "configuration" for the request stage */ private ApnsService buildApnsService(iOSVariant iOSVariant) { // this check should not be needed, but you never know: if (iOSVariant.getCertificate() != null && iOSVariant.getPassphrase() != null) { final ApnsServiceBuilder builder = APNS.newService(); // add the certificate: ByteArrayInputStream stream = new ByteArrayInputStream(iOSVariant.getCertificate()); builder.withCert(stream, iOSVariant.getPassphrase()); try { // release the stream stream.close(); } catch (IOException e) { logger.log(Level.SEVERE, "Error reading certificate", e); } // pick the destination: if (iOSVariant.isProduction()) { builder.withProductionDestination(); } else { builder.withSandboxDestination(); } // create the service return builder.build(); } // null if, why ever, there was no cert/passphrase return null; }
/** * Sends APNs notifications ({@link UnifiedPushMessage}) to all devices, that are represented by * the {@link Collection} of tokens for the given {@link iOSVariant}. * * @param iOSVariant the logical construct, needed to lookup the certificate and the passphrase. * @param tokens collection of tokens, representing actual iOS devices * @param pushMessage the payload to be submitted */ public void sendPushMessage( iOSVariant iOSVariant, Collection<String> tokens, UnifiedPushMessage pushMessage) { // no need to send empty list if (tokens.isEmpty()) { return; } PayloadBuilder builder = APNS.newPayload() // adding recognized key values .alertBody(pushMessage.getAlert()) // alert dialog, in iOS .badge(pushMessage.getBadge()) // little badge icon update; .sound(pushMessage.getSound()); // sound to be played by app // apply the 'content-available:1' value: if (pushMessage.isContentAvailable()) { // content-available:1 is (with iOS7) not only used // Newsstand, however 'notnoop' names it this way (legacy)... builder = builder.forNewsstand(); } builder = builder.customFields(pushMessage.getData()); // adding other (submitted) fields final String apnsMessage = builder.build(); // build the JSON payload, for APNs ApnsService service = buildApnsService(iOSVariant); if (service != null) { try { logger.fine(String.format("Sending transformed APNs payload: '%s' ", apnsMessage)); // send: service.start(); Date expireDate = createFutureDateBasedOnTTL(pushMessage.getTimeToLive()); service.push(tokens, apnsMessage, expireDate); // after sending, let's ask for the inactive tokens: final Set<String> inactiveTokens = service.getInactiveDevices().keySet(); // transform the tokens to be all lower-case: final Set<String> transformedTokens = lowerCaseAllTokens(inactiveTokens); // trigger asynchronous deletion: clientInstallationService.removeInstallationsForVariantByDeviceTokens( iOSVariant.getVariantID(), transformedTokens); } catch (RuntimeException e) { logger.log(Level.SEVERE, "Error sending messages to APN server", e); } finally { // tear down and release resources: service.stop(); } } else { logger.severe("No certificate was found. Could not send messages to APNs"); } }