{getToc} $title={Table of Contents}
Firebase Push Notification with Spring Boot by using HTTP v1 API |
Introduction
In this blog, I'm going to write about creating push nofication service in
Spring Boot
by using
Firebase Migrate from legacy HTTP to HTTP v1.
Apps using the FCM legacy HTTP API should consider migrating to the HTTP v1
API using the instructions in this guide. The HTTP v1 API has these advantages
over the legacy API:
- Better security via access tokens
- More efficient customization of messages across platforms
- More extendable and future-proof for new client platform versions
Keep reading in
firebase official docs.
1. Create Firebase Account
You need to create firebase account in order to create project in firebase. Go
here to
create project.
Provide credentials manually
Firebase projects support Google
service accounts, which you can use to call Firebase server APIs from your app server or
trusted environment. If you're developing code locally or deploying your
application on-premises, you can use credentials obtained via this service
account to authorize server requests.
To authenticate a service account and authorize it to access Firebase
services, you must generate a private key file in JSON format.
To generate a private key file for your service account:
- In the Firebase console, open Settings > Service Accounts.
- Click Generate New Private Key, then confirm by clicking Generate Key.
- Securely store the JSON file containing the key.
2. Project Setting
After you have successfully generated firebase service account, you'll get
service account json file, that's the private key file in JSON format.
In my csae, I'm build the service with spring boot maven project, so you need
to place that service account json file in the resources package in the
project.
place the service account in project path |
3. Set Firebase Key
In your spring boot project, you need to set these firebase keys in the
application.properties file (my case) or applicatin.yml...
#Firebaseio HTTP V1 fcm.service.account=service-account.json fcm.endpoint.url=https://fcm.googleapis.com/v1/projects/gdt-lucky-draw/messages:send google.scope=https://www.googleapis.com/auth/firebase.messaging
- To authorize access to FCM, request the scope
https://www.googleapis.com/auth/firebase.messaging.
- The endpoint URL for the HTTP v1
API https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send
- fcm.service.account is the service account which just have generated in
step 1.
4. Add Dependency
Use credentials to mint access tokens
Use your Firebase credentials together with the
Google Auth Library
for your preferred language to retrieve a short-lived OAuth 2.0 access
token:
If you are using Maven, add this to your pom.xml file (notice that you can
replace google-auth-library-oauth2-http with any of
google-auth-library-credentials and google-auth-library-appengine, depending
on your application needs):
In this case, I'm using spring boot maven project, so we need to add maven
dependeny in the project POM file.
<dependency> <groupId>com.google.auth</groupId> <artifactId>google-auth-library-oauth2-http</artifactId> <version>1.3.0</version> </dependency>
5. Service Implementation
This step, I will do the implementation service. You need to call the
properties value which have already set in application properties file in step
3.
@Value("${google.scope}") private String scopes; @Value("${fcm.endpoint.url}") private String fcm_url; @Value("${fcm.service.account}") private String fcm_svc;
To get Credentials from a Service Account JSON key use
GoogleCredentials.fromStream(InputStream) or
GoogleCredentials.fromStream(InputStream, HttpTransportFactory). Note that the
credentials must be refreshed before the access token is available.
private String getAccessToken() throws IOException { GoogleCredentials googleCredentials = GoogleCredentials .fromStream(new FileInputStream(new ClassPathResource(fcm_svc).getFile())) .createScoped(Arrays.asList(scopes)); googleCredentials.refreshIfExpired(); return googleCredentials.refreshAccessToken().getTokenValue(); }
Send requests
HTTP v1 send requests require an OAuth 2.0 access token. If you are using the
Admin SDK to send messages, the library handles the token for you. If you are
using the raw protocol, obtain the token as described in this section and add
it to the header as Authorization: Bearer <valid Oauth 2.0 token>.
URL url = new URL(fcm_url); HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); httpURLConnection.setUseCaches(false); httpURLConnection.setDoInput(true); httpURLConnection.setDoOutput(true); httpURLConnection.setRequestMethod("POST"); httpURLConnection.setRequestProperty("Authorization", "Bearer " + getAccessToken()); httpURLConnection.setRequestProperty("Content-Type", "application/json");
Payload of send requests
Example: customizing with platform overrides
In addition to simplifying cross-platform targeting of messages, the
HTTP v1 API provides flexibility to customize messages per
platform.
Example: customizing with platform overrides
In addition to simplifying cross-platform targeting of messages, the
HTTP v1 API provides flexibility to customize messages per
platform.
{
"message": {
"topic": "news",
"notification": {
"title": "Breaking News",
"body": "New news story available."
},
"data": {
"story_id": "story_12345"
},
"android": {
"notification": {
"click_action": "TOP_STORY_ACTIVITY",
"body": "Check out the Top Story"
}
},
// For send to iOS
"apns": {
"payload": {
"aps": {
"category" : "NEW_MESSAGE_CATEGORY"
}
}
}
}
}
I have created a function to wrape data as Json Object in order to send
payload to Http V1 API.
/** * Send data message with Firebase Http V1 * @param json_data * @param json_payload * @param userDeviceIdKey * @return * @throws IOException */ private static JSONObject dataMessagesHttpV1(JSONObject json_data, JSONObject json_payload, String userDeviceIdKey) throws IOException { JSONObject json = new JSONObject(); JSONObject json_obj = new JSONObject(); json_obj.put("token", userDeviceIdKey); json_obj.put("data", json_data); json_obj.put("apns", json_payload); json.put("message", json_obj); return json; }
And in the push notification service method, I have added all the payload data
by seperated for each Json Object key value and call the above function
dataMessagesHttpV1 like this.
OutputStreamWriter wr = new OutputStreamWriter(httpURLConnection.getOutputStream(), "UTF-8"); JSONObject json_data = new JSONObject(); JSONObject json_payload = new JSONObject(); JSONObject payload = new JSONObject(); JSONObject obj = new JSONObject(); json_data.put("TITLE_EN", title_en); json_data.put("DESCRIPTION_EN", description_en); json_data.put("NOTI_ID", String.valueOf(noti_id)); payload.put("TITLE_EN", title_en); payload.put("DESCRIPTION_EN", description_en); payload.put("NOTI_ID", String.valueOf(noti_id)); obj.put("content-available", 1); payload.put("aps", obj); json_payload.put("payload", payload); JSONObject req = dataMessagesHttpV1(json_data, json_payload, userDeviceIdKey); wr.write(req.toJSONString()); wr.flush(); wr.close();
I have do logging implementation in order to pring out the logs after push
notification success or faile.
private final Logger logger = LoggerFactory.getLogger(getClass()); logger.info("===( Start PushNotiHttpV1Service response log )==="); int responseCode = httpURLConnection.getResponseCode(); if (responseCode == 200) { String response = inputstreamToString(httpURLConnection.getInputStream()); logger.info("Response Code : " + responseCode); logger.info("Response Message : " + httpURLConnection.getResponseMessage()); logger.info("Sending 'POST' request to URL : " + url); logger.info("Post parameters : " + req); logger.info("Message sent to Firebase for delivery, response:"); logger.info(response); } else { logger.info("Response Code : " + responseCode); logger.info("Response Message : " + httpURLConnection.getResponseMessage()); logger.info("Unable to send message to Firebase:"); String response = inputstreamToString(httpURLConnection.getErrorStream()); logger.info(response); } logger.info("===( End PushNotiHttpV1Service response log )===");
Full HttpV1 Push Notification Service Implementation
import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.net.HttpURLConnection; import java.net.URL; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.Scanner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.ClassPathResource; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import com.alibaba.fastjson.JSONObject; import com.google.auth.oauth2.GoogleCredentials; @Service public class PushNotiHttpV1Service { private final Logger logger = LoggerFactory.getLogger(getClass()); @Value("${google.scope}") private String scopes; @Value("${fcm.endpoint.url}") private String fcm_url; @Value("${fcm.service.account}") private String fcm_svc; // [START use_access_token] private String getAccessToken() throws IOException { GoogleCredentials googleCredentials = GoogleCredentials .fromStream(new FileInputStream(new ClassPathResource(fcm_svc).getFile())) .createScoped(Arrays.asList(scopes)); googleCredentials.refreshIfExpired(); return googleCredentials.refreshAccessToken().getTokenValue(); } // [END use_access_token] /** * Send data message with Firebase Http V1 * @param json_data * @param json_payload * @param userDeviceIdKey * @return * @throws IOException */ private static JSONObject dataMessagesHttpV1(JSONObject json_data, JSONObject json_payload, String userDeviceIdKey) throws IOException { JSONObject json = new JSONObject(); JSONObject json_obj = new JSONObject(); json_obj.put("token", userDeviceIdKey); json_obj.put("data", json_data); json_obj.put("apns", json_payload); json.put("message", json_obj); return json; } /** * Send request to FCM message using HTTP. * Encoded with UTF-8 and support special characters. * @param fcmMessage Body of the HTTP request. * @throws IOException * @param title_en * @param description_en * @param noti_id * @param userDeviceIdKey * @throws IOException */ @Async public void pushNotificationWithJsonData(String title_en, String description_en, long noti_id, String userDeviceIdKey) throws IOException { URL url = new URL(fcm_url); HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); httpURLConnection.setUseCaches(false); httpURLConnection.setDoInput(true); httpURLConnection.setDoOutput(true); httpURLConnection.setRequestMethod("POST"); httpURLConnection.setRequestProperty("Authorization", "Bearer " + getAccessToken()); httpURLConnection.setRequestProperty("Content-Type", "application/json"); OutputStreamWriter wr = new OutputStreamWriter(httpURLConnection.getOutputStream(), "UTF-8"); JSONObject json_data = new JSONObject(); JSONObject json_payload = new JSONObject(); JSONObject payload = new JSONObject(); JSONObject obj = new JSONObject(); json_data.put("TITLE_EN", title_en); json_data.put("DESCRIPTION_EN", description_en); json_data.put("NOTI_ID", String.valueOf(noti_id)); payload.put("TITLE_EN", title_en); payload.put("TITLE_KH", title_kh); payload.put("DESCRIPTION_EN", description_en); payload.put("NOTI_ID", String.valueOf(noti_id)); obj.put("content-available", 1); payload.put("aps", obj); json_payload.put("payload", payload); JSONObject req = dataMessagesHttpV1(json_data, json_payload, userDeviceIdKey); wr.write(req.toJSONString()); wr.flush(); wr.close(); logger.info("===( Start PushNotiHttpV1Service response log )==="); int responseCode = httpURLConnection.getResponseCode(); if (responseCode == 200) { String response = inputstreamToString(httpURLConnection.getInputStream()); logger.info("Response Code : " + responseCode); logger.info("Response Message : " + httpURLConnection.getResponseMessage()); logger.info("Sending 'POST' request to URL : " + url); logger.info("Post parameters : " + req); logger.info("Message sent to Firebase for delivery, response:"); logger.info(response); } else { logger.info("Response Code : " + responseCode); logger.info("Response Message : " + httpURLConnection.getResponseMessage()); logger.info("Unable to send message to Firebase:"); String response = inputstreamToString(httpURLConnection.getErrorStream()); logger.info(response); } logger.info("===( End PushNotiHttpV1Service response log )==="); } /** * Read contents of InputStream into String. * * @param inputStream InputStream to read. * @return String containing contents of InputStream. * @throws IOException */ private static String inputstreamToString(InputStream inputStream) throws IOException { StringBuilder stringBuilder = new StringBuilder(); try (Scanner scanner = new Scanner(inputStream)) { while (scanner.hasNext()) { stringBuilder.append(scanner.nextLine()); } } return stringBuilder.toString(); } }
Lastly, call push notification service from other class
/*Inject service bean*/ @Autowired private PushNotiHttpV1Service pHttpV1Service; --- /** * Push Notification To User */ try { this.pHttpV1Service.pushNotificationWithJsonData( params.get("TITLE_EN").toString(), params.get("DESCRIPTION_EN").toString(), noti_id,notiToken); } catch (Exception e) { e.printStackTrace(); }
Summary
Congratulations! We have just developed a push notification service using
firebase Http V1 API in Spring boot maven project.
Conclusion
Hope this article was helpful.