TIL 2022.09.14 - Java, APN

anonymous·2022년 9월 14일
0

@VisibleForTesting

  • 비즈니스 로직 담은 클래스와 테스트 클래스 분리된 상황
  • 비즈니스 로직을 담은 클래스의 private 맴버를 테스트 클래스에서는 접근 불가.
  • VisibleForTesting annotation 사용하면 테스트에서 쓸 수 있다.

필요한 의존성

<dependency>
  <groupId>
  com.google.guava
  </groupId>
  <artifactId>
  guava
  </artifactId>
  <version>
  13.0
  </version>
</dependency>

Immutable Map

  • Map 데이터 선언 후 고정되거나 일정하다는 의미 (읽기 전용)
  • 맵에 CUD를 하려하면 UnsupportedOperationException 발생. null 도 불가 .
  • 장점으로는 thread-safe, 효율적 메모리, 타사 라이브러리에 전달 가능.
// Java code illustrating of() method to
// create a ImmutableSet
import java.util.*;
import com.google.common.collect.ImmutableMap;
  
class GfG {
    public static void main(String args[])
    {
        // non-empty immutable set
        ImmutableMap<Integer, String> imap = 
                         ImmutableMap.<Integer, String>builder()
                                                 .put(1, "Geeks")
                                                 .put(2, "For")
                                                 .put(3, "Geeks")
                                                 .build();
  
        // Let's print the set
        System.out.println(imap);
    }
}
// {1 = Geeks, 2 = For, 3 = Geeks}

APNS


Remote notification Process

  • Begins with company server(provider server)
  • Make request with noti. data and UID for user device.
  • Forward request to APNs
  • APNs devliers noti. to user device.
  • OS handles noti. and delivers noti. to app.

Remote noti. process-dev. role

  • Set up provider server
  • Configure app to handle noti. on user's device
  • app : enable push noti. capability in Xcode project
  • Register app with APNs and get global unique device token.
  • provider server uses this token to deliver noti to device.

Noti. request to send to user

  • Make post request and send to APNs
  • Must hava data(JSON payload), device token, request-header fields to specify how to deliver noti, token-based auth, provider server's current auth. token.

Communicating with APNs at launch

func application(_ application: UIApplication,
           didFinishLaunchingWithOptions launchOptions:
           [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
   // Override point for customization after application launch.you’re         
   UIApplication.shared.registerForRemoteNotifications()
   return true
}

func application(_ application: UIApplication,
            didRegisterForRemoteNotificationsWithDeviceToken 
                deviceToken: Data) {
   self.sendDeviceTokenToServer(data: deviceToken)
}

func application(_ application: UIApplication,
            didFailToRegisterForRemoteNotificationsWithError 
                error: Error) {
   // Try again later.
}

Example of Message sent by provider server

// Single
public void sendMessageByToken(String token, String title, String body, String type, Map<String, String> data) throws FirebaseMessagingException {
FirebaseMessaging.getInstance().send(Message.builder()
        .setToken(token)
        .setNotification(Notification.builder()
                .setTitle(title)
                .setBody(body)
                .build())
        .putAllData(data)
        .putData("type", type)
        .putData("body", body)
        .build());
}

// Multi
public void sendMessageByTokenList(List<UserDeviceDTO.UserToken> userTokenList, String title, String body, String type)         throws FirebaseMessagingException {

        log.debug("[sendMessageByTokenList] start");
        int fullSize = 500;
        int successCnt = 0;
        int failCnt = 0;

        int batchCnt = userTokenList.size() / fullSize;

        List<UserDeviceDTO.UserTokenFail> failedUserTokens = new ArrayList<>();
        List<String> tokenList = userTokenList.stream().map(UserDeviceDTO.UserToken::getToken).collect(Collectors.toList());

        for (int cnt = 0; cnt <= batchCnt; cnt++) {
            int start = cnt * fullSize;
            int end = cnt == batchCnt ? tokenList.size() : (cnt + 1) * fullSize;
            log.debug("[sendMessageByTokenList] send token index {} ~ {}", start, end);

            MulticastMessage message = MulticastMessage.builder()
                    .addAllTokens(tokenList.subList(start, end))
                    .setNotification(Notification.builder()
                            .setTitle(title)
                            .setBody(body)
                            .build())
                    .putData("type", type)
                    .putData("body", body)
                    .build();

            BatchResponse response = FirebaseMessaging.getInstance().sendMulticast(message);
            successCnt += response.getSuccessCount();
            if (response.getFailureCount() > 0) {
                failCnt += response.getFailureCount();

                List<SendResponse> responses = response.getResponses();
                for (int i = 0; i < responses.size(); i++) {
                    if (!responses.get(i).isSuccessful()) {
                        int org_index = cnt * fullSize + i;
                        failedUserTokens.add(UserDeviceDTO.UserTokenFail.builder()
                                .userToken(userTokenList.get(org_index))
                                .response(responses.get(i))
                                .build());
                        userTokenList.set(org_index, null);
                    }
                }
            }
        }

        // 실패 데이터 제거
        userTokenList = userTokenList.stream().filter(Objects::nonNull).collect(Collectors.toList());

        // History
        List<PushHistory> pushHistories = new ArrayList<>();
        for (UserDeviceDTO.UserToken userToken : userTokenList) {
            pushHistories.add(PushHistory.builder()
                    .userId(userToken.getUserId())
                    .type(type)
                    .title(title)
                    .body(body)
                    .isChat(true)
                    .status(SUCCESS)
                    .build());
        }
        for (UserDeviceDTO.UserTokenFail userTokenFail : failedUserTokens) {
            pushHistories.add(PushHistory.builder()
                    .userId(userTokenFail.getUserToken().getUserId())
                    .type(type)
                    .title(title)
                    .body(body)
                    .isChat(false)
                    .status(FAIL)
                    .message(userTokenFail.getResponse().getException().getMessage())
                    .build());
        }
        log.debug("[sendMessageByTokenList] pushHistoryRepository saveAll");
        
        pushHistoryRepository.saveAll(pushHistories);
        // Notification
        List<UserNotification> userNotifications = new ArrayList<>();
        for (UserDeviceDTO.UserToken userToken : userTokenList) {
            userNotifications.add(UserNotification.builder()
                    .userId(userToken.getUserId())
                    .type(type)
                    .title(title)
                    .body(body)
                    .isChat(true)
                    .build());
        }
        log.debug("[sendMessageByTokenList] pushHistoryRepository saveAll");
        userNotificationRepository.saveAll(userNotifications);

        log.debug("[sendMessageByTokenList] Success Count: {}, Failure Count: {}", successCnt, failCnt);
}

출처

https://hbase.tistory.com/3
https://hipdizzy.tistory.com/60
https://developer.apple.com/documentation/usernotifications/registering_your_app_with_apns


스프링-에노테이션-용도-OOO-@VisibleForTesting-접근성-OOO
Apple Device-Push-Provider Server-OOO-OS-UID-User Device

profile
기술블로거입니다

0개의 댓글