@Override public void sendPushMessage( Variant variant, Collection<String> clientIdentifiers, UnifiedPushMessage pushMessage, String pushMessageInformationId, NotificationSenderCallback senderCallback) { final AdmService admService = ADM.newService(); final PayloadBuilder builder = ADM.newPayload(); // flatten the "special keys" builder.dataField("alert", pushMessage.getMessage().getAlert()); // if present, apply the time-to-live metadata: int ttl = pushMessage.getConfig().getTimeToLive(); if (ttl != -1) { builder.expiresAfter(ttl); } // dirty hack for cordova, // TODO should be removed once we have our clients SDKs, tracked by AGPUSH-1269 builder.dataField("message", "useless payload"); // Handle consolidation key builder.consolidationKey(pushMessage.getMessage().getConsolidationKey()); pushMessage .getMessage() .getUserData() .keySet() .forEach(key -> builder.dataField(key, pushMessage.getMessage().getUserData().get(key))); // add the aerogear-push-id builder.dataField(InternalUnifiedPushMessage.PUSH_MESSAGE_ID, pushMessageInformationId); final AdmVariant admVariant = (AdmVariant) variant; clientIdentifiers.forEach( token -> { try { admService.sendMessageToDevice( token, admVariant.getClientId(), admVariant.getClientSecret(), builder.build()); senderCallback.onSuccess(); } catch (Exception e) { logger.error("Error sending payload to ADM server", e); senderCallback.onError(e.getMessage()); } }); logger.info( String.format( "Sent push notification to Amazon's ADM Server for %d tokens", clientIdentifiers.size())); }
/** * Receives request for processing a {@link UnifiedPushMessage} and loads tokens for devices that * match requested parameters from database. * * <p>Device tokens are loaded in a stream and split to batches of configured size (see {@link * SenderConfiguration#batchSize()}). Once the pre-configured number of batches (see {@link * SenderConfiguration#batchesToLoad()}) is reached, this method resends message to the same queue * it took the request from, so that the transaction it worked in is split and further processing * may continue in next transaction. * * <p>Additionally it fires {@link BatchLoadedEvent} as CDI event (that is translated to JMS * event) that helps {@link MetricsCollector} to track how many batches were loaded. When all * batches were loaded for the given variant, it fires {@link AllBatchesLoadedEvent}. * * @param msg holder object containing the payload and info about the effected variants */ public void loadAndQueueTokenBatch(@Observes @Dequeue MessageHolderWithVariants msg) throws IllegalStateException { final UnifiedPushMessage message = msg.getUnifiedPushMessage(); final VariantType variantType = msg.getVariantType(); final Collection<Variant> variants = msg.getVariants(); final String lastTokenFromPreviousBatch = msg.getLastTokenFromPreviousBatch(); final SenderConfiguration configuration = senderConfiguration.select(new SenderTypeLiteral(variantType)).get(); final PushMessageInformation pushMessageInformation = msg.getPushMessageInformation(); int serialId = msg.getLastSerialId(); logger.debug("Received message from queue: " + message.getMessage().getAlert()); final Criteria criteria = message.getCriteria(); final List<String> categories = criteria.getCategories(); final List<String> aliases = criteria.getAliases(); final List<String> deviceTypes = criteria.getDeviceTypes(); logger.info( String.format( "Preparing message delivery and loading tokens for the %s 3rd-party Push Network (for %d variants)", variantType, variants.size())); for (Variant variant : variants) { try { ResultsStream<String> tokenStream; final Set<String> topics = new TreeSet<>(); final boolean isAndroid = variantType.equals(VariantType.ANDROID); // the entire batch size int batchesToLoad = configuration.batchesToLoad(); // Some checks for GCM, because of GCM-3 topics boolean gcmTopicRequest = (isAndroid && TokenLoaderUtils.isGCMTopicRequest(criteria)); if (gcmTopicRequest) { // If we are able to do push for GCM topics... // 1) // find all topics, BUT only on the very first round of batches // otherwise after 10 (or what ever the max. is) another request would be sent to that // topic if (serialId == 0) { topics.addAll(TokenLoaderUtils.extractGCMTopics(criteria, variant.getVariantID())); // topics are handled as a first extra batch, // therefore we have to adjust the number by adding this extra batch batchesToLoad = batchesToLoad + 1; } // 2) always load the legacy tokens, for all number of batch iterations tokenStream = clientInstallationService .findAllOldGoogleCloudMessagingDeviceTokenForVariantIDByCriteria( variant.getVariantID(), categories, aliases, deviceTypes, configuration.tokensToLoad(), lastTokenFromPreviousBatch) .fetchSize(configuration.batchSize()) .executeQuery(); } else { tokenStream = clientInstallationService .findAllDeviceTokenForVariantIDByCriteria( variant.getVariantID(), categories, aliases, deviceTypes, configuration.tokensToLoad(), lastTokenFromPreviousBatch) .fetchSize(configuration.batchSize()) .executeQuery(); } String lastTokenInBatch = null; int tokensLoaded = 0; for (int batchNumber = 0; batchNumber < batchesToLoad; batchNumber++) { // increasing the serial ID, // to make sure it's properly read from all block ++serialId; final Set<String> tokens = new TreeSet<>(); // On Android, the first batch is for GCM3 topics // legacy tokens are submitted in the batch #2 and later if (isAndroid && batchNumber == 0 && !topics.isEmpty()) { tokens.addAll(topics); } else { for (int i = 0; i < configuration.batchSize() && tokenStream.next(); i++) { lastTokenInBatch = tokenStream.get(); tokens.add(lastTokenInBatch); tokensLoaded += 1; } } if (tokens.size() > 0) { if (tryToDispatchTokens( new MessageHolderWithTokens( msg.getPushMessageInformation(), message, variant, tokens, serialId))) { logger.info( String.format( "Loaded batch #%s, containing %d tokens, for %s variant (%s)", serialId, tokens.size(), variant.getType().getTypeName(), variant.getVariantID())); } else { logger.debug( String.format( "Failing token loading transaction for batch token #%s for %s variant (%s), since queue is full, will retry...", serialId, variant.getType().getTypeName(), variant.getVariantID())); context.setRollbackOnly(); return; } // using combined key of variant and PMI (AGPUSH-1585): batchLoaded.fire( new BatchLoadedEvent( variant.getVariantID() + ":" + msg.getPushMessageInformation().getId())); if (serialId == MessageHolderWithVariants.INITIAL_SERIAL_ID) { triggerVariantMetricCollection.fire( new TriggerVariantMetricCollectionEvent( msg.getPushMessageInformation(), variant)); } } else { logger.debug( String.format( "Ending batch processing: No more tokens for batch #%s available", serialId)); break; } } // should we trigger next transaction? if (tokensLoaded >= configuration.tokensToLoad()) { logger.debug( String.format( "Ending token loading transaction for %s variant (%s)", variant.getType().getTypeName(), variant.getVariantID())); nextBatchEvent.fire( new MessageHolderWithVariants( msg.getPushMessageInformation(), message, msg.getVariantType(), variants, serialId, lastTokenInBatch)); } else { logger.debug( String.format( "All batches for %s variant were loaded (%s)", variant.getType().getTypeName(), pushMessageInformation.getId())); // using combined key of variant and PMI (AGPUSH-1585): allBatchesLoaded.fire( new AllBatchesLoadedEvent( variant.getVariantID() + ":" + msg.getPushMessageInformation().getId())); triggerVariantMetricCollection.fire( new TriggerVariantMetricCollectionEvent(pushMessageInformation, variant)); if (tokensLoaded == 0 && lastTokenFromPreviousBatch == null) { // no tokens were loaded at all! if (gcmTopicRequest) { logger.debug("No legacy(non-InstanceID) tokens found. Just pure GCM topic requests"); } else { logger.warn("Check your push query: Not a single token was loaded from the DB!"); } VariantMetricInformation variantMetricInformation = new VariantMetricInformation(); variantMetricInformation.setPushMessageInformation(msg.getPushMessageInformation()); variantMetricInformation.setVariantID(variant.getVariantID()); variantMetricInformation.setDeliveryStatus(Boolean.TRUE); dispatchVariantMetricEvent.fire(variantMetricInformation); } } } catch (ResultStreamException e) { logger.error("Failed to load batch of tokens", e); } } }