파이어베이스 홈페이지에서 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 |