FCM Integration with Spring Boot To Send Push Notification From Server Side.


Hey Guys, recently i have integrated FCM( Firebase Cloud Messaging) with one of my spring boot application. So I would like to share my experience with you.

Some Basics Steps You Need To Follow:
1) Please make sure client application registered under Firebase.

2) Now we need to generate Firebase SDK admin key. This is basically JSON file and the purpose of this file is for server side authorization.You can get more details under https://firebase.google.com/docs/cloud-messaging/auth-server .

3) To get Admin SDK go to Project Setting -> Service Accounts -> Check Java radio button -> Finally click on Generate new private key and save it.


4) Now we need to use generated private JSON file in our spring boot application.So for this I am assuming you have already Spring Boot application , if you don't have then you can create from here https://start.spring.io/

5) Create one folder under src/main/resources in your spring boot application. In my case I have created fcm folder. Now put you private key under this folder.

6) In your application.properties file create key/value like app.firebase-configuration-file=fcm/dummy-d7bd4-firebase-adminsdk-l3psc-8c2aa79daa.json
Note : value key contains location of your private JSON file.

7) Add Firebase dependency in pom.xml file.

<dependency>
    <groupId>com.google.firebase</groupId>
    <artifactId>firebase-admin</artifactId>
    <version>6.8.1</version>
</dependency>

8) Now we need to initialize Firebase application , for this we need private key location so to access the location we can use @Value annotation with key name. Create class FCMInitializer with file name FCMInitializer.java

package com.pushnotification.fcm.service;

import java.io.IOException;

import javax.annotation.PostConstruct;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;

import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;

@Service
public class FCMInitializer {

    @Value("${app.firebase-configuration-file}")
    private String firebaseConfigPath;

    Logger logger = LoggerFactory.getLogger(FCMInitializer.class);

    @PostConstruct
    public void initialize() {
        try {
            FirebaseOptions options = new FirebaseOptions.Builder()
                    .setCredentials(GoogleCredentials.fromStream(new ClassPathResource(firebaseConfigPath).getInputStream())).build();
            if (FirebaseApp.getApps().isEmpty()) {
                FirebaseApp.initializeApp(options);
                logger.info("Firebase application has been initialized");
            }
        } catch (IOException e) {
            logger.error(e.getMessage());
        }
    }

}

In the above code snippets , initialize() method is being called on application start up due to @PostConstruct annotaction.

9) Create Class FCMService with @Service annotation which contains some different types of methods. Create class with file name FCMService.java

package com.pushnotification.fcm.service;

import java.time.Duration;
import java.util.Map;
import java.util.concurrent.ExecutionException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.google.firebase.messaging.AndroidConfig;
import com.google.firebase.messaging.AndroidNotification;
import com.google.firebase.messaging.ApnsConfig;
import com.google.firebase.messaging.Aps;
import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.Message;
import com.google.firebase.messaging.Notification;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.pushnotification.fcm.model.PushNotificationRequest;

@Service
public class FCMService {

    private Logger logger = LoggerFactory.getLogger(FCMService.class);

    public void sendMessage(Map<String, String> data, PushNotificationRequest request)
            throws InterruptedException, ExecutionException {
        Message message = getPreconfiguredMessageWithData(data, request);
        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        String jsonOutput = gson.toJson(message);
        String response = sendAndGetResponse(message);
        logger.info("Sent message with data. Topic: " + request.getTopic() + ", " + response+ " msg "+jsonOutput);
    }

    public void sendMessageWithoutData(PushNotificationRequest request)
            throws InterruptedException, ExecutionException {
        Message message = getPreconfiguredMessageWithoutData(request);
        String response = sendAndGetResponse(message);
        logger.info("Sent message without data. Topic: " + request.getTopic() + ", " + response);
    }

    public void sendMessageToToken(PushNotificationRequest request)
            throws InterruptedException, ExecutionException {
        Message message = getPreconfiguredMessageToToken(request);
        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        String jsonOutput = gson.toJson(message);
        String response = sendAndGetResponse(message);
        logger.info("Sent message to token. Device token: " + request.getToken() + ", " + response+ " msg "+jsonOutput);
    }

