본문 바로가기
Java

파이어베이스 다중 푸시

by NaHyungMin 2023. 10. 16.

파이어베이스 홈페이지에서 json을 가져오는 형식과 처음 보내면 서비스 등록하라는 내용은 넘기고 구현체만 남김

단일 푸시지만 ios에서 개인 프로젝트로 만들었기 때문에 프로젝트 합치면서 오류가 날 경우 다른 정보로 보내도록 구현되어 있다.

한 프로젝트로 디바이스 등록하여 사용할거면 이렇게 처리 안해도 됨.

implementation 'com.google.firebase:firebase-admin:9.2.0' //230927 nhm, push

 

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

 

@PostConstruct
private void initFireBaseApp() {
    try {
        ClassPathResource resource = new ClassPathResource("fcm/firebase.json");
        GoogleCredentials credentials = GoogleCredentials.fromStream(resource.getInputStream());

        FirebaseOptions options = FirebaseOptions.builder()
                .setCredentials(credentials)
                .build();

        //23년 -> 25년 안에 제거해야 한다.
        ClassPathResource resourceIosOldVersion = new ClassPathResource("fcm/ios-firebase.json");
        GoogleCredentials credentialsIosOldVersion = GoogleCredentials.fromStream(resourceIosOldVersion.getInputStream());

        FirebaseOptions optionsIosOldVersion = FirebaseOptions.builder()
                .setCredentials(credentialsIosOldVersion)
                .build();

        firebaseApp = FirebaseApp.initializeApp(options, "firebaseApp");
        fireBaseIosOldVersion = FirebaseApp.initializeApp(optionsIosOldVersion, "fireBaseIosOldVersion");
    } catch (FileNotFoundException ex) {
        오류처리("System fcm File Not Found : {}", ex.getMessage());
    } catch (IOException ex) {
        오류처리("System fcm io exception : {}", ex.getMessage());
    }
}

 

public synchronized void androidPush() {
    //android 푸시.
    List<PushAppInformation> androidPushList = pushV2Mapper.selectReservationsPushAppInformation(android);

    if(androidPushList.size() > 0) {
        sendPush(androidPushList);
    }
}

 

private void sendPush(List<PushAppInformation> pushList) {
    for(PushAppInformation pushAppInformation : pushList) {
        List<CompletableFuture> completableFutureList = new ArrayList<>();

        AtomicInteger successCount = new AtomicInteger();
        successCount.set(pushAppInformation.getSuccessCount());
        AtomicInteger failedCount = new AtomicInteger();
        failedCount.set(pushAppInformation.getFailedCount());

        String pushGroup = pushAppInformation.getPushGroup();
        String countryCode = pushAppInformation.getCountryCode();
        String deviceType = pushAppInformation.getDeviceType();

        int pushAppDataCount = pushV2Mapper.countReservationsPushAppData(pushGroup, countryCode, deviceType);

        if(pushAppDataCount > 0) { //0이라면 보내다가 배포로 인해 중단될 가능성 체크.
            try {
                int searchCount = (int) Math.ceil((double) pushAppDataCount / pushMaxCount);
                Queue<List<String>> queue = new LinkedList<>();

                for(int i = 0; i < searchCount; i++) {
                    //카운트 500 단위 0, 500 -> 501, 1000 -> 1001, 1500 ...
                    int startLimit = i * pushMaxCount;

                    queue.offer(selectReservationsPushAppToken(pushGroup, countryCode, deviceType, startLimit, pushMaxCount));
                }

                while (!queue.isEmpty()) {
                    List<String> pushAppDataList = queue.poll();

                    if(pushAppDataList.size() > 0) {
                        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                            try {
                                sendReservationsPushAppData(pushAppInformation, pushAppDataList, successCount, failedCount);
                            } catch (FirebaseMessagingException ex) {
                                오류처리("sendPush device : {}, FirebaseMessagingException exception : {}",  pushAppInformation.getDeviceType(), ex.getMessage());
                            }
                        });

                        completableFutureList.add(future);
                    }
                }

                if(completableFutureList.size() > 0) {
                    CompletableFuture.allOf(completableFutureList.toArray(new CompletableFuture[0]));
                }
            } catch (Exception ex) {
                오류처리("PUSH V2 updateReservationsPushAppInformationComplete ERROR seq : {}, successCount : {}, failedCount : {}, message : {}"
                        , pushAppInformation.getSeq(), successCount, failedCount, ex.getMessage());

                //Message 발송.

                //Information isSend 처리.
                pushV2Mapper.updateReservationsPushAppInformationComplete(pushAppInformation.getSeq(), successCount.get(), failedCount.get());
            }
        }

        로그업데이트("PUSH V2 updateReservationsPushAppInformationComplete seq : {}, successCount : {}, failedCount : {}"
                , pushAppInformation.getSeq(), successCount, failedCount);

        //Information isSend 처리.
        pushV2Mapper.updateReservationsPushAppInformationComplete(pushAppInformation.getSeq(), successCount.get(), failedCount.get());
    }
}

 

