Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b53c94f
Preliminary impl of FCM batch support
hiranya911 Jan 29, 2019
6882354
Added BatchMessage class and tests
hiranya911 Jan 29, 2019
e75bcff
Refactored the FCM send operation
hiranya911 Jan 29, 2019
726ee09
Added unit tests for sendBatch() API
hiranya911 Jan 29, 2019
e3ad436
Refactored error handling
hiranya911 Jan 29, 2019
504e9ef
Refactored FCM logic into a new FirebaseMessagingClient class
hiranya911 Jan 30, 2019
80ed4b1
Using a separate request factory for child requests
hiranya911 Jan 30, 2019
057fcb5
Added the InstanceIdClient class for handling topic mgt ops
hiranya911 Jan 30, 2019
2c36d4c
Added license headers
hiranya911 Jan 30, 2019
c437042
Renamed BatchResponse as SendResponse
hiranya911 Jan 30, 2019
a3223fd
Added BatchResponse class; Renamed BatchMessage to MulticastMessage
hiranya911 Feb 6, 2019
5668421
Updated tests and docs
hiranya911 Feb 6, 2019
693711f
Updated documentation
hiranya911 Feb 6, 2019
46507d8
Added documentation and tests
hiranya911 Feb 6, 2019
b4d80e0
Merge branch 'master' into hkj-fcm-batch
hiranya911 Feb 6, 2019
0371159
Updated docs, changelog and annotations
hiranya911 Feb 6, 2019
5d57e07
Updated integration test to use multiple topics; Updated docs
hiranya911 Feb 7, 2019
f8f8c1e
Removing a redundant whitespace
hiranya911 Feb 11, 2019
7ecabe0
Reduced FCM batch size to 100 (#250)
hiranya911 Feb 19, 2019
3c0ae40
Setting the X-Client-Version header for FCM (#252)
hiranya911 Feb 22, 2019
c6367c0
Snippets for FCM sendAll() and sendMulticast() (#249)
hiranya911 Feb 25, 2019
1fbddcb
Addressing some documentation nits
hiranya911 Mar 12, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Added BatchMessage class and tests
  • Loading branch information
hiranya911 committed Jan 29, 2019
commit 688235424ec4d4be3386ee55e706f23cf92f9f0b
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ target/
.classpath
.project
.checkstyle
.factorypath
.vscode/
release.properties
integration_cert.json
integration_apikey.txt
21 changes: 20 additions & 1 deletion src/main/java/com/google/firebase/messaging/BatchMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

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

import com.google.api.client.util.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.firebase.internal.NonNull;
Expand All @@ -41,13 +42,32 @@ private BatchMessage(Builder builder) {
this.tokens = builder.tokens.build();
checkArgument(!this.tokens.isEmpty(), "at least one token must be specified");
checkArgument(this.tokens.size() <= 1000, "no more than 1000 tokens can be specified");
for (String token : this.tokens) {
checkArgument(!Strings.isNullOrEmpty(token), "none of the tokens can be null or empty");
}
this.data = builder.data.isEmpty() ? null : ImmutableMap.copyOf(builder.data);
this.notification = builder.notification;
this.androidConfig = builder.androidConfig;
this.webpushConfig = builder.webpushConfig;
this.apnsConfig = builder.apnsConfig;
}

List<Message> getMessageList() {
Message.Builder builder = Message.builder()
.setNotification(this.notification)
.setAndroidConfig(this.androidConfig)
.setApnsConfig(this.apnsConfig)
.setWebpushConfig(this.webpushConfig);
if (this.data != null) {
builder.putAllData(this.data);
}
ImmutableList.Builder<Message> messages = ImmutableList.builder();
for (String token : this.tokens) {
messages.add(builder.setToken(token).build());
}
return messages.build();
}

/**
* Creates a new {@link Message.Builder}.
*
Expand Down Expand Up @@ -157,5 +177,4 @@ public BatchMessage build() {
return new BatchMessage(this);
}
}

}
164 changes: 91 additions & 73 deletions src/main/java/com/google/firebase/messaging/FirebaseMessaging.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import com.google.api.client.http.HttpResponseInterceptor;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.json.JsonHttpContent;
import com.google.api.client.json.GenericJson;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.JsonObjectParser;
import com.google.api.client.json.JsonParser;
Expand Down Expand Up @@ -229,90 +228,35 @@ public List<BatchResponse> sendBatch(List<Message> messages) throws FirebaseMess
}

public List<BatchResponse> sendBatch(
List<Message> messages, boolean dryRun) throws FirebaseMessagingException {
return sendBatchOp(messages, dryRun).call();
BatchMessage batch, boolean dryRun) throws FirebaseMessagingException {
checkNotNull(batch, "batch message must not be null");
return sendBatch(batch.getMessageList(), dryRun);
}

public ApiFuture<List<BatchResponse>> sendBatchAsync(List<Message> messages) {
return sendBatchAsync(messages, false);
public List<BatchResponse> sendBatch(BatchMessage batch) throws FirebaseMessagingException {
return sendBatch(batch, false);
}

public ApiFuture<List<BatchResponse>> sendBatchAsync(List<Message> messages, boolean dryRun) {
return sendBatchOp(messages, dryRun).callAsync(app);
public List<BatchResponse> sendBatch(
List<Message> messages, boolean dryRun) throws FirebaseMessagingException {
return sendBatchOp(messages, dryRun).call();
}

private CallableOperation<List<BatchResponse>, FirebaseMessagingException> sendBatchOp(
final List<Message> messages, final boolean dryRun) {

final List<Message> immutableMessages = ImmutableList.copyOf(messages);
checkArgument(!immutableMessages.isEmpty(), "messages list must not be empty");
checkArgument(immutableMessages.size() <= 1000,
"messages list must not contain more than 1000 elements");
return new CallableOperation<List<BatchResponse>,FirebaseMessagingException>() {
@Override
protected List<BatchResponse> execute() throws FirebaseMessagingException {
try {
return sendBatchRequest(immutableMessages, dryRun);
} catch (HttpResponseException e) {
handleSendHttpError(e);
return null;
} catch (IOException e) {
throw new FirebaseMessagingException(
INTERNAL_ERROR, "Error while calling FCM backend service", e);
}
}
};
public ApiFuture<List<BatchResponse>> sendBatchAsync(BatchMessage batch) {
return sendBatchAsync(batch, false);
}

private List<BatchResponse> sendBatchRequest(
List<Message> messages, boolean dryRun) throws IOException {

MessagingBatchCallback callback = new MessagingBatchCallback();
BatchRequest batch = newBatchRequest(messages, dryRun, callback);
batch.execute();
return ImmutableList.copyOf(callback.responses);
public ApiFuture<List<BatchResponse>> sendBatchAsync(BatchMessage batch, boolean dryRun) {
checkNotNull(batch, "batch message must not be null");
return sendBatchAsync(batch.getMessageList(), dryRun);
}

private BatchRequest newBatchRequest(
List<Message> messages, boolean dryRun, MessagingBatchCallback callback) throws IOException {

BatchRequest batch = new BatchRequest(
requestFactory.getTransport(), requestFactory.getInitializer());
batch.setBatchUrl(new GenericUrl(FCM_BATCH_URL));

for (Message message : messages) {
ImmutableMap.Builder<String, Object> payload = ImmutableMap.<String, Object>builder()
.put("message", message);
if (dryRun) {
payload.put("validate_only", true);
}
HttpRequest request = requestFactory.buildPostRequest(
new GenericUrl(url), new JsonHttpContent(jsonFactory, payload.build()));
request.getHeaders().set("X-GOOG-API-FORMAT-VERSION", "2");
request.setParser(new JsonObjectParser(jsonFactory));
batch.queue(
request, MessagingServiceResponse.class, MessagingServiceErrorResponse.class, callback);
}

return batch;
public ApiFuture<List<BatchResponse>> sendBatchAsync(List<Message> messages) {
return sendBatchAsync(messages, false);
}

private static class MessagingBatchCallback
implements BatchCallback<MessagingServiceResponse, MessagingServiceErrorResponse> {

private final List<BatchResponse> responses = new ArrayList<>();

@Override
public void onSuccess(
MessagingServiceResponse response, HttpHeaders responseHeaders) throws IOException {
responses.add(BatchResponse.fromSuccessResponse(response));
}

@Override
public void onFailure(
MessagingServiceErrorResponse error, HttpHeaders responseHeaders) throws IOException {
responses.add(BatchResponse.fromErrorResponse(error));
}
public ApiFuture<List<BatchResponse>> sendBatchAsync(List<Message> messages, boolean dryRun) {
return sendBatchOp(messages, dryRun).callAsync(app);
}

private CallableOperation<String, FirebaseMessagingException> sendOp(
Expand Down Expand Up @@ -432,6 +376,62 @@ private void handleTopicManagementHttpError(
throw new FirebaseMessagingException(code, msg, e);
}

private CallableOperation<List<BatchResponse>, FirebaseMessagingException> sendBatchOp(
final List<Message> messages, final boolean dryRun) {

final List<Message> immutableMessages = ImmutableList.copyOf(messages);
checkArgument(!immutableMessages.isEmpty(), "messages list must not be empty");
checkArgument(immutableMessages.size() <= 1000,
"messages list must not contain more than 1000 elements");
return new CallableOperation<List<BatchResponse>,FirebaseMessagingException>() {
@Override
protected List<BatchResponse> execute() throws FirebaseMessagingException {
try {
return sendBatchRequest(immutableMessages, dryRun);
} catch (HttpResponseException e) {
handleSendHttpError(e);
return null;
} catch (IOException e) {
throw new FirebaseMessagingException(
INTERNAL_ERROR, "Error while calling FCM backend service", e);
}
}
};
}

private List<BatchResponse> sendBatchRequest(
List<Message> messages, boolean dryRun) throws IOException {

MessagingBatchCallback callback = new MessagingBatchCallback();
BatchRequest batch = newBatchRequest(messages, dryRun, callback);
batch.execute();
return ImmutableList.copyOf(callback.responses);
}

private BatchRequest newBatchRequest(
List<Message> messages, boolean dryRun, MessagingBatchCallback callback) throws IOException {

BatchRequest batch = new BatchRequest(
requestFactory.getTransport(), requestFactory.getInitializer());
batch.setBatchUrl(new GenericUrl(FCM_BATCH_URL));

for (Message message : messages) {
ImmutableMap.Builder<String, Object> payload = ImmutableMap.<String, Object>builder()
.put("message", message);
if (dryRun) {
payload.put("validate_only", true);
}
HttpRequest request = requestFactory.buildPostRequest(
new GenericUrl(url), new JsonHttpContent(jsonFactory, payload.build()));
request.getHeaders().set("X-GOOG-API-FORMAT-VERSION", "2");
request.setParser(new JsonObjectParser(jsonFactory));
batch.queue(
request, MessagingServiceResponse.class, MessagingServiceErrorResponse.class, callback);
}

return batch;
}

private static void disconnectQuietly(HttpResponse response) {
if (response != null) {
try {
Expand Down Expand Up @@ -488,4 +488,22 @@ private static class InstanceIdServiceErrorResponse {
@Key("error")
private String error;
}

private static class MessagingBatchCallback
implements BatchCallback<MessagingServiceResponse, MessagingServiceErrorResponse> {

private final List<BatchResponse> responses = new ArrayList<>();

@Override
public void onSuccess(
MessagingServiceResponse response, HttpHeaders responseHeaders) throws IOException {
responses.add(BatchResponse.fromSuccessResponse(response));
}

@Override
public void onFailure(
MessagingServiceErrorResponse error, HttpHeaders responseHeaders) throws IOException {
responses.add(BatchResponse.fromErrorResponse(error));
}
}
}
33 changes: 32 additions & 1 deletion src/main/java/com/google/firebase/messaging/Message.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,38 @@ private Message(Builder builder) {
this.condition = builder.condition;
}

Map<String, String> getData() {
return data;
}

Notification getNotification() {
return notification;
}

AndroidConfig getAndroidConfig() {
return androidConfig;
}

WebpushConfig getWebpushConfig() {
return webpushConfig;
}

ApnsConfig getApnsConfig() {
return apnsConfig;
}

String getToken() {
return token;
}

String getTopic() {
return topic;
}

String getCondition() {
return condition;
}

private static String stripPrefix(String topic) {
if (Strings.isNullOrEmpty(topic)) {
return null;
Expand Down Expand Up @@ -226,5 +258,4 @@ public Message build() {
return new Message(this);
}
}

}
Loading