    private String sendAndGetResponse(Message message) throws InterruptedException, ExecutionException {
        return FirebaseMessaging.getInstance().sendAsync(message).get();
    }

    private AndroidConfig getAndroidConfig(String topic) {
        return AndroidConfig.builder()
                .setTtl(Duration.ofMinutes(2).toMillis()).setCollapseKey(topic)
                .setPriority(AndroidConfig.Priority.HIGH)
                .setNotification(AndroidNotification.builder().setSound(NotificationParameter.SOUND.getValue())
                        .setColor(NotificationParameter.COLOR.getValue()).setTag(topic).build()).build();
    }

    private ApnsConfig getApnsConfig(String topic) {
        return ApnsConfig.builder()
                .setAps(Aps.builder().setCategory(topic).setThreadId(topic).build()).build();
    }

    private Message getPreconfiguredMessageToToken(PushNotificationRequest request) {
        return getPreconfiguredMessageBuilder(request).setToken(request.getToken())
                .build();
    }

    private Message getPreconfiguredMessageWithoutData(PushNotificationRequest request) {
        return getPreconfiguredMessageBuilder(request).setTopic(request.getTopic())
                .build();
    }

    private Message getPreconfiguredMessageWithData(Map<String, String> data, PushNotificationRequest request) {
        return getPreconfiguredMessageBuilder(request).putAllData(data).setToken(request.getToken())
                .build();
    }

    private Message.Builder getPreconfiguredMessageBuilder(PushNotificationRequest request) {
        AndroidConfig androidConfig = getAndroidConfig(request.getTopic());
        ApnsConfig apnsConfig = getApnsConfig(request.getTopic());
        return Message.builder()
                .setApnsConfig(apnsConfig).setAndroidConfig(androidConfig).setNotification(
                        new Notification(request.getTitle(), request.getMessage()));
    }
}

10) Now I am going to add one more service layer. Create class PushNotificationService with file name PushNotificationService.java

package com.pushnotification.fcm.service;

import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.pushnotification.fcm.model.PushNotificationRequest;

@Service
public class PushNotificationService {

    private Logger logger = LoggerFactory.getLogger(PushNotificationService.class);
    private FCMService fcmService;

    public PushNotificationService(FCMService fcmService) {
        this.fcmService = fcmService;
    }

    public void sendPushNotification(PushNotificationRequest request) {
        try {
            fcmService.sendMessage(getSamplePayloadData(), request);
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
    }

    public void sendPushNotificationWithoutData(PushNotificationRequest request) {
        try {
            fcmService.sendMessageWithoutData(request);
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
    }

    public void sendPushNotificationToToken(PushNotificationRequest request) {
        try {
            fcmService.sendMessageToToken(request);
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
    }

    private Map<String, String> getSamplePayloadData() {
        Map<String, String> pushData = new HashMap<>();
        pushData.put("messageId", "msgid");
        pushData.put("text", "txt");
        pushData.put("user", "pankaj singh");
        return pushData;
    }
}

Note : Create these all three above files under the service layer.

11) Now create two files under model layer PushNotificationRequest.java , PushNotificationResponse.java

package com.pushnotification.fcm.model;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Setter
@Getter
@NoArgsConstructor
public class PushNotificationRequest {

    private String title;
    private String message;
    private String topic;
    private String token;  
}
package com.pushnotification.fcm.model;
public class PushNotificationResponse {

    private int status;
    private String message;

    public PushNotificationResponse() {
    }

    public PushNotificationResponse(int status, String message) {
        this.status = status;
        this.message = message;
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

12) Create controller class to test push notification.Create file PushNotificationController.java

package com.pushnotification.fcm.controller;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.pushnotification.fcm.model.PushNotificationRequest;
import com.pushnotification.fcm.model.PushNotificationResponse;
import com.pushnotification.fcm.service.PushNotificationService;

@RestController
public class PushNotificationController {

    private PushNotificationService pushNotificationService;

    public PushNotificationController(PushNotificationService pushNotificationService) {
        this.pushNotificationService = pushNotificationService;
    }

    @PostMapping("/notification/topic")
    public ResponseEntity sendNotification(@RequestBody PushNotificationRequest request) {
        pushNotificationService.sendPushNotificationWithoutData(request);
        return new ResponseEntity<>(new PushNotificationResponse(HttpStatus.OK.value(), "Notification has been sent."), HttpStatus.OK);
    }

    @PostMapping("/notification/token")
    public ResponseEntity sendTokenNotification(@RequestBody PushNotificationRequest request) {
        pushNotificationService.sendPushNotificationToToken(request);
        return new ResponseEntity<>(new PushNotificationResponse(HttpStatus.OK.value(), "Notification has been sent."), HttpStatus.OK);
    }

    @PostMapping("/notification/data")
    public ResponseEntity sendDataNotification(@RequestBody PushNotificationRequest request) {
        pushNotificationService.sendPushNotification(request);
        return new ResponseEntity<>(new PushNotificationResponse(HttpStatus.OK.value(), "Notification has been sent."), HttpStatus.OK);
    }   
}

Finally we have configured everything for push notification.So our code is ready to send the push notification.


We have mainly three use cases:
a) Push notification based on topics. In this kind of notification we don't required any device token. So based on topic configuration push notification will work.

For this you can use end point : /notification/topic

b) Push notification with token.With this end point we can send only title and body part.

For this you can use end point : /notification/token

c) Push notification with additional payload data. With this end point we can send title,body and some additional key/pair data.

