@POST @Consumes(MediaType.APPLICATION_JSON) public Response registerInstallation(Installation entity, @Context HttpServletRequest request) { // find the matching variation: final Variant variant = loadVariantWhenAuthorized(request); if (variant == null) { return appendAllowOriginHeader( Response.status(Status.UNAUTHORIZED) .header("WWW-Authenticate", "Basic realm=\"AeroGear UnifiedPush Server\"") .entity("Unauthorized Request"), request); } // Poor validation: We require the Token! And the 'simplePushEndpoint' for SimplePush clients! if (entity.getDeviceToken() == null || (variant.getType() == VariantType.SIMPLE_PUSH && entity.getSimplePushEndpoint() == null)) { return appendAllowOriginHeader(Response.status(Status.BAD_REQUEST), request); } // look up all installations (with same token) for the given variant: Installation installation = clientInstallationService.findInstallationForVariantByDeviceToken( variant.getVariantID(), entity.getDeviceToken()); // Needed for the Admin UI Only. Help for setting up Routes entity.setPlatform(variant.getType().getTypeName()); // The 'mobile application' on the device/client was launched. // If the installation is already in the DB, let's update the metadata, // otherwise we register a new installation: logger.log(Level.FINEST, "Mobile Application on device was launched"); // new device/client ? if (installation == null) { logger.log(Level.FINEST, "Performing new device/client registration"); // store the installation: clientInstallationService.addInstallation(variant.getType(), entity); // add installation to the matching variant genericVariantService.addInstallation(variant, entity); } else { // We only update the metadata, if the device is enabled: if (installation.isEnabled()) { logger.log(Level.FINEST, "Updating received metadata for an 'enabled' installation"); // update the entity: clientInstallationService.updateInstallation(installation, entity); } } return appendAllowOriginHeader(Response.ok(entity), request); }
/** * 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); } } }