private void sendReservationsPushAppData(PushAppInformation pushAppInformation, List<String> pushAppFcmTokenList, AtomicInteger successCount, AtomicInteger failedCount) throws FirebaseMessagingException {
    String pushGroup = pushAppInformation.getPushGroup();
    String deviceType = pushAppInformation.getDeviceType();
    String countryCode = pushAppInformation.getCountryCode();

    BatchResponse response = FirebaseMessaging.getInstance(firebaseApp).sendEachForMulticast(getSendMessage(pushAppInformation, pushAppFcmTokenList));

    Map<String, Object> updateMap = new HashMap<>();
    updateMap.put("infoPushGroup", pushGroup);
    updateMap.put("deviceType", deviceType);
    updateMap.put("countryCode", countryCode);
    updateMap.put("list", pushAppFcmTokenList);

    //update. send
    pushV2Mapper.updateSendReservationsPushAppData(updateMap);

    successCount.set(successCount.get() + response.getSuccessCount());
    failedCount.set(failedCount.get() + response.getFailureCount());

    if (response.getFailureCount() > 0) {
        List<SendResponse> responses = response.getResponses();
        List<String> failedTokens = new ArrayList<>();
        List<String> failedIosTokens = new ArrayList<>();

        for (int i = 0; i < responses.size(); i++) {
            if (!responses.get(i).isSuccessful()) {
                MessagingErrorCode messagingErrorCode = responses.get(i).getException().getMessagingErrorCode();

                if(deviceType.equalsIgnoreCase(ios) && messagingErrorCode.name().equals(MessagingErrorCode.SENDER_ID_MISMATCH.name())) {
                    failedIosTokens.add(pushAppFcmTokenList.get(i));
                } else {
                    failedTokens.add(pushAppFcmTokenList.get(i));
                }
            }
        }

        if(failedIosTokens.size() > 0) {
            //231013 nhm, ios 프로젝트가 개인으로 만들어져 과거 버전 호환해야 한다. failedTokens update
            failedTokens.addAll(sendIosOldVersion(pushAppInformation, failedIosTokens, successCount, failedCount));
        }

        //update. failed
        if(failedTokens.size() > 0) {
            updateMap.put("list", failedTokens);
            pushV2Mapper.updateFailedReservationsPushAppData(updateMap);
        }
    }
}

 

private List<String> sendIosOldVersion(PushAppInformation pushAppInformation, List<String> failedIosTokens, AtomicInteger successCount, AtomicInteger failedCount) throws FirebaseMessagingException {
    List<String> failedTokens = new ArrayList<>();

    try {
        BatchResponse response = FirebaseMessaging.getInstance(fireBaseIosOldVersion).sendEachForMulticast(getSendMessage(pushAppInformation, failedIosTokens));

        successCount.set(successCount.get() + response.getSuccessCount());
        failedCount.set(failedCount.get() - response.getSuccessCount());

        List<SendResponse> responses = response.getResponses();

        for (int i = 0; i < responses.size(); i++) {
            if (!responses.get(i).isSuccessful()) {
                failedTokens.add(failedIosTokens.get(i));
            }
        }
    } catch (Exception ex) {
        ex.printStackTrace();
        오류처리("sendIosOldVersion ERROR : {}", ex.getMessage());
    }

    return failedTokens;
}

 

데이터베이스는 조회용 모테이블과 실제 발송 토큰 자식테이블로 되어 있음.

내부 정보가 있어 비공개 처리.

'Java' 카테고리의 다른 글

구글 드라이브 시트 변경 / Google Drive Api, Slack Api  (1) 2024.01.22
자바 스프링부트 버전업  (1) 2023.12.28
자바 Spring Boot 1.5 동적 Cron  (0) 2023.07.19
Java region  (0) 2023.02.16
자바 열거형 Find Value  (1) 2022.09.23