For this you can use end point : /notification/data

If you want to test please use postman and pass JSON body like :

{
	"title":"Put the push notification title here",
	"message":"Put here push notification body here",
	"token":"f65ewWeHOUs:APA91bH2mVv-cVpjVv4mJZxkugeNoJJ_AL-Bl_Kfosz26rk1nTJREp-_b9eEzojh3GgvGxtu1VXlyXTxc5j_jQNMl0oBEEw2oe_knHVIpvdAKMG6qya6"
}

Note : You can download full working source code from my repository link https://github.com/firststepitsolution/push-notification-with-fcm-and-spring-boot I have tested on both platform (IOS and Android) and its working as expected.

21 thoughts on “FCM Integration with Spring Boot To Send Push Notification From Server Side.”

  1. Wonderful goods from you, man. I’ve consider your stuff prior
    to and you are simply extremely magnificent. I really like what you’ve received right
    here, really like what you’re stating and the best way by which you say it.
    You make it entertaining and you still care for to stay it smart.
    I can not wait to learn much more from you.

    This is really a terrific site. adreamoftrains best website hosting

  2. magnificent publish, very informative. I’m wondering why the opposite experts of this sector don’t realize this.
    You must proceed your writing. I’m sure, you’ve a great
    readers’ base already!

  3. I have already implemented the push notification service and I want to save all notifications in my database so the application user can delete notifications from the app.
    My app currently has around 100,000 users and creating a single database record for each user is a very tedious process.
    Could you please suggest a proper way for this ?

  4. NotificationParameter is a enum.

    @Getter
    @AllArgsConstructor
    public enum NotificationParameter {

    SOUND(“default”), COLOR(“#FFFF00”);
    private String value;

    }

  5. Hiya very cool site!! Man .. Beautiful .. Superb ..
    I’ll bookmark your web site and take the feeds also?
    I am satisfied to find a lot of useful information here in the publish, we want develop extra techniques in this regard, thanks for sharing.
    . . . . .

  6. Magnificent items from you, man. I have remember your stuff
    previous to and you are simply too wonderful. I really like what you’ve got here,
    certainly like what you’re stating and the best way during which you assert
    it. You’re making it entertaining and you still care for to keep it wise.
    I cant wait to learn far more from you. That is actually a tremendous web site.

  7. Hey! This is my first visit to your blog! We are a group of
    volunteers and starting a new initiative in a community in the same niche.
    Your blog provided us valuable information to work on. You have
    done a extraordinary job!

Comments are closed.