From 15753b2ebdac2ab49cf5682c417851a0653e136e Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Tue, 25 Sep 2018 12:49:57 +0300 Subject: notifications server refactoring --- .../java/com/juick/components/APNSManager.java | 140 +++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 juick-notifications/src/main/java/com/juick/components/APNSManager.java (limited to 'juick-notifications/src/main/java/com/juick/components/APNSManager.java') diff --git a/juick-notifications/src/main/java/com/juick/components/APNSManager.java b/juick-notifications/src/main/java/com/juick/components/APNSManager.java new file mode 100644 index 00000000..c0380847 --- /dev/null +++ b/juick-notifications/src/main/java/com/juick/components/APNSManager.java @@ -0,0 +1,140 @@ +package com.juick.components; + +import com.juick.ExternalToken; +import com.juick.User; +import com.juick.formatters.PlainTextFormatter; +import com.juick.service.component.*; +import com.turo.pushy.apns.ApnsClient; +import com.turo.pushy.apns.ApnsClientBuilder; +import com.turo.pushy.apns.PushNotificationResponse; +import com.turo.pushy.apns.auth.ApnsSigningKey; +import com.turo.pushy.apns.util.ApnsPayloadBuilder; +import com.turo.pushy.apns.util.SimpleApnsPushNotification; +import com.turo.pushy.apns.util.concurrent.PushNotificationResponseListener; +import io.netty.util.concurrent.Future; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.inject.Inject; +import java.io.File; +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Date; +import java.util.List; +import java.util.Optional; + +public class APNSManager implements NotificationListener { + private static Logger logger = LoggerFactory.getLogger(APNSManager.class); + + private ApnsClient apns; + @Value("${ios_p8_key:}") + private String p8key; + @Value("${ios_app_id:}") + private String topic; + @Value("${ios_team_id:}") + private String teamId; + @Value("${ios_key_id:}") + private String keyId; + @Inject + private NotificationsManager notificationsManager; + @PostConstruct + public void initialize() throws NoSuchAlgorithmException, InvalidKeyException, IOException { + apns = new ApnsClientBuilder() + .setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST) + .setSigningKey(ApnsSigningKey.loadFromPkcs8File(new File(p8key), + teamId, keyId)) + .build(); + } + @Override + public void processMessageEvent(MessageEvent messageEvent) { + com.juick.Message jmsg = messageEvent.getMessage(); + List users = messageEvent.getUsers(); + ApnsPayloadBuilder apnsPayloadBuilder = new ApnsPayloadBuilder(); + apnsPayloadBuilder.addCustomProperty("mid", jmsg.getMid()); + apnsPayloadBuilder.addCustomProperty("uname", jmsg.getUser().getName()); + String post = PlainTextFormatter.formatPost(jmsg); + String[] parts = post.split("\n", 2); + apnsPayloadBuilder.setAlertTitle(parts[0]).setAlertBody(parts[1]); + users.forEach( user -> { + apnsPayloadBuilder.setBadgeNumber(user.getUnreadCount()); + String payload = apnsPayloadBuilder.buildWithDefaultMaximumLength(); + user.getTokens().stream().filter(t -> t.getType().equals("apns")) + .map(ExternalToken::getToken).forEach(token -> { + Future> notification = apns.sendNotification( + new SimpleApnsPushNotification(token, topic, payload)); + notification.addListener((PushNotificationResponseListener) future -> { + if (future.isSuccess()) { + processAPNSResponse(token, future.getNow()); + } else { + logger.warn("APNS error ", future.cause()); + } + }); + }); + }); + } + + @Override + public void processSubscribeEvent(SubscribeEvent subscribeEvent) { + + } + + @Override + public void processLikeEvent(LikeEvent likeEvent) { + + } + + @Override + public void processPingEvent(PingEvent pingEvent) { + + } + + @Override + public void processMessageReadEvent(MessageReadEvent messageReadEvent) { + List users = messageReadEvent.getUsers(); + ApnsPayloadBuilder apnsPayloadBuilder = new ApnsPayloadBuilder(); + users.forEach(user -> { + apnsPayloadBuilder.setBadgeNumber(user.getUnreadCount()); + String payload = apnsPayloadBuilder.buildWithDefaultMaximumLength(); + user.getTokens().stream().filter(t -> t.getType().equals("apns")) + .map(ExternalToken::getToken).forEach(token -> { + Future> notification = apns.sendNotification( + new SimpleApnsPushNotification(token, topic, payload)); + notification.addListener((PushNotificationResponseListener) future -> { + if (future.isSuccess()) { + processAPNSResponse(token, future.getNow()); + } else { + logger.warn("APNS error ", future.cause()); + } + }); + }); + }); + } + @PreDestroy + public void close() { + apns.close(); + } + + private void processAPNSResponse(String token, PushNotificationResponse pushNotificationResponse) { + if (pushNotificationResponse.isAccepted()) { + logger.info("APNS accepted: {}", token); + } else { + String reason = pushNotificationResponse.getRejectionReason(); + logger.info("APNS rejected: {}", reason); + if (reason.equals("BadDeviceToken")) { + notificationsManager.getInvalidAPNSTokens().add(token); + } + } + Optional invalidationDate = Optional.ofNullable( + pushNotificationResponse.getTokenInvalidationTimestamp()); + invalidationDate.ifPresent(date -> { + if (date.before(new Date())) { + logger.info("Token invalidated: {}", token); + notificationsManager.getInvalidAPNSTokens().add(token); + } + }); + } +} -- cgit v1.2.3