diff --git a/FcmJava/deployment/deploy_release.sh b/FcmJava/deployment/deploy_release.sh
new file mode 100755
index 0000000..f759962
--- /dev/null
+++ b/FcmJava/deployment/deploy_release.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# Copyright (c) Philipp Wagner. All rights reserved.
+# Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+# Path to the Executables:
+MVN_EXECUTABLE="/Users/bytefish/Development/maven-3.3.9/bin/mvn"
+GPG_EXECUTABLE="/usr/local/bin/gpg"
+
+# GPG Key ID used for signing:
+GPG_KEY_ID=E4B54CD3
+
+# Logs to be used:
+STDOUT=stdout.log
+STDERR=stderr.log
+
+# POM File to use for building the project:
+POM_FILE=../pom.xml
+
+# Prompt for Sonatype:
+read -p "Sonatype User: " SONATYPE_USER
+read -p "Sonatype Password: " SONATYPE_PASSWORD
+
+# Prompt GPG Passphrase:
+read -p "GPG Signing Passphrase: " GPG_PASSPHRASE
+
+$MVN_EXECUTABLE clean deploy -Prelease,docs-and-source --settings deploysettings.xml -DskipTests -Dgpg.keyname=$GPG_KEY_ID -Dgpg.executable=$GPG_EXECUTABLE -Dgpg.passphrase=$GPG_PASSPHRASE -DretryFailedDeploymentCount=3 -f $POM_FILE
+
+pause
diff --git a/FcmJava/fcmjava-client/pom.xml b/FcmJava/fcmjava-client/pom.xml
index ffd9239..88637d9 100644
--- a/FcmJava/fcmjava-client/pom.xml
+++ b/FcmJava/fcmjava-client/pom.xml
@@ -7,7 +7,7 @@
de.bytefish.fcmjava
fcmjava-parent
- 0.4
+ 1.1
..
diff --git a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/FcmClient.java b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/FcmClient.java
index bae0973..a09001b 100644
--- a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/FcmClient.java
+++ b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/FcmClient.java
@@ -3,14 +3,10 @@
package de.bytefish.fcmjava.client;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import de.bytefish.fcmjava.client.utils.HttpUtils;
+import de.bytefish.fcmjava.client.http.HttpClient;
+import de.bytefish.fcmjava.client.http.IHttpClient;
+import de.bytefish.fcmjava.client.settings.PropertiesBasedSettings;
import de.bytefish.fcmjava.http.client.IFcmClient;
-import de.bytefish.fcmjava.client.interceptors.request.AuthenticationRequestInterceptor;
-import de.bytefish.fcmjava.client.interceptors.request.JsonRequestInterceptor;
-import de.bytefish.fcmjava.client.interceptors.request.LoggingRequestInterceptor;
-import de.bytefish.fcmjava.client.interceptors.response.LoggingResponseInterceptor;
-import de.bytefish.fcmjava.client.interceptors.response.StatusResponseInterceptor;
import de.bytefish.fcmjava.http.options.IFcmClientSettings;
import de.bytefish.fcmjava.requests.data.DataMulticastMessage;
import de.bytefish.fcmjava.requests.data.DataUnicastMessage;
@@ -22,59 +18,54 @@
import de.bytefish.fcmjava.requests.topic.TopicMulticastMessage;
import de.bytefish.fcmjava.requests.topic.TopicUnicastMessage;
import de.bytefish.fcmjava.responses.CreateDeviceGroupMessageResponse;
-import de.bytefish.fcmjava.responses.MulticastMessageResponse;
+import de.bytefish.fcmjava.responses.FcmMessageResponse;
import de.bytefish.fcmjava.responses.TopicMessageResponse;
-import de.bytefish.fcmjava.responses.UnicastMessageResponse;
-import org.apache.http.HttpEntity;
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClientBuilder;
-import org.apache.http.util.EntityUtils;
public class FcmClient implements IFcmClient {
private final IFcmClientSettings settings;
- private final HttpClientBuilder httpClientBuilder;
+ private final IHttpClient httpClient;
+
+ public FcmClient() {
+ this(PropertiesBasedSettings.createFromDefault());
+ }
public FcmClient(IFcmClientSettings settings) {
+ this(settings, new HttpClient(settings));
+ }
+
+ public FcmClient(IFcmClientSettings settings, IHttpClient httpClient) {
if(settings == null) {
throw new IllegalArgumentException("settings");
}
- this.settings = settings;
+ if(httpClient == null) {
+ throw new IllegalArgumentException("httpClient");
+ }
- // Construct the Builder for all Requests:
- this.httpClientBuilder = HttpClientBuilder.create()
- // Build Request Pipeline:
- .addInterceptorFirst(new AuthenticationRequestInterceptor(settings.getApiKey()))
- .addInterceptorLast(new JsonRequestInterceptor())
- .addInterceptorLast(new LoggingRequestInterceptor())
- // Build Response Pipeline:
- .addInterceptorFirst(new LoggingResponseInterceptor())
- .addInterceptorLast(new StatusResponseInterceptor());
+ this.settings = settings;
+ this.httpClient = httpClient;
}
@Override
- public MulticastMessageResponse send(DataMulticastMessage message) {
- return post(message, MulticastMessageResponse.class);
+ public FcmMessageResponse send(DataMulticastMessage message) {
+ return post(message, FcmMessageResponse.class);
}
@Override
- public MulticastMessageResponse send(NotificationMulticastMessage notification) {
- return post(notification, MulticastMessageResponse.class);
+ public FcmMessageResponse send(NotificationMulticastMessage notification) {
+ return post(notification, FcmMessageResponse.class);
}
@Override
- public UnicastMessageResponse send(DataUnicastMessage message) {
- return post(message, UnicastMessageResponse.class);
+ public FcmMessageResponse send(DataUnicastMessage message) {
+ return post(message, FcmMessageResponse.class);
}
@Override
- public UnicastMessageResponse send(NotificationUnicastMessage notification) {
- return post(notification, UnicastMessageResponse.class);
+ public FcmMessageResponse send(NotificationUnicastMessage notification) {
+ return post(notification, FcmMessageResponse.class);
}
@Override
@@ -103,18 +94,10 @@ public void send(AddDeviceGroupMessage message) {
}
protected TResponseMessage post(TRequestMessage requestMessage, Class responseType) {
- try {
- return HttpUtils.post(httpClientBuilder, settings, requestMessage, responseType);
- } catch(Exception e) {
- throw new RuntimeException(e);
- }
+ return httpClient.post(requestMessage, responseType);
}
protected void post(TRequestMessage requestMessage) {
- try {
- HttpUtils.post(httpClientBuilder, settings, requestMessage);
- } catch(Exception e) {
- throw new RuntimeException(e);
- }
+ httpClient.post(requestMessage);
}
}
diff --git a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/functional/Action0.java b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/functional/Action0.java
new file mode 100644
index 0000000..2b3a5fb
--- /dev/null
+++ b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/functional/Action0.java
@@ -0,0 +1,9 @@
+// Copyright (c) Philipp Wagner. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+package de.bytefish.fcmjava.client.functional;
+
+@FunctionalInterface
+public interface Action0 {
+ void invoke();
+}
diff --git a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/functional/Action1.java b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/functional/Action1.java
new file mode 100644
index 0000000..c8845dd
--- /dev/null
+++ b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/functional/Action1.java
@@ -0,0 +1,9 @@
+// Copyright (c) Philipp Wagner. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+package de.bytefish.fcmjava.client.functional;
+
+@FunctionalInterface
+public interface Action1 {
+ void invoke(S s);
+}
\ No newline at end of file
diff --git a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/utils/StringUtils.java b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/functional/Func1.java
similarity index 58%
rename from FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/utils/StringUtils.java
rename to FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/functional/Func1.java
index 5a78fe1..c96a4e6 100644
--- a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/utils/StringUtils.java
+++ b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/functional/Func1.java
@@ -1,10 +1,9 @@
// Copyright (c) Philipp Wagner. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-package de.bytefish.fcmjava.utils;
-
-public class StringUtils {
-
- public static String EmptyString = "";
+package de.bytefish.fcmjava.client.functional;
+@FunctionalInterface
+public interface Func1 {
+ S invoke();
}
diff --git a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/utils/HttpUtils.java b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/http/HttpClient.java
similarity index 52%
rename from FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/utils/HttpUtils.java
rename to FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/http/HttpClient.java
index 4c99e41..48df69f 100644
--- a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/utils/HttpUtils.java
+++ b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/http/HttpClient.java
@@ -1,8 +1,13 @@
// Copyright (c) Philipp Wagner. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-package de.bytefish.fcmjava.client.utils;
+package de.bytefish.fcmjava.client.http;
+import de.bytefish.fcmjava.client.functional.Action1;
+import de.bytefish.fcmjava.client.interceptors.request.AuthenticationRequestInterceptor;
+import de.bytefish.fcmjava.client.interceptors.request.JsonRequestInterceptor;
+import de.bytefish.fcmjava.client.interceptors.response.StatusResponseInterceptor;
+import de.bytefish.fcmjava.client.utils.JsonUtils;
import de.bytefish.fcmjava.http.options.IFcmClientSettings;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
@@ -12,26 +17,63 @@
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
-public class HttpUtils {
+import java.nio.charset.StandardCharsets;
+/**
+ * This HttpClient is based on the Apache HttpClient.
+ *
+ * If you need to configure the Apache HttpClient (proxy settings, timeouts, ...) you can call the configure(...)
+ * method to modify the HttpClientBuilder used for creating Apache HttpClient instances.
+ */
+public class HttpClient implements IHttpClient {
- public static TResponseMessage post(HttpClientBuilder httpClientBuilder, IFcmClientSettings settings, TRequestMessage requestMessage, Class responseType) {
+ private final IFcmClientSettings settings;
+ private final HttpClientBuilder httpClientBuilder;
+
+ public HttpClient(IFcmClientSettings settings) {
+
+ if(settings == null) {
+ throw new IllegalArgumentException("settings");
+ }
+
+ this.settings = settings;
+
+ // Construct the Builder for all Requests:
+ this.httpClientBuilder = HttpClientBuilder.create()
+ // Build Request Pipeline:
+ .addInterceptorFirst(new AuthenticationRequestInterceptor(settings.getApiKey()))
+ .addInterceptorLast(new JsonRequestInterceptor())
+ // Build Response Pipeline:
+ .addInterceptorLast(new StatusResponseInterceptor());
+ }
+
+ public HttpClient configure(Action1 configuration) {
+ if(configuration == null) {
+ throw new IllegalArgumentException("configuration");
+ }
+
+ configuration.invoke(httpClientBuilder);
+
+ return this;
+ }
+
+ public TResponseMessage post(TRequestMessage requestMessage, Class responseType) {
try {
- return internalPost(httpClientBuilder, settings, requestMessage, responseType);
+ return internalPost(requestMessage, responseType);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
- public static void post(HttpClientBuilder httpClientBuilder, IFcmClientSettings settings, TRequestMessage requestMessage) {
+ public void post(TRequestMessage requestMessage) {
try {
- internalPost(httpClientBuilder, settings, requestMessage);
+ internalPost(requestMessage);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
- private static void internalPost(HttpClientBuilder httpClientBuilder, IFcmClientSettings settings, TRequestMessage requestMessage) throws Exception {
+ private void internalPost(TRequestMessage requestMessage) throws Exception {
try (CloseableHttpClient client = httpClientBuilder.build()) {
@@ -39,7 +81,7 @@ private static void internalPost(HttpClientBuilder httpClientB
HttpPost httpPost = new HttpPost(settings.getFcmUrl());
// Set the JSON String as data:
- httpPost.setEntity(new StringEntity(JsonUtils.getAsJsonString(requestMessage)));
+ httpPost.setEntity(new StringEntity(JsonUtils.getAsJsonString(requestMessage), StandardCharsets.UTF_8));
// Execute the Request:
try(CloseableHttpResponse response = client.execute(httpPost)) {
@@ -57,7 +99,7 @@ private static void internalPost(HttpClientBuilder httpClientB
}
}
- private static TResponseMessage internalPost(HttpClientBuilder httpClientBuilder, IFcmClientSettings settings, TRequestMessage requestMessage, Class responseType) throws Exception {
+ private TResponseMessage internalPost(TRequestMessage requestMessage, Class responseType) throws Exception {
try(CloseableHttpClient client = httpClientBuilder.build()) {
@@ -68,7 +110,7 @@ private static TResponseMessage internalPost
String requestJson = JsonUtils.getAsJsonString(requestMessage);
// Set the JSON String as data:
- httpPost.setEntity(new StringEntity(requestJson));
+ httpPost.setEntity(new StringEntity(requestJson, StandardCharsets.UTF_8));
// Execute the Request:
try(CloseableHttpResponse response = client.execute(httpPost)) {
@@ -93,6 +135,4 @@ private static TResponseMessage internalPost
}
}
}
-
-
}
diff --git a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/http/IHttpClient.java b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/http/IHttpClient.java
new file mode 100644
index 0000000..bec3e48
--- /dev/null
+++ b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/http/IHttpClient.java
@@ -0,0 +1,15 @@
+// Copyright (c) Philipp Wagner. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+package de.bytefish.fcmjava.client.http;
+
+/**
+ * An HttpClient is used to send Requests to FCM.
+ */
+public interface IHttpClient {
+
+ void post(TRequestMessage requestMessage);
+
+ TResponseMessage post(TRequestMessage requestMessage, Class responseType);
+
+}
diff --git a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/interceptors/request/AuthenticationRequestInterceptor.java b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/interceptors/request/AuthenticationRequestInterceptor.java
index f897b43..29a7392 100644
--- a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/interceptors/request/AuthenticationRequestInterceptor.java
+++ b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/interceptors/request/AuthenticationRequestInterceptor.java
@@ -10,10 +10,18 @@
import java.io.IOException;
+/**
+ * This RequestInterceptor adds the API Key Request Header.
+ */
public class AuthenticationRequestInterceptor implements HttpRequestInterceptor {
private final String apiKey;
+ /**
+ * Instantiates a new RequestInterceptor with the given API Key.
+ *
+ * @param apiKey API Key used for Requests to FCM
+ */
public AuthenticationRequestInterceptor(String apiKey) {
this.apiKey = apiKey;
}
diff --git a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/interceptors/request/JsonRequestInterceptor.java b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/interceptors/request/JsonRequestInterceptor.java
index 12edc1a..7a6b7ff 100644
--- a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/interceptors/request/JsonRequestInterceptor.java
+++ b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/interceptors/request/JsonRequestInterceptor.java
@@ -11,6 +11,9 @@
import java.io.IOException;
+/**
+ * This RequestInterceptor sets the Request Content-Type to application/json.
+ */
public class JsonRequestInterceptor implements HttpRequestInterceptor {
@Override
diff --git a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/interceptors/request/LoggingRequestInterceptor.java b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/interceptors/request/LoggingRequestInterceptor.java
deleted file mode 100644
index 30028d3..0000000
--- a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/interceptors/request/LoggingRequestInterceptor.java
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) Philipp Wagner. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-package de.bytefish.fcmjava.client.interceptors.request;
-
-import org.apache.http.*;
-import org.apache.http.protocol.HttpContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-
-public class LoggingRequestInterceptor implements HttpRequestInterceptor {
-
- private static final Logger log = LoggerFactory.getLogger(LoggingRequestInterceptor.class);
-
- @Override
- public void process(HttpRequest httpRequest, HttpContext httpContext) throws HttpException, IOException {
- if(log.isDebugEnabled()) {
- log.debug(httpRequest.toString());
- }
- }
-}
diff --git a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/interceptors/response/LoggingResponseInterceptor.java b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/interceptors/response/LoggingResponseInterceptor.java
deleted file mode 100644
index c7f095c..0000000
--- a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/interceptors/response/LoggingResponseInterceptor.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) Philipp Wagner. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-package de.bytefish.fcmjava.client.interceptors.response;
-
-import org.apache.http.HttpException;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpResponseInterceptor;
-import org.apache.http.protocol.HttpContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-
-public class LoggingResponseInterceptor implements HttpResponseInterceptor {
-
- private static final Logger log = LoggerFactory.getLogger(LoggingResponseInterceptor.class);
-
- @Override
- public void process(HttpResponse httpResponse, HttpContext httpContext) throws HttpException, IOException {
- if(log.isDebugEnabled()) {
- log.debug(httpResponse.toString());
- }
- }
-}
diff --git a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/interceptors/response/StatusResponseInterceptor.java b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/interceptors/response/StatusResponseInterceptor.java
index ce5d45c..02389c9 100644
--- a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/interceptors/response/StatusResponseInterceptor.java
+++ b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/interceptors/response/StatusResponseInterceptor.java
@@ -3,29 +3,28 @@
package de.bytefish.fcmjava.client.interceptors.response;
-import de.bytefish.fcmjava.exceptions.FcmAuthenticationException;
-import de.bytefish.fcmjava.exceptions.FcmBadRequestException;
-import de.bytefish.fcmjava.exceptions.FcmGeneralException;
-import de.bytefish.fcmjava.exceptions.FcmUnavailableException;
-import org.apache.http.HttpException;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpResponseInterceptor;
-import org.apache.http.HttpStatus;
+import de.bytefish.fcmjava.client.interceptors.response.utils.RetryHeaderUtils;
+import de.bytefish.fcmjava.client.utils.DateUtils;
+import de.bytefish.fcmjava.client.utils.OutParameter;
+import de.bytefish.fcmjava.exceptions.*;
+import org.apache.http.*;
import org.apache.http.protocol.HttpContext;
import java.io.IOException;
+import java.time.*;
+import java.time.format.DateTimeFormatter;
public class StatusResponseInterceptor implements HttpResponseInterceptor {
@Override
public void process(HttpResponse httpResponse, HttpContext httpContext) throws HttpException, IOException {
// Early exit, if there is no HTTP Response:
- if(httpResponse == null) {
+ if (httpResponse == null) {
return;
}
// Early exit, if we can't determine the Status:
- if(httpResponse.getStatusLine() == null) {
+ if (httpResponse.getStatusLine() == null) {
return;
}
@@ -33,25 +32,37 @@ public void process(HttpResponse httpResponse, HttpContext httpContext) throws H
int httpStatusCode = httpResponse.getStatusLine().getStatusCode();
// Is it OK? So we can exit here:
- if (httpStatusCode == HttpStatus.SC_OK)
- {
+ if (httpStatusCode == HttpStatus.SC_OK) {
return;
}
// The Error Reason:
String reasonPhrase = httpResponse.getStatusLine().getReasonPhrase();
- // Now throw the right exception at the user:
- switch (httpStatusCode)
- {
- case HttpStatus.SC_BAD_REQUEST:
- throw new FcmBadRequestException(reasonPhrase);
- case HttpStatus.SC_UNAUTHORIZED:
- throw new FcmAuthenticationException(reasonPhrase);
- case HttpStatus.SC_SERVICE_UNAVAILABLE:
- throw new FcmUnavailableException(reasonPhrase);
- default:
- throw new FcmGeneralException(reasonPhrase);
+ // If it is a Bad Request, we could not retry it:
+ if (httpStatusCode == HttpStatus.SC_BAD_REQUEST) {
+ throw new FcmBadRequestException(reasonPhrase);
}
+
+ // If we are unauthorized, we could not retry it:
+ if (httpStatusCode == HttpStatus.SC_UNAUTHORIZED) {
+ throw new FcmAuthenticationException(reasonPhrase);
+ }
+
+ // Any Status Code between 500 and 600 could be retried:
+ if (httpStatusCode >= 500 && httpStatusCode < 600) {
+
+ // Holds the Duration, which has been sent by the Server:
+ OutParameter result = new OutParameter<>();
+
+ // Try to determine the next interval we can send at:
+ if (RetryHeaderUtils.tryDetermineRetryDelay(httpResponse, result)) {
+ throw new FcmRetryAfterException(result.get(), reasonPhrase);
+ }
+ }
+
+ throw new FcmGeneralException(reasonPhrase);
}
-}
+
+
+}
\ No newline at end of file
diff --git a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/interceptors/response/utils/RetryHeaderUtils.java b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/interceptors/response/utils/RetryHeaderUtils.java
new file mode 100644
index 0000000..665f6c0
--- /dev/null
+++ b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/interceptors/response/utils/RetryHeaderUtils.java
@@ -0,0 +1,128 @@
+// Copyright (c) Philipp Wagner. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+package de.bytefish.fcmjava.client.interceptors.response.utils;
+
+import de.bytefish.fcmjava.client.utils.DateUtils;
+import de.bytefish.fcmjava.client.utils.OutParameter;
+import de.bytefish.fcmjava.client.utils.StringUtils;
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
+
+import java.time.Duration;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+
+public class RetryHeaderUtils {
+
+ public static boolean tryDetermineRetryDelay(HttpResponse httpResponse, OutParameter result) {
+ try {
+ return internalTryDetermineRetryDelay(httpResponse, result);
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ private static boolean internalTryDetermineRetryDelay(HttpResponse httpResponse, OutParameter result) {
+
+ // Try to get the Retry-After Header send by FCM:
+ Header retryAfterHeader = httpResponse.getFirstHeader("Retry-After");
+
+ // Early exit, if we do not have a Retry Header:
+ if (retryAfterHeader == null) {
+ return false;
+ }
+
+ // Try to get the Value:
+ String retryDelayAsString = retryAfterHeader.getValue();
+
+ // Early exit, if the Retry Header has no Value:
+ if(StringUtils.isNullOrWhiteSpace(retryDelayAsString)) {
+ return false;
+ }
+
+ // First check if we have a Number Retry Delay as Seconds:
+ if(tryGetFromLong(retryDelayAsString, result)) {
+ return true;
+ }
+
+ // Then check if we have a RFC1123-compliant date:
+ if(tryGetFromDate(retryDelayAsString, result)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private static boolean tryGetFromLong(String retryDelayAsString, OutParameter result) {
+
+ // Try to convert the String to a Long:
+ OutParameter longResult = new OutParameter<>();
+
+ if(!tryConvertToLong(retryDelayAsString, longResult)) {
+ return false;
+ }
+
+ // If we can convert it to Long, then convert to a Duration in seconds:
+ Duration retryDelayAsDuration = Duration.ofSeconds(longResult.get());
+
+ // Set in the Out Parameter:
+ result.set(retryDelayAsDuration);
+
+ return true;
+ }
+
+ private static boolean tryConvertToLong(String longAsString, OutParameter result) {
+ try {
+ result.set(Long.parseLong(longAsString));
+
+ return true;
+ } catch(Exception e) {
+ return false;
+ }
+ }
+
+ private static boolean tryGetFromDate(String dateAsString, OutParameter result) {
+
+ // Try to convert the String to a RFC1123-compliant Zoned DateTime
+ OutParameter resultDate = new OutParameter<>();
+
+ if(!tryToConvertToDate(dateAsString, resultDate)) {
+ return false;
+ }
+
+ // Get the UTC Now DateTime and the Retry DateTime in UTC Time Zone:
+ ZonedDateTime utcNowDateTime = DateUtils.getUtcNow();
+ ZonedDateTime nextRetryDateTime = resultDate.get().withZoneSameInstant(ZoneOffset.UTC);
+
+ // Calculate Duration between both as the Retry Delay:
+ Duration durationToNextRetryTime = Duration.between(utcNowDateTime, nextRetryDateTime);
+
+ // Negative Retry Delays should not be allowed:
+ if(durationToNextRetryTime.getSeconds() < 0) {
+ durationToNextRetryTime = Duration.ofSeconds(0);
+ }
+
+ // Set it as Result:
+ result.set(durationToNextRetryTime);
+
+ // And return success:
+ return true;
+ }
+
+ private static boolean tryToConvertToDate(String dateAsString, OutParameter result) {
+ try {
+
+ // We assume the HTTP Header to contain an RFC1123-compliant DateTime value:
+ DateTimeFormatter formatter = DateTimeFormatter.RFC_1123_DATE_TIME;
+
+ // Try to parse and set it as the result:
+ result.set(ZonedDateTime.parse(dateAsString, formatter));
+
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+}
diff --git a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/retry/RetryUtils.java b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/retry/RetryUtils.java
new file mode 100644
index 0000000..8a32c8e
--- /dev/null
+++ b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/retry/RetryUtils.java
@@ -0,0 +1,64 @@
+// Copyright (c) Philipp Wagner. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+package de.bytefish.fcmjava.client.retry;
+
+import de.bytefish.fcmjava.client.functional.Action0;
+import de.bytefish.fcmjava.client.functional.Func1;
+import de.bytefish.fcmjava.client.retry.strategy.IRetryStrategy;
+import de.bytefish.fcmjava.client.retry.strategy.SimpleRetryStrategy;
+
+/**
+ * This class implements RetryStrategies, for explicitly retrying requests to the FCM server.
+ */
+public class RetryUtils {
+
+ /**
+ * Retries a method with the SimpleRetryStrategy and a maximum amount of retries.
+ *
+ * @param function Function to retry.
+ * @param maxRetries The Maximum Number of Retries.
+ * @param The Result of the Method.
+ * @return Result of the Method invocation.
+ */
+ public static TResult getWithRetry(Func1 function, int maxRetries) {
+ IRetryStrategy retryStrategy = new SimpleRetryStrategy(maxRetries);
+
+ return getWithRetry(function, retryStrategy);
+ }
+
+ /**
+ * Retries a method with the given Retry Strategy.
+ *
+ * @param function Function to retry.
+ * @param retryStrategy RetryStrategy to apply.
+ * @param Result of the invocation.
+ * @return Result of the Method invocation.
+ */
+ public static TResult getWithRetry(Func1 function, IRetryStrategy retryStrategy) {
+ return retryStrategy.getWithRetry(function);
+ }
+
+ /**
+ * Retries a method with the SimpleRetryStrategy and a maximum amount of retries.
+ *
+ * @param action Action to retry.
+ * @param maxRetries The Maximum Number of Retries.
+ */
+ public static void doWithRetry(Action0 action, int maxRetries) {
+ IRetryStrategy retryStrategy = new SimpleRetryStrategy(maxRetries);
+
+ doWithRetry(action, retryStrategy);
+ }
+
+ /**
+ * Retries a method with the given Retry Strategy.
+ *
+ * @param action Action to retry.
+ * @param retryStrategy RetryStrategy to apply.
+ */
+ public static void doWithRetry(Action0 action, IRetryStrategy retryStrategy) {
+ retryStrategy.doWithRetry(action);
+ }
+
+}
diff --git a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/retry/strategy/IRetryStrategy.java b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/retry/strategy/IRetryStrategy.java
new file mode 100644
index 0000000..0d2991e
--- /dev/null
+++ b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/retry/strategy/IRetryStrategy.java
@@ -0,0 +1,31 @@
+// Copyright (c) Philipp Wagner. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+package de.bytefish.fcmjava.client.retry.strategy;
+
+import de.bytefish.fcmjava.client.functional.Action0;
+import de.bytefish.fcmjava.client.functional.Func1;
+
+/**
+ * A Retry Strategy used to retry a function without a return value (@see {@link Action0}) and
+ * functions with return values (@see {@link Func1}.
+ */
+public interface IRetryStrategy {
+
+ /**
+ * Retries a function without a return value.
+ *
+ * @param action Action to invoke.
+ */
+ void doWithRetry(Action0 action);
+
+ /**
+ * Retries a function with a return values.
+ *
+ * @param function Function to invoke.
+ * @param Result of the invocation.
+ * @return Result of the invocation.
+ */
+ TResult getWithRetry(Func1 function);
+
+}
diff --git a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/retry/strategy/SimpleRetryStrategy.java b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/retry/strategy/SimpleRetryStrategy.java
new file mode 100644
index 0000000..c019120
--- /dev/null
+++ b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/retry/strategy/SimpleRetryStrategy.java
@@ -0,0 +1,76 @@
+// Copyright (c) Philipp Wagner. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+package de.bytefish.fcmjava.client.retry.strategy;
+
+import de.bytefish.fcmjava.client.functional.Action0;
+import de.bytefish.fcmjava.client.functional.Func1;
+import de.bytefish.fcmjava.exceptions.FcmRetryAfterException;
+import de.bytefish.fcmjava.http.options.IFcmClientSettings;
+
+import java.time.Duration;
+
+/**
+ * The SimpleRetryStrategy retries all methods, that throw a @see {@link FcmRetryAfterException} for a
+ * maximum number of retries.
+ *
+ * The @see {@link FcmRetryAfterException} includes a Retry Delay, which indicates when the method
+ * should be retried. This Strategy waits for the amount of time given in the @see {@link FcmRetryAfterException}
+ * and waits for a fixed amount of time.
+ */
+public class SimpleRetryStrategy implements IRetryStrategy {
+
+ private final int maxRetries;
+
+ public SimpleRetryStrategy(int maxRetries) {
+ this.maxRetries = maxRetries;
+ }
+
+ @Override
+ public void doWithRetry(Action0 action) {
+ getWithRetry(() -> {
+ action.invoke();
+
+ return null;
+ });
+ }
+
+ @Override
+ public TResult getWithRetry(Func1 function) {
+
+ // Holds the current Retry Count:
+ int currentRetryCount = 0;
+
+ // Holds the Return Value:
+ TResult returnValue = null;
+
+ // Simple Retry Loop with Thread Sleep for waiting:
+ do {
+ try {
+ returnValue = function.invoke();
+ // Break out of Loop, if there was no exception:
+ break;
+ } catch(FcmRetryAfterException e) {
+ currentRetryCount = currentRetryCount + 1;
+ // If we hit the maximum retry count, then throw the Exception:
+ if(currentRetryCount == maxRetries) {
+ throw e;
+ }
+ // Sleep for the amount of time returned by FCM:
+ internalSleep(e.getRetryDelay());
+ }
+ } while(currentRetryCount <= maxRetries);
+
+ // And finally return the result:
+ return returnValue;
+ }
+
+ private void internalSleep(Duration duration) {
+ try {
+ Thread.sleep(duration.toMillis());
+ } catch(InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/settings/PropertiesBasedSettings.java b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/settings/PropertiesBasedSettings.java
index a199be0..c6abc6f 100644
--- a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/settings/PropertiesBasedSettings.java
+++ b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/settings/PropertiesBasedSettings.java
@@ -10,7 +10,6 @@
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystems;
-import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Properties;
@@ -20,10 +19,9 @@
public class PropertiesBasedSettings implements IFcmClientSettings {
private final String fcmUrl;
-
private final String fcmApiKey;
- protected PropertiesBasedSettings(Properties properties) {
+ private PropertiesBasedSettings(Properties properties) {
fcmUrl = properties.getProperty("fcm.api.url", Constants.FCM_URL);
fcmApiKey = properties.getProperty("fcm.api.key");
}
@@ -77,4 +75,15 @@ public static PropertiesBasedSettings createFromSystemProperties() {
return new PropertiesBasedSettings(properties);
}
+
+ /**
+ * Reads the properties from a Properties object.
+ *
+ * @param properties Properties instance
+ * @return Initialized Client Settings
+ */
+ public static PropertiesBasedSettings createFromProperties(Properties properties) {
+ return new PropertiesBasedSettings(properties);
+ }
+
}
diff --git a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/utils/DateUtils.java b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/utils/DateUtils.java
similarity index 50%
rename from FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/utils/DateUtils.java
rename to FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/utils/DateUtils.java
index 14c75f2..ceb5e46 100644
--- a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/utils/DateUtils.java
+++ b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/utils/DateUtils.java
@@ -1,13 +1,15 @@
-// Copyright (c) Philipp Wagner. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-package de.bytefish.fcmjava.utils;
+package de.bytefish.fcmjava.client.utils;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
public class DateUtils {
+ /**
+ * Gets the current UTC DateTime.
+ *
+ * @return Current UTC DateTime
+ */
public static ZonedDateTime getUtcNow() {
return ZonedDateTime.now(ZoneOffset.UTC);
}
diff --git a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/utils/JsonUtils.java b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/utils/JsonUtils.java
index a368b12..7326425 100644
--- a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/utils/JsonUtils.java
+++ b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/utils/JsonUtils.java
@@ -5,10 +5,19 @@
import com.fasterxml.jackson.databind.ObjectMapper;
+/**
+ * Utility Methods to simplify JSON Serialization and Deserialization with Jackson.
+ */
public class JsonUtils {
private static final ObjectMapper mapper = new ObjectMapper();
+ /**
+ * Returns the given Entity as a JSON String.
+ * @param source The Source object, which should be annotated-
+ * @param Type of the Source object.
+ * @return String representation of the Java object.
+ */
public static String getAsJsonString(TEntity source) {
try {
return internalGetAsJsonString(source);
@@ -17,6 +26,13 @@ public static String getAsJsonString(TEntity source) {
}
}
+ /**
+ * Deserializes a JSON String into a Java Object.
+ * @param source The Source JSON
+ * @param valueType The Class to deserialize into.
+ * @param The type of the Java class.
+ * @return A deserialized object from the given JSON data.
+ */
public static TEntity getEntityFromString(String source, Class valueType) {
try {
return internalGetEntityFromString(source, valueType);
diff --git a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/utils/OutParameter.java b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/utils/OutParameter.java
new file mode 100644
index 0000000..f7f5cdc
--- /dev/null
+++ b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/utils/OutParameter.java
@@ -0,0 +1,44 @@
+package de.bytefish.fcmjava.client.utils;
+
+/**
+ * Out Parameter to enable try-Methods for simpler code. A try method using an
+ * OutParameter should always initialize the OutParameter first, so you have a
+ * valid reference.
+ *
+ * @param Out Result
+ */
+public class OutParameter {
+
+ private E ref;
+
+ public OutParameter() {
+ }
+
+ /**
+ * Gets the Result of the OutParameter.
+ *
+ * @return Result
+ */
+ public E get() {
+ return ref;
+ }
+
+ /**
+ * Sets the OutParameter.
+ *
+ * @param e Result
+ */
+ public void set(E e) {
+ this.ref = e;
+ }
+
+ /**
+ * Overrides the toString Method to print the reference
+ * of the OutParameter instead of itself.
+ *
+ * @return String Representation of the Result.
+ */
+ public String toString() {
+ return ref.toString();
+ }
+}
diff --git a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/utils/PropertiesUtils.java b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/utils/PropertiesUtils.java
index 687084d..563896c 100644
--- a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/utils/PropertiesUtils.java
+++ b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/utils/PropertiesUtils.java
@@ -12,6 +12,13 @@
public class PropertiesUtils {
+ /**
+ * Loads a Poperties file from a given Path using a given Charset.
+ *
+ * @param path The File to read the Properties from.
+ * @param charset The Charset used for reading the Properties file.
+ * @return The Properties of the given file.
+ */
public static Properties loadProperties(Path path, Charset charset) {
try {
// Get a Reader for the given File:
@@ -23,6 +30,12 @@ public static Properties loadProperties(Path path, Charset charset) {
}
}
+ /**
+ * Loads Properties from a given Reader.
+ *
+ * @param reader The reader used for parsing the Properties.
+ * @return The Properties of the given Reader.
+ */
public static Properties loadProperties(Reader reader) {
Properties properties = new Properties();
try {
diff --git a/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/utils/StringUtils.java b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/utils/StringUtils.java
new file mode 100644
index 0000000..374cf74
--- /dev/null
+++ b/FcmJava/fcmjava-client/src/main/java/de/bytefish/fcmjava/client/utils/StringUtils.java
@@ -0,0 +1,20 @@
+// Copyright (c) Philipp Wagner. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+package de.bytefish.fcmjava.client.utils;
+
+public class StringUtils {
+
+ private StringUtils() {
+ }
+
+ /**
+ * Returns true, if a string is null or only contains of Whitespace characters.
+ *
+ * @param input Input string
+ * @return true, if string is null or a whitespace character
+ */
+ public static boolean isNullOrWhiteSpace(String input) {
+ return input == null || input.trim().length() == 0;
+ }
+}
\ No newline at end of file
diff --git a/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/HttpBuilderConfigurationTest.java b/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/HttpBuilderConfigurationTest.java
new file mode 100644
index 0000000..0b1e364
--- /dev/null
+++ b/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/HttpBuilderConfigurationTest.java
@@ -0,0 +1,63 @@
+// Copyright (c) Philipp Wagner. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+package de.bytefish.fcmjava.client.tests;
+
+import de.bytefish.fcmjava.client.FcmClient;
+import de.bytefish.fcmjava.client.http.HttpClient;
+import de.bytefish.fcmjava.http.client.IFcmClient;
+import de.bytefish.fcmjava.http.options.IFcmClientSettings;
+import org.apache.http.HttpHost;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.client.ProxyAuthenticationStrategy;
+import org.junit.Test;
+
+class FakeFcmClientSettings implements IFcmClientSettings {
+
+ @Override
+ public String getFcmUrl() {
+ return "";
+ }
+
+ @Override
+ public String getApiKey() {
+ return "";
+ }
+}
+
+public class HttpBuilderConfigurationTest {
+
+
+ @Test
+ public void testFcmClientWithProxySettings() {
+
+ // Create Settings:
+ IFcmClientSettings settings = new FakeFcmClientSettings();
+
+ // Create the HttpClient:
+ HttpClient httpClient = new HttpClient(settings);
+
+ // And configure the HttpClient:
+ httpClient.configure((httpClientBuilder -> {
+
+ // Define the Credentials to be used:
+ BasicCredentialsProvider basicCredentialsProvider = new BasicCredentialsProvider();
+
+ // Set the Credentials (any auth scope used):
+ basicCredentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("your_username", "your_password"));
+
+ httpClientBuilder
+ // Set the Proxy Address:
+ .setProxy(new HttpHost("your_hostname", 1234))
+ // Set the Authentication Strategy:
+ .setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy())
+ // Set the Credentials Provider we built above:
+ .setDefaultCredentialsProvider(basicCredentialsProvider);
+ }));
+
+ // Finally build the FcmClient:
+ IFcmClient client = new FcmClient(settings, httpClient);
+ }
+}
diff --git a/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/integration/FcmClientIntegrationTest.java b/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/integration/FcmClientIntegrationTest.java
similarity index 88%
rename from FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/integration/FcmClientIntegrationTest.java
rename to FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/integration/FcmClientIntegrationTest.java
index 33ce581..358a2de 100644
--- a/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/integration/FcmClientIntegrationTest.java
+++ b/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/integration/FcmClientIntegrationTest.java
@@ -1,16 +1,16 @@
// Copyright (c) Philipp Wagner. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-package de.bytefish.fcmjava.integration;
+package de.bytefish.fcmjava.client.tests.integration;
import com.fasterxml.jackson.annotation.JsonProperty;
import de.bytefish.fcmjava.client.FcmClient;
-import de.bytefish.fcmjava.client.settings.*;
+import de.bytefish.fcmjava.client.settings.PropertiesBasedSettings;
import de.bytefish.fcmjava.model.options.FcmMessageOptions;
import de.bytefish.fcmjava.model.topics.Topic;
import de.bytefish.fcmjava.requests.data.DataMulticastMessage;
import de.bytefish.fcmjava.requests.topic.TopicUnicastMessage;
-import de.bytefish.fcmjava.responses.MulticastMessageResponse;
+import de.bytefish.fcmjava.responses.FcmMessageResponse;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
@@ -73,7 +73,7 @@ public void SendDataMulticastMessageTest() throws Exception {
registrationIds.add("invalid_key");
// Send a Message:
- MulticastMessageResponse msgResponse = client.send(new DataMulticastMessage(options, registrationIds, new PersonData("Philipp", "Wagner")));
+ FcmMessageResponse msgResponse = client.send(new DataMulticastMessage(options, registrationIds, new PersonData("Philipp", "Wagner")));
Assert.assertNotNull(msgResponse);
}
diff --git a/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/integration/WeatherWarningIntegrationTest.java b/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/integration/WeatherWarningIntegrationTest.java
similarity index 98%
rename from FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/integration/WeatherWarningIntegrationTest.java
rename to FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/integration/WeatherWarningIntegrationTest.java
index 1325447..9dd45ff 100644
--- a/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/integration/WeatherWarningIntegrationTest.java
+++ b/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/integration/WeatherWarningIntegrationTest.java
@@ -1,12 +1,12 @@
// Copyright (c) Philipp Wagner. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-package de.bytefish.fcmjava.integration;
+package de.bytefish.fcmjava.client.tests.integration;
import com.fasterxml.jackson.annotation.JsonProperty;
import de.bytefish.fcmjava.client.FcmClient;
import de.bytefish.fcmjava.client.settings.PropertiesBasedSettings;
-import de.bytefish.fcmjava.integration.utils.DateUtils;
+import de.bytefish.fcmjava.client.tests.testutils.DateUtils;
import de.bytefish.fcmjava.model.options.FcmMessageOptions;
import de.bytefish.fcmjava.model.topics.Topic;
import de.bytefish.fcmjava.requests.topic.TopicUnicastMessage;
diff --git a/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/interceptors/response/utils/RetryHeaderUtilsTest.java b/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/interceptors/response/utils/RetryHeaderUtilsTest.java
new file mode 100644
index 0000000..a43fae7
--- /dev/null
+++ b/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/interceptors/response/utils/RetryHeaderUtilsTest.java
@@ -0,0 +1,135 @@
+// Copyright (c) Philipp Wagner. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+package de.bytefish.fcmjava.client.tests.interceptors.response.utils;
+
+import de.bytefish.fcmjava.client.interceptors.response.utils.RetryHeaderUtils;
+import de.bytefish.fcmjava.client.utils.DateUtils;
+import de.bytefish.fcmjava.client.utils.OutParameter;
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+import java.time.Duration;
+import java.time.format.DateTimeFormatter;
+
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+public class RetryHeaderUtilsTest {
+
+ @Mock
+ private HttpResponse httpResponseMock;
+
+ @Mock
+ private Header headerMock;
+
+ @Before
+ public void setup() {
+ initMocks(this);
+ }
+
+ @Test
+ public void noHeaderFoundTest() {
+ when(httpResponseMock.getFirstHeader("Retry-After"))
+ .thenReturn(null);
+
+ // Holds the Result:
+ OutParameter result = new OutParameter<>();
+
+ // Try to get the Result:
+ boolean success = RetryHeaderUtils.tryDetermineRetryDelay(httpResponseMock, result);
+
+ // Assertions:
+ Assert.assertEquals(false, success);
+ }
+
+ @Test
+ public void headerFoundButNoValidContentTest() {
+
+ when(headerMock.getValue())
+ .thenReturn("AX4");
+
+ when(httpResponseMock.getFirstHeader("Retry-After"))
+ .thenReturn(headerMock);
+
+ // Holds the Result:
+ OutParameter result = new OutParameter<>();
+
+ // Try to get the Result:
+ boolean success = RetryHeaderUtils.tryDetermineRetryDelay(httpResponseMock, result);
+
+ // Assertions:
+ Assert.assertEquals(false, success);
+ }
+
+ @Test
+ public void headerFoundWithSecondsContentTest() {
+
+ when(headerMock.getValue())
+ .thenReturn("4");
+
+ when(httpResponseMock.getFirstHeader("Retry-After"))
+ .thenReturn(headerMock);
+
+ // Holds the Result:
+ OutParameter result = new OutParameter<>();
+
+ // Try to get the Result:
+ boolean success = RetryHeaderUtils.tryDetermineRetryDelay(httpResponseMock, result);
+
+ // Assertions:
+ Assert.assertEquals(true, success);
+ Assert.assertEquals(4, result.get().getSeconds());
+ }
+
+ @Test
+ public void headerFoundWithDateTimeInFutureContentTest() {
+
+ // We assume the HTTP Header to contain an RFC1123-compliant DateTime value:
+ DateTimeFormatter formatter = DateTimeFormatter.RFC_1123_DATE_TIME;
+
+ String formattedStringInFuture = formatter.format(DateUtils.getUtcNow().plusYears(1));
+
+ // Expectations
+ when(headerMock.getValue())
+ .thenReturn(formattedStringInFuture);
+
+ when(httpResponseMock.getFirstHeader("Retry-After"))
+ .thenReturn(headerMock);
+
+ // Holds the Result:
+ OutParameter result = new OutParameter<>();
+
+ // Try to get the Result:
+ boolean success = RetryHeaderUtils.tryDetermineRetryDelay(httpResponseMock, result);
+
+ // Assertions:
+ Assert.assertEquals(true, success);
+ Assert.assertNotEquals(0, result.get().getSeconds());
+ Assert.assertTrue(result.get().getSeconds() > 120);
+ }
+
+ @Test
+ public void headerFoundWithNegativeDateTimeContentTest() {
+
+ when(headerMock.getValue())
+ .thenReturn("Tue, 3 Jun 2008 11:05:30 GMT");
+
+ when(httpResponseMock.getFirstHeader("Retry-After"))
+ .thenReturn(headerMock);
+
+ // Holds the Result:
+ OutParameter result = new OutParameter<>();
+
+ // Try to get the Result:
+ boolean success = RetryHeaderUtils.tryDetermineRetryDelay(httpResponseMock, result);
+
+ // Assertions:
+ Assert.assertEquals(true, success);
+ Assert.assertEquals(0, result.get().getSeconds());
+ }
+}
diff --git a/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/retry/FcmClientRetryTest.java b/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/retry/FcmClientRetryTest.java
new file mode 100644
index 0000000..518a3bd
--- /dev/null
+++ b/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/retry/FcmClientRetryTest.java
@@ -0,0 +1,83 @@
+// Copyright (c) Philipp Wagner. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+package de.bytefish.fcmjava.client.tests.retry;
+
+import de.bytefish.fcmjava.client.FcmClient;
+import de.bytefish.fcmjava.client.http.IHttpClient;
+import de.bytefish.fcmjava.client.retry.RetryUtils;
+import de.bytefish.fcmjava.exceptions.FcmRetryAfterException;
+import de.bytefish.fcmjava.http.client.IFcmClient;
+import de.bytefish.fcmjava.http.options.IFcmClientSettings;
+import de.bytefish.fcmjava.client.tests.testutils.TestUtils;
+import de.bytefish.fcmjava.model.builders.FcmMessageOptionsBuilder;
+import de.bytefish.fcmjava.requests.groups.CreateDeviceGroupMessage;
+import de.bytefish.fcmjava.responses.CreateDeviceGroupMessageResponse;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+import java.time.Duration;
+import java.util.ArrayList;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+public class FcmClientRetryTest {
+
+ @Mock
+ private IFcmClientSettings settingsMock;
+
+ @Mock
+ private IHttpClient httpClientMock;
+
+ @Before
+ public void Setup() {
+ initMocks(this);
+ }
+
+ @Test
+ public void retryWithThrowTest() {
+
+ // Fake Message to send:
+ CreateDeviceGroupMessage createDeviceGroupMessage = new CreateDeviceGroupMessage(new FcmMessageOptionsBuilder().build(), new ArrayList<>(), "Unit Test");
+
+ when(httpClientMock.post(createDeviceGroupMessage, CreateDeviceGroupMessageResponse.class))
+ .thenThrow(new FcmRetryAfterException(Duration.ZERO));
+
+ // Create the Test Subject:
+ IFcmClient client = new FcmClient(settingsMock, httpClientMock);
+
+ // Invoke it and make sure it throws:
+ TestUtils.assertThrows(() -> RetryUtils.getWithRetry(() -> client.send(createDeviceGroupMessage), 5), FcmRetryAfterException.class);
+
+ // And finally verify it has been called 5 times as set in the Mock Expectations:
+ verify(httpClientMock, times(5))
+ .post(createDeviceGroupMessage, CreateDeviceGroupMessageResponse.class);
+ }
+
+ @Test
+ public void retryNotNecessaryTest() {
+
+ // Fake Message to send:
+ CreateDeviceGroupMessage createDeviceGroupMessage = new CreateDeviceGroupMessage(new FcmMessageOptionsBuilder().build(), new ArrayList<>(), "Unit Test");
+
+ // Fake Message to receive:
+ CreateDeviceGroupMessageResponse createDeviceGroupMessageResponse = new CreateDeviceGroupMessageResponse("Unit Test");
+
+ when(httpClientMock.post(createDeviceGroupMessage, CreateDeviceGroupMessageResponse.class))
+ .thenReturn(createDeviceGroupMessageResponse);
+
+ // Create the Test Subject:
+ IFcmClient client = new FcmClient(settingsMock, httpClientMock);
+
+ // Invoke it and make sure it throws:
+ TestUtils.assertDoesNotThrow(() -> RetryUtils.getWithRetry(() -> client.send(createDeviceGroupMessage), 5));
+
+ // And finally verify it has been called 5 times as set in the Mock Expectations:
+ verify(httpClientMock, times(1))
+ .post(createDeviceGroupMessage, CreateDeviceGroupMessageResponse.class);
+ }
+}
diff --git a/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/settings/FcmClientSettingsTest.java b/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/settings/FcmClientSettingsTest.java
new file mode 100644
index 0000000..27a429f
--- /dev/null
+++ b/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/settings/FcmClientSettingsTest.java
@@ -0,0 +1,43 @@
+// Copyright (c) Philipp Wagner. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+package de.bytefish.fcmjava.client.tests.settings;
+
+import de.bytefish.fcmjava.client.FcmClient;
+import de.bytefish.fcmjava.constants.Constants;
+import de.bytefish.fcmjava.http.client.IFcmClient;
+import de.bytefish.fcmjava.http.options.IFcmClientSettings;
+import org.junit.Test;
+
+class FixedFcmClientSettings implements IFcmClientSettings {
+
+ private final String apiKey;
+
+ public FixedFcmClientSettings(String apiKey) {
+ this.apiKey = apiKey;
+ }
+
+ @Override
+ public String getFcmUrl() {
+ return Constants.FCM_URL;
+ }
+
+ @Override
+ public String getApiKey() {
+ return apiKey;
+ }
+}
+
+public class FcmClientSettingsTest {
+
+ @Test
+ public void testFixedClientSettings() {
+
+ // Construct the FCM Client Settings with your API Key:
+ IFcmClientSettings clientSettings = new FixedFcmClientSettings("your_api_key_here");
+
+ // Instantiate the FcmClient with the API Key:
+ IFcmClient client = new FcmClient(clientSettings);
+ }
+
+}
diff --git a/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/integration/utils/DateUtils.java b/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/testutils/DateUtils.java
similarity index 92%
rename from FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/integration/utils/DateUtils.java
rename to FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/testutils/DateUtils.java
index 01da785..3e21ee8 100644
--- a/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/integration/utils/DateUtils.java
+++ b/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/testutils/DateUtils.java
@@ -1,7 +1,7 @@
// Copyright (c) Philipp Wagner. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-package de.bytefish.fcmjava.integration.utils;
+package de.bytefish.fcmjava.client.tests.testutils;
import java.time.*;
import java.util.Date;
diff --git a/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/testutils/TestUtils.java b/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/testutils/TestUtils.java
new file mode 100644
index 0000000..a9a55dd
--- /dev/null
+++ b/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/testutils/TestUtils.java
@@ -0,0 +1,27 @@
+// Copyright (c) Philipp Wagner. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+package de.bytefish.fcmjava.client.tests.testutils;
+
+import de.bytefish.fcmjava.client.functional.Action0;
+import org.junit.Assert;
+
+public class TestUtils {
+ public static void assertThrows(Action0 action, Class> expectedException) {
+ Throwable throwable = null;
+ try {
+ action.invoke();
+ } catch(Throwable t) {
+ throwable = t;
+ }
+ if(throwable == null) {
+ Assert.assertEquals(expectedException, null);
+ } else {
+ Assert.assertEquals(expectedException, throwable.getClass());
+ }
+ }
+
+ public static void assertDoesNotThrow(Action0 action) {
+ assertThrows(action, null);
+ }
+}
diff --git a/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/utils/JsonUtilsTest.java b/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/utils/JsonUtilsTest.java
new file mode 100644
index 0000000..cb366ff
--- /dev/null
+++ b/FcmJava/fcmjava-client/src/test/java/de/bytefish/fcmjava/client/tests/utils/JsonUtilsTest.java
@@ -0,0 +1,32 @@
+package de.bytefish.fcmjava.client.tests.utils;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import de.bytefish.fcmjava.client.utils.JsonUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class JsonUtilsTest {
+
+ public class GermanUmlautEntity {
+
+ private final String content;
+
+ @JsonCreator
+ public GermanUmlautEntity(@JsonProperty("content") String content) {
+ this.content = content;
+ }
+
+ public String getContent() {
+ return content;
+ }
+ }
+
+ @Test
+ public void umlautsSerializeTest() {
+ GermanUmlautEntity entity = new GermanUmlautEntity("Bitteschön. Dankeschön.");
+
+ Assert.assertEquals("{\"content\":\"Bitteschön. Dankeschön.\"}", JsonUtils.getAsJsonString(entity));
+ }
+
+}
diff --git a/FcmJava/fcmjava-core/pom.xml b/FcmJava/fcmjava-core/pom.xml
index 57233f7..1e317cd 100644
--- a/FcmJava/fcmjava-core/pom.xml
+++ b/FcmJava/fcmjava-core/pom.xml
@@ -8,7 +8,7 @@
de.bytefish.fcmjava
fcmjava-parent
- 0.4
+ 1.1
..
@@ -19,6 +19,7 @@
UTF-8
+ 2.7.4
@@ -26,13 +27,13 @@
com.fasterxml.jackson.core
jackson-annotations
- 2.6.2
+ ${jackson.version}
com.fasterxml.jackson.core
jackson-databind
- 2.6.2
+ ${jackson.version}
diff --git a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/constants/Constants.java b/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/constants/Constants.java
index de071c7..ebd7d74 100644
--- a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/constants/Constants.java
+++ b/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/constants/Constants.java
@@ -7,10 +7,9 @@ public class Constants {
private Constants() {}
+ /**
+ * The URL of the FCM Endpoint.
+ */
public static String FCM_URL = "https://fcm.googleapis.com/fcm/send";
- public static String FCM_PATH_SEND = "send";
-
- public static String FCM_PATH_NOTIFICATION = "notification";
-
}
diff --git a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/exceptions/FcmAuthenticationException.java b/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/exceptions/FcmAuthenticationException.java
index 11da853..d11cd4b 100644
--- a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/exceptions/FcmAuthenticationException.java
+++ b/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/exceptions/FcmAuthenticationException.java
@@ -3,6 +3,9 @@
package de.bytefish.fcmjava.exceptions;
+/**
+ * This Exception is thrown, if the Authentication with the FCM server failed.
+ */
public class FcmAuthenticationException extends FcmException {
public FcmAuthenticationException() {
diff --git a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/exceptions/FcmBadRequestException.java b/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/exceptions/FcmBadRequestException.java
index 1d5e1ff..cfbd01f 100644
--- a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/exceptions/FcmBadRequestException.java
+++ b/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/exceptions/FcmBadRequestException.java
@@ -3,6 +3,9 @@
package de.bytefish.fcmjava.exceptions;
+/**
+ * This Exception is thrown, if a Bad Request to FCM was made.
+ */
public class FcmBadRequestException extends FcmException {
public FcmBadRequestException() {
diff --git a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/exceptions/FcmRetryAfterException.java b/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/exceptions/FcmRetryAfterException.java
new file mode 100644
index 0000000..e29c255
--- /dev/null
+++ b/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/exceptions/FcmRetryAfterException.java
@@ -0,0 +1,47 @@
+// Copyright (c) Philipp Wagner. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+package de.bytefish.fcmjava.exceptions;
+
+import java.time.Duration;
+
+/**
+ * This Exception is thrown, when a message failed, but we are allowed to Retry it. You have to respect the Retry Delay
+ * associated with this Exception, before you retry the Operation. You can use the RetryUtils to retry the operations.
+ */
+public class FcmRetryAfterException extends FcmException {
+
+ private final Duration retryDelay;
+
+ public FcmRetryAfterException(Duration retryDelay) {
+ this.retryDelay = retryDelay;
+ }
+
+ public FcmRetryAfterException(Duration retryDelay, String message) {
+ super(message);
+
+ this.retryDelay = retryDelay;
+ }
+
+ public FcmRetryAfterException(Duration retryDelay, String message, Throwable cause) {
+ super(message, cause);
+
+ this.retryDelay = retryDelay;
+ }
+
+ public FcmRetryAfterException(Duration retryDelay, Throwable cause) {
+ super(cause);
+
+ this.retryDelay = retryDelay;
+ }
+
+ public FcmRetryAfterException(Duration retryDelay, String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+
+ this.retryDelay = retryDelay;
+ }
+
+ public Duration getRetryDelay() {
+ return retryDelay;
+ }
+}
diff --git a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/exceptions/FcmUnavailableException.java b/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/exceptions/FcmUnavailableException.java
index 021aa9e..5c74c4c 100644
--- a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/exceptions/FcmUnavailableException.java
+++ b/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/exceptions/FcmUnavailableException.java
@@ -3,6 +3,9 @@
package de.bytefish.fcmjava.exceptions;
+/**
+ * This Exception is thrown, if the FCM Server was unavailable. You should retry the Operation using
+ */
public class FcmUnavailableException extends FcmException {
public FcmUnavailableException() {
diff --git a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/http/client/IFcmClient.java b/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/http/client/IFcmClient.java
index 15c1327..4565a18 100644
--- a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/http/client/IFcmClient.java
+++ b/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/http/client/IFcmClient.java
@@ -13,19 +13,18 @@
import de.bytefish.fcmjava.requests.topic.TopicMulticastMessage;
import de.bytefish.fcmjava.requests.topic.TopicUnicastMessage;
import de.bytefish.fcmjava.responses.CreateDeviceGroupMessageResponse;
-import de.bytefish.fcmjava.responses.MulticastMessageResponse;
+import de.bytefish.fcmjava.responses.FcmMessageResponse;
import de.bytefish.fcmjava.responses.TopicMessageResponse;
-import de.bytefish.fcmjava.responses.UnicastMessageResponse;
public interface IFcmClient {
- MulticastMessageResponse send(DataMulticastMessage message);
+ FcmMessageResponse send(DataMulticastMessage message);
- MulticastMessageResponse send(NotificationMulticastMessage notification);
+ FcmMessageResponse send(NotificationMulticastMessage notification);
- UnicastMessageResponse send(DataUnicastMessage message);
+ FcmMessageResponse send(DataUnicastMessage message);
- UnicastMessageResponse send(NotificationUnicastMessage notification);
+ FcmMessageResponse send(NotificationUnicastMessage notification);
CreateDeviceGroupMessageResponse send(CreateDeviceGroupMessage message);
diff --git a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/model/builders/FcmMessageOptionsBuilder.java b/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/model/builders/FcmMessageOptionsBuilder.java
index 73254ca..e375214 100644
--- a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/model/builders/FcmMessageOptionsBuilder.java
+++ b/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/model/builders/FcmMessageOptionsBuilder.java
@@ -5,7 +5,6 @@
import de.bytefish.fcmjava.model.enums.PriorityEnum;
import de.bytefish.fcmjava.model.options.FcmMessageOptions;
-import de.bytefish.fcmjava.utils.StringUtils;
import java.time.Duration;
diff --git a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/model/enums/ErrorCodeEnum.java b/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/model/enums/ErrorCodeEnum.java
index ab7e409..0cc739b 100644
--- a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/model/enums/ErrorCodeEnum.java
+++ b/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/model/enums/ErrorCodeEnum.java
@@ -7,39 +7,45 @@
public enum ErrorCodeEnum
{
+ @JsonProperty("MissingRegistration")
+ MissingRegistration,
+
@JsonProperty("InvalidRegistration")
InvalidRegistration,
@JsonProperty("NotRegistered")
NotRegistered,
- @JsonProperty("MessageTooBig")
- MessageTooBig,
-
- @JsonProperty("MissingRegistration")
- MissingRegistration,
-
- @JsonProperty("Unavailable")
- Unavailable,
+ @JsonProperty("InvalidPackageName")
+ InvalidPackageName,
@JsonProperty("MismatchSenderId")
MismatchSenderId,
+ @JsonProperty("InvalidParameters")
+ InvalidParameters,
+
+ @JsonProperty("MessageTooBig")
+ MessageTooBig,
+
@JsonProperty("InvalidDataKey")
InvalidDataKey,
@JsonProperty("InvalidTtl")
InvalidTtl,
+ @JsonProperty("Unavailable")
+ Unavailable,
+
@JsonProperty("InternalServerError")
InternalServerError,
- @JsonProperty("InvalidPackageName")
- InvalidPackageName,
-
@JsonProperty("DeviceMessageRateExceeded")
DeviceMessageRateExceeded,
@JsonProperty("TopicsMessageRateExceeded")
- TopicsMessageRateExceeded
+ TopicsMessageRateExceeded,
+
+ @JsonProperty("InvalidApnsCredential")
+ InvalidApnsCredential
}
\ No newline at end of file
diff --git a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/model/topics/TopicList.java b/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/model/topics/TopicList.java
index 0bfe5e3..0ccb129 100644
--- a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/model/topics/TopicList.java
+++ b/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/model/topics/TopicList.java
@@ -21,7 +21,7 @@ public List getTopics() {
public String getTopicsCondition() {
return topics.stream()
- .map(topic -> String.format("'%s' in topics", topic))
+ .map(topic -> String.format("'%s' in topics", topic.getName()))
.collect(Collectors.joining(" || "));
}
}
diff --git a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/requests/builders/NotificationPayloadBuilder.java b/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/requests/builders/NotificationPayloadBuilder.java
new file mode 100644
index 0000000..bed64ea
--- /dev/null
+++ b/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/requests/builders/NotificationPayloadBuilder.java
@@ -0,0 +1,106 @@
+package de.bytefish.fcmjava.requests.builders;
+
+import de.bytefish.fcmjava.requests.notification.NotificationPayload;
+import java.util.List;
+
+/**
+ * Builder for creating {@link NotificationPayload} instances.
+ *
+ * All fields are optional, and some of them are common for both Android and iOS and some
+ * of them are specific to Android ({@link #icon}, {@link #tag}, {@link #color})
+ * or specific to iOS ({@link #badge}).
+ *
+ * @author Francisco Aranda (fran.culebras@gmail.com)
+ */
+public class NotificationPayloadBuilder {
+
+ private String title;
+ private String body;
+ private String icon;
+ private String sound;
+ private String badge;
+ private String tag;
+ private String color;
+ private String clickAction;
+ private String bodyLocKey;
+ private List bodyLocKeyArgs;
+ private String titleLocKey;
+ private List titleLocKeyArgs;
+
+ public NotificationPayloadBuilder setTitle(String title) {
+ this.title = title;
+ return this;
+ }
+
+ public NotificationPayloadBuilder setBody(String body) {
+ this.body = body;
+ return this;
+ }
+
+ public NotificationPayloadBuilder setIcon(String icon) {
+ this.icon = icon;
+ return this;
+ }
+
+ public NotificationPayloadBuilder setSound(String sound) {
+ this.sound = sound;
+ return this;
+ }
+
+ public NotificationPayloadBuilder setBadge(String badge) {
+ this.badge = badge;
+ return this;
+ }
+
+ public NotificationPayloadBuilder setTag(String tag) {
+ this.tag = tag;
+ return this;
+ }
+
+ public NotificationPayloadBuilder setColor(String color) {
+ this.color = color;
+ return this;
+ }
+
+ public NotificationPayloadBuilder setClickAction(String clickAction) {
+ this.clickAction = clickAction;
+ return this;
+ }
+
+ public NotificationPayloadBuilder setBodyLocKey(String bodyLocKey) {
+ this.bodyLocKey = bodyLocKey;
+ return this;
+ }
+
+ public NotificationPayloadBuilder setBodyLocKeyArgs(List bodyLocKeyArgs) {
+ this.bodyLocKeyArgs = bodyLocKeyArgs;
+ return this;
+ }
+
+ public NotificationPayloadBuilder setTitleLocKey(String titleLocKey) {
+ this.titleLocKey = titleLocKey;
+ return this;
+ }
+
+ public NotificationPayloadBuilder setTitleLocKeyArgs(List titleLocKeyArgs) {
+ this.titleLocKeyArgs = titleLocKeyArgs;
+ return this;
+ }
+
+ public NotificationPayload build() {
+ return new NotificationPayload(
+ title,
+ body,
+ icon,
+ sound,
+ badge,
+ tag,
+ color,
+ clickAction,
+ bodyLocKey,
+ bodyLocKeyArgs,
+ titleLocKey,
+ titleLocKeyArgs);
+ }
+
+}
diff --git a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/requests/notification/NotificationPayload.java b/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/requests/notification/NotificationPayload.java
index 0a5c1bc..b2e4921 100644
--- a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/requests/notification/NotificationPayload.java
+++ b/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/requests/notification/NotificationPayload.java
@@ -5,6 +5,7 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
+import de.bytefish.fcmjava.requests.builders.NotificationPayloadBuilder;
import java.util.List;
@@ -97,4 +98,8 @@ public String getTitleLocKey() {
public List getTitleLocKeyArgs() {
return titleLocKeyArgs;
}
+
+ public static NotificationPayloadBuilder builder() {
+ return new NotificationPayloadBuilder();
+ }
}
\ No newline at end of file
diff --git a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/requests/topic/TopicMulticastMessage.java b/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/requests/topic/TopicMulticastMessage.java
index fd910d7..f806ff9 100644
--- a/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/requests/topic/TopicMulticastMessage.java
+++ b/FcmJava/fcmjava-core/src/main/java/de/bytefish/fcmjava/requests/topic/TopicMulticastMessage.java
@@ -16,6 +16,14 @@ public class TopicMulticastMessage extends FcmUnicastMessage