All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.firebase.messaging.FirebaseMessaging Maven / Gradle / Ivy

Go to download

This is the official Firebase Admin Java SDK. Build extraordinary native JVM apps in minutes with Firebase. The Firebase platform can power your app’s backend, user authentication, static hosting, and more.

The newest version!
/*
 * Copyright 2018 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.firebase.messaging;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.api.core.ApiFuture;
import com.google.api.core.ApiFutures;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.firebase.ErrorCode;
import com.google.firebase.FirebaseApp;
import com.google.firebase.ImplFirebaseTrampolines;
import com.google.firebase.internal.CallableOperation;
import com.google.firebase.internal.FirebaseService;
import com.google.firebase.internal.NonNull;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;

/**
 * This class is the entry point for all server-side Firebase Cloud Messaging actions.
 *
 * 

You can get an instance of FirebaseMessaging via {@link #getInstance(FirebaseApp)}, and * then use it to send messages or manage FCM topic subscriptions. */ public class FirebaseMessaging { private final FirebaseApp app; private final Supplier messagingClient; private final Supplier instanceIdClient; private FirebaseMessaging(Builder builder) { this.app = checkNotNull(builder.firebaseApp); this.messagingClient = Suppliers.memoize(builder.messagingClient); this.instanceIdClient = Suppliers.memoize(builder.instanceIdClient); } /** * Gets the {@link FirebaseMessaging} instance for the default {@link FirebaseApp}. * * @return The {@link FirebaseMessaging} instance for the default {@link FirebaseApp}. */ public static FirebaseMessaging getInstance() { return getInstance(FirebaseApp.getInstance()); } /** * Gets the {@link FirebaseMessaging} instance for the specified {@link FirebaseApp}. * * @return The {@link FirebaseMessaging} instance for the specified {@link FirebaseApp}. */ public static synchronized FirebaseMessaging getInstance(FirebaseApp app) { FirebaseMessagingService service = ImplFirebaseTrampolines.getService(app, SERVICE_ID, FirebaseMessagingService.class); if (service == null) { service = ImplFirebaseTrampolines.addService(app, new FirebaseMessagingService(app)); } return service.getInstance(); } /** * Sends the given {@link Message} via Firebase Cloud Messaging. * * @param message A non-null {@link Message} to be sent. * @return A message ID string. * @throws FirebaseMessagingException If an error occurs while handing the message off to FCM for * delivery. */ public String send(@NonNull Message message) throws FirebaseMessagingException { return send(message, false); } /** * Sends the given {@link Message} via Firebase Cloud Messaging. * *

If the {@code dryRun} option is set to true, the message will not be actually sent. Instead * FCM performs all the necessary validations and emulates the send operation. The {@code dryRun} * option is useful for determining whether an FCM registration has been deleted. However, it * cannot be used to validate APNs tokens. * * @param message A non-null {@link Message} to be sent. * @param dryRun a boolean indicating whether to perform a dry run (validation only) of the send. * @return A message ID string. * @throws FirebaseMessagingException If an error occurs while handing the message off to FCM for * delivery. */ public String send(@NonNull Message message, boolean dryRun) throws FirebaseMessagingException { return sendOp(message, dryRun).call(); } /** * Similar to {@link #send(Message)} but performs the operation asynchronously. * * @param message A non-null {@link Message} to be sent. * @return An {@code ApiFuture} that will complete with a message ID string when the message * has been sent. */ public ApiFuture sendAsync(@NonNull Message message) { return sendAsync(message, false); } /** * Similar to {@link #send(Message, boolean)} but performs the operation asynchronously. * * @param message A non-null {@link Message} to be sent. * @param dryRun a boolean indicating whether to perform a dry run (validation only) of the send. * @return An {@code ApiFuture} that will complete with a message ID string when the message * has been sent, or when the emulation has finished. */ public ApiFuture sendAsync(@NonNull Message message, boolean dryRun) { return sendOp(message, dryRun).callAsync(app); } private CallableOperation sendOp( final Message message, final boolean dryRun) { checkNotNull(message, "message must not be null"); final FirebaseMessagingClient messagingClient = getMessagingClient(); return new CallableOperation() { @Override protected String execute() throws FirebaseMessagingException { return messagingClient.send(message, dryRun); } }; } /** * Sends each message in the given list via Firebase Cloud Messaging. * Unlike {@link #sendAll(List)}, this method makes an HTTP call for each message in the * given array. * *

The list of responses obtained by calling {@link BatchResponse#getResponses()} on the return * value is in the same order as the input list. * * @param messages A non-null, non-empty list containing up to 500 messages. * @return A {@link BatchResponse} indicating the result of the operation. * @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for * delivery. An exception here or a {@link BatchResponse} with all failures indicates a total * failure, meaning that none of the messages in the list could be sent. Partial failures or * no failures are only indicated by a {@link BatchResponse}. */ public BatchResponse sendEach(@NonNull List messages) throws FirebaseMessagingException { return sendEach(messages, false); } /** * Sends each message in the given list via Firebase Cloud Messaging. * Unlike {@link #sendAll(List)}, this method makes an HTTP call for each message in the * given array. * *

If the {@code dryRun} option is set to true, the message will not be actually sent. Instead * FCM performs all the necessary validations, and emulates the send operation. The {@code dryRun} * option is useful for determining whether an FCM registration has been deleted. But it cannot be * used to validate APNs tokens. * *

The list of responses obtained by calling {@link BatchResponse#getResponses()} on the return * value is in the same order as the input list. * * @param messages A non-null, non-empty list containing up to 500 messages. * @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send. * @return A {@link BatchResponse} indicating the result of the operation. * @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for * delivery. An exception here or a {@link BatchResponse} with all failures indicates a total * failure, meaning that none of the messages in the list could be sent. Partial failures or * no failures are only indicated by a {@link BatchResponse}. */ public BatchResponse sendEach( @NonNull List messages, boolean dryRun) throws FirebaseMessagingException { try { return sendEachOpAsync(messages, dryRun).get(); } catch (InterruptedException | ExecutionException e) { throw new FirebaseMessagingException(ErrorCode.CANCELLED, SERVICE_ID); } } /** * Similar to {@link #sendEach(List)} but performs the operation asynchronously. * * @param messages A non-null, non-empty list containing up to 500 messages. * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when * the messages have been sent. */ public ApiFuture sendEachAsync(@NonNull List messages) { return sendEachOpAsync(messages, false); } /** * Similar to {@link #sendEach(List, boolean)} but performs the operation asynchronously. * * @param messages A non-null, non-empty list containing up to 500 messages. * @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send. * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when * the messages have been sent. */ public ApiFuture sendEachAsync(@NonNull List messages, boolean dryRun) { return sendEachOpAsync(messages, dryRun); } // Returns an ApiFuture directly since this function is non-blocking. Individual child send // requests are still called async and run in background threads. private ApiFuture sendEachOpAsync( final List messages, final boolean dryRun) { final List immutableMessages = ImmutableList.copyOf(messages); checkArgument(!immutableMessages.isEmpty(), "messages list must not be empty"); checkArgument(immutableMessages.size() <= 500, "messages list must not contain more than 500 elements"); List> list = new ArrayList<>(); for (Message message : immutableMessages) { // Make async send calls per message ApiFuture messageId = sendOpForSendResponse(message, dryRun).callAsync(app); list.add(messageId); } // Gather all futures and combine into a list ApiFuture> responsesFuture = ApiFutures.allAsList(list); // Chain this future to wrap the eventual responses in a BatchResponse without blocking // the main thread. This uses the current thread to execute, but since the transformation // function is non-blocking the transformation itself is also non-blocking. return ApiFutures.transform( responsesFuture, (responses) -> { return new BatchResponseImpl(responses); }, MoreExecutors.directExecutor()); } private CallableOperation sendOpForSendResponse( final Message message, final boolean dryRun) { checkNotNull(message, "message must not be null"); final FirebaseMessagingClient messagingClient = getMessagingClient(); return new CallableOperation() { @Override protected SendResponse execute() { try { String messageId = messagingClient.send(message, dryRun); return SendResponse.fromMessageId(messageId); } catch (FirebaseMessagingException e) { return SendResponse.fromException(e); } } }; } /** * Sends the given multicast message to all the FCM registration tokens specified in it. * *

This method uses the {@link #sendEach(List)} API under the hood to send the given * message to all the target recipients. The list of responses obtained by calling * {@link BatchResponse#getResponses()} on the return value is in the same order as the * tokens in the {@link MulticastMessage}. * * @param message A non-null {@link MulticastMessage} * @return A {@link BatchResponse} indicating the result of the operation. * @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for * delivery. An exception here or a {@link BatchResponse} with all failures indicates a total * failure, meaning that none of the messages in the list could be sent. Partial failures or * no failures are only indicated by a {@link BatchResponse}. */ public BatchResponse sendEachForMulticast( @NonNull MulticastMessage message) throws FirebaseMessagingException { return sendEachForMulticast(message, false); } /** * Sends the given multicast message to all the FCM registration tokens specified in it. * *

If the {@code dryRun} option is set to true, the message will not be actually sent. Instead * FCM performs all the necessary validations, and emulates the send operation. The {@code dryRun} * option is useful for determining whether an FCM registration has been deleted. But it cannot be * used to validate APNs tokens. * *

This method uses the {@link #sendEach(List)} API under the hood to send the given * message to all the target recipients. The list of responses obtained by calling * {@link BatchResponse#getResponses()} on the return value is in the same order as the * tokens in the {@link MulticastMessage}. * * @param message A non-null {@link MulticastMessage}. * @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send. * @return A {@link BatchResponse} indicating the result of the operation. * @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for * delivery. An exception here or a {@link BatchResponse} with all failures indicates a total * failure, meaning that none of the messages in the list could be sent. Partial failures or * no failures are only indicated by a {@link BatchResponse}. */ public BatchResponse sendEachForMulticast(@NonNull MulticastMessage message, boolean dryRun) throws FirebaseMessagingException { checkNotNull(message, "multicast message must not be null"); return sendEach(message.getMessageList(), dryRun); } /** * Similar to {@link #sendEachForMulticast(MulticastMessage)} but performs the operation * asynchronously. * * @param message A non-null {@link MulticastMessage}. * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when * the messages have been sent. */ public ApiFuture sendEachForMulticastAsync(@NonNull MulticastMessage message) { return sendEachForMulticastAsync(message, false); } /** * Similar to {@link #sendEachForMulticast(MulticastMessage, boolean)} but performs the operation * asynchronously. * * @param message A non-null {@link MulticastMessage}. * @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send. * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when * the messages have been sent. */ public ApiFuture sendEachForMulticastAsync( @NonNull MulticastMessage message, boolean dryRun) { checkNotNull(message, "multicast message must not be null"); return sendEachAsync(message.getMessageList(), dryRun); } /** * Sends all the messages in the given list via Firebase Cloud Messaging. Employs batching to * send the entire list as a single RPC call. Compared to the {@link #send(Message)} method, this * is a significantly more efficient way to send multiple messages. * *

The responses list obtained by calling {@link BatchResponse#getResponses()} on the return * value corresponds to the order of input messages. * * @param messages A non-null, non-empty list containing up to 500 messages. * @return A {@link BatchResponse} indicating the result of the operation. * @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for * delivery. An exception here indicates a total failure, meaning that none of the messages in * the list could be sent. Partial failures are indicated by a {@link BatchResponse} return * value. * @deprecated Use {@link #sendEach(List)} instead. */ @Deprecated public BatchResponse sendAll( @NonNull List messages) throws FirebaseMessagingException { return sendAll(messages, false); } /** * Sends all the messages in the given list via Firebase Cloud Messaging. Employs batching to * send the entire list as a single RPC call. Compared to the {@link #send(Message)} method, this * is a significantly more efficient way to send multiple messages. * *

If the {@code dryRun} option is set to true, the message will not be actually sent. Instead * FCM performs all the necessary validations, and emulates the send operation. The {@code dryRun} * option is useful for determining whether an FCM registration has been deleted. But it cannot be * used to validate APNs tokens. * *

The responses list obtained by calling {@link BatchResponse#getResponses()} on the return * value corresponds to the order of input messages. * * @param messages A non-null, non-empty list containing up to 500 messages. * @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send. * @return A {@link BatchResponse} indicating the result of the operation. * @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for * delivery. An exception here indicates a total failure, meaning that none of the messages in * the list could be sent. Partial failures are indicated by a {@link BatchResponse} return * value. * @deprecated Use {@link #sendEach(List, boolean)} instead. */ @Deprecated public BatchResponse sendAll( @NonNull List messages, boolean dryRun) throws FirebaseMessagingException { return sendAllOp(messages, dryRun).call(); } /** * Similar to {@link #sendAll(List)} but performs the operation asynchronously. * * @param messages A non-null, non-empty list containing up to 500 messages. * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when * the messages have been sent. * @deprecated Use {@link #sendEachAsync(List)} instead. */ @Deprecated public ApiFuture sendAllAsync(@NonNull List messages) { return sendAllAsync(messages, false); } /** * Similar to {@link #sendAll(List, boolean)} but performs the operation asynchronously. * * @param messages A non-null, non-empty list containing up to 500 messages. * @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send. * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when * the messages have been sent, or when the emulation has finished. * @deprecated Use {@link #sendEachAsync(List, boolean)} instead. */ @Deprecated public ApiFuture sendAllAsync( @NonNull List messages, boolean dryRun) { return sendAllOp(messages, dryRun).callAsync(app); } /** * Sends the given multicast message to all the FCM registration tokens specified in it. * *

This method uses the {@link #sendAll(List)} API under the hood to send the given * message to all the target recipients. The responses list obtained by calling * {@link BatchResponse#getResponses()} on the return value corresponds to the order of tokens * in the {@link MulticastMessage}. * * @param message A non-null {@link MulticastMessage} * @return A {@link BatchResponse} indicating the result of the operation. * @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for * delivery. An exception here indicates a total failure, meaning that the messages could not * be delivered to any recipient. Partial failures are indicated by a {@link BatchResponse} * return value. * @deprecated Use {@link #sendEachForMulticast(MulticastMessage)} instead. */ @Deprecated public BatchResponse sendMulticast( @NonNull MulticastMessage message) throws FirebaseMessagingException { return sendMulticast(message, false); } /** * Sends the given multicast message to all the FCM registration tokens specified in it. * *

If the {@code dryRun} option is set to true, the message will not be actually sent. Instead * FCM performs all the necessary validations, and emulates the send operation. The {@code dryRun} * option is useful for determining whether an FCM registration has been deleted. But it cannot be * used to validate APNs tokens. * *

This method uses the {@link #sendAll(List)} API under the hood to send the given * message to all the target recipients. The responses list obtained by calling * {@link BatchResponse#getResponses()} on the return value corresponds to the order of tokens * in the {@link MulticastMessage}. * * @param message A non-null {@link MulticastMessage}. * @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send. * @return A {@link BatchResponse} indicating the result of the operation. * @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for * delivery. An exception here indicates a total failure, meaning that the messages could not * be delivered to any recipient. Partial failures are indicated by a {@link BatchResponse} * return value. * @deprecated Use {@link #sendEachForMulticast(MulticastMessage, boolean)} instead. */ @Deprecated public BatchResponse sendMulticast( @NonNull MulticastMessage message, boolean dryRun) throws FirebaseMessagingException { checkNotNull(message, "multicast message must not be null"); return sendAll(message.getMessageList(), dryRun); } /** * Similar to {@link #sendMulticast(MulticastMessage)} but performs the operation * asynchronously. * * @param message A non-null {@link MulticastMessage}. * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when * the messages have been sent. * @deprecated Use {@link #sendEachForMulticastAsync(MulticastMessage)} instead. */ @Deprecated public ApiFuture sendMulticastAsync(@NonNull MulticastMessage message) { return sendMulticastAsync(message, false); } /** * Similar to {@link #sendMulticast(MulticastMessage, boolean)} but performs the operation * asynchronously. * * @param message A non-null {@link MulticastMessage}. * @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send. * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when * the messages have been sent. * @deprecated Use {@link #sendEachForMulticastAsync(MulticastMessage, boolean)} instead. */ @Deprecated public ApiFuture sendMulticastAsync( @NonNull MulticastMessage message, boolean dryRun) { checkNotNull(message, "multicast message must not be null"); return sendAllAsync(message.getMessageList(), dryRun); } private CallableOperation sendAllOp( final List messages, final boolean dryRun) { final List immutableMessages = ImmutableList.copyOf(messages); checkArgument(!immutableMessages.isEmpty(), "messages list must not be empty"); checkArgument(immutableMessages.size() <= 500, "messages list must not contain more than 500 elements"); final FirebaseMessagingClient messagingClient = getMessagingClient(); return new CallableOperation() { @Override protected BatchResponse execute() throws FirebaseMessagingException { return messagingClient.sendAll(messages, dryRun); } }; } @VisibleForTesting FirebaseMessagingClient getMessagingClient() { return messagingClient.get(); } /** * Subscribes a list of registration tokens to a topic. * * @param registrationTokens A non-null, non-empty list of device registration tokens, with at * most 1000 entries. * @param topic Name of the topic to subscribe to. May contain the {@code /topics/} prefix. * @return A {@link TopicManagementResponse}. */ public TopicManagementResponse subscribeToTopic(@NonNull List registrationTokens, @NonNull String topic) throws FirebaseMessagingException { return subscribeOp(registrationTokens, topic).call(); } /** * Similar to {@link #subscribeToTopic(List, String)} but performs the operation asynchronously. * * @param registrationTokens A non-null, non-empty list of device registration tokens, with at * most 1000 entries. * @param topic Name of the topic to subscribe to. May contain the {@code /topics/} prefix. * @return An {@code ApiFuture} that will complete with a {@link TopicManagementResponse}. */ public ApiFuture subscribeToTopicAsync( @NonNull List registrationTokens, @NonNull String topic) { return subscribeOp(registrationTokens, topic).callAsync(app); } private CallableOperation subscribeOp( final List registrationTokens, final String topic) { checkRegistrationTokens(registrationTokens); checkTopic(topic); final InstanceIdClient instanceIdClient = getInstanceIdClient(); return new CallableOperation() { @Override protected TopicManagementResponse execute() throws FirebaseMessagingException { return instanceIdClient.subscribeToTopic(topic, registrationTokens); } }; } /** * Unsubscribes a list of registration tokens from a topic. * * @param registrationTokens A non-null, non-empty list of device registration tokens, with at * most 1000 entries. * @param topic Name of the topic to unsubscribe from. May contain the {@code /topics/} prefix. * @return A {@link TopicManagementResponse}. */ public TopicManagementResponse unsubscribeFromTopic(@NonNull List registrationTokens, @NonNull String topic) throws FirebaseMessagingException { return unsubscribeOp(registrationTokens, topic).call(); } /** * Similar to {@link #unsubscribeFromTopic(List, String)} but performs the operation * asynchronously. * * @param registrationTokens A non-null, non-empty list of device registration tokens, with at * most 1000 entries. * @param topic Name of the topic to unsubscribe from. May contain the {@code /topics/} prefix. * @return An {@code ApiFuture} that will complete with a {@link TopicManagementResponse}. */ public ApiFuture unsubscribeFromTopicAsync( @NonNull List registrationTokens, @NonNull String topic) { return unsubscribeOp(registrationTokens, topic).callAsync(app); } private CallableOperation unsubscribeOp( final List registrationTokens, final String topic) { checkRegistrationTokens(registrationTokens); checkTopic(topic); final InstanceIdClient instanceIdClient = getInstanceIdClient(); return new CallableOperation() { @Override protected TopicManagementResponse execute() throws FirebaseMessagingException { return instanceIdClient.unsubscribeFromTopic(topic, registrationTokens); } }; } @VisibleForTesting InstanceIdClient getInstanceIdClient() { return this.instanceIdClient.get(); } private void checkRegistrationTokens(List registrationTokens) { checkArgument(registrationTokens != null && !registrationTokens.isEmpty(), "registrationTokens list must not be null or empty"); checkArgument(registrationTokens.size() <= 1000, "registrationTokens list must not contain more than 1000 elements"); for (String token : registrationTokens) { checkArgument(!Strings.isNullOrEmpty(token), "registration tokens list must not contain null or empty strings"); } } private void checkTopic(String topic) { checkArgument(!Strings.isNullOrEmpty(topic), "topic must not be null or empty"); checkArgument(topic.matches("^(/topics/)?(private/)?[a-zA-Z0-9-_.~%]+$"), "invalid topic name"); } private static final String SERVICE_ID = FirebaseMessaging.class.getName(); private static class FirebaseMessagingService extends FirebaseService { FirebaseMessagingService(FirebaseApp app) { super(SERVICE_ID, FirebaseMessaging.fromApp(app)); } } private static FirebaseMessaging fromApp(final FirebaseApp app) { return FirebaseMessaging.builder() .setFirebaseApp(app) .setMessagingClient(new Supplier() { @Override public FirebaseMessagingClient get() { return FirebaseMessagingClientImpl.fromApp(app); } }) .setInstanceIdClient(new Supplier() { @Override public InstanceIdClientImpl get() { return InstanceIdClientImpl.fromApp(app); } }) .build(); } static Builder builder() { return new Builder(); } static class Builder { private FirebaseApp firebaseApp; private Supplier messagingClient; private Supplier instanceIdClient; private Builder() { } Builder setFirebaseApp(FirebaseApp firebaseApp) { this.firebaseApp = firebaseApp; return this; } Builder setMessagingClient(Supplier messagingClient) { this.messagingClient = messagingClient; return this; } Builder setInstanceIdClient(Supplier instanceIdClient) { this.instanceIdClient = instanceIdClient; return this; } FirebaseMessaging build() { return new FirebaseMessaging(this); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy