package com.juick.components; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutureCallback; import com.google.api.core.ApiFutures; import com.google.auth.oauth2.GoogleCredentials; import com.google.firebase.FirebaseApp; import com.google.firebase.FirebaseOptions; import com.google.firebase.messaging.FirebaseMessaging; import com.google.firebase.messaging.FirebaseMessagingException; import com.google.firebase.messaging.Message; import com.juick.ExternalToken; import com.juick.User; import com.juick.service.component.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.task.TaskExecutor; import javax.annotation.PostConstruct; import javax.inject.Inject; import java.io.IOException; import java.util.List; import java.util.stream.Collectors; public class FirebaseManager implements NotificationListener { private static Logger logger = LoggerFactory.getLogger(FirebaseManager.class); @Inject ObjectMapper jsonMapper; @Value("${fcm_database_url:}") private String fcmDatabaseUrl; @Inject private NotificationsManager notificationsManager; @Inject private TaskExecutor taskExecutor; @PostConstruct public void initialize() throws IOException { Resource serviceAccount = new ClassPathResource("serviceAccount.json"); FirebaseOptions options = new FirebaseOptions.Builder() .setCredentials(GoogleCredentials.fromStream(serviceAccount.getInputStream())) .setDatabaseUrl(fcmDatabaseUrl) .build(); FirebaseApp.initializeApp(options); } @Override public void processMessageEvent(MessageEvent messageEvent) { com.juick.Message jmsg = messageEvent.getMessage(); List users = messageEvent.getUsers(); // GCM List regids = users.stream().flatMap(u -> u.getTokens().stream()).filter(d -> d.getType().equals("gcm")) .map(ExternalToken::getToken).collect(Collectors.toList()); if (!regids.isEmpty()) { String json = null; try { json = jsonMapper.writeValueAsString(jmsg); } catch (JsonProcessingException e) { logger.warn("JSON exception", e); } logger.info(json); Message.Builder messageBuilder = Message.builder() .putData("message", json); regids.forEach(token -> { messageBuilder.setToken(token); ApiFuture response = FirebaseMessaging.getInstance().sendAsync(messageBuilder.build()); ApiFutures.addCallback(response, new ApiFutureCallback() { @Override public void onFailure(Throwable t) { if (t instanceof FirebaseMessagingException) { FirebaseMessagingException e = (FirebaseMessagingException) t; logger.warn("FirebaseMessaging error: {}", e.getErrorCode()); if (e.getErrorCode().equals("invalid-argument") || e.getErrorCode().equals("registration-token-not-registered") || e.getErrorCode().equals("invalid-registration-token")) { // invalid token logger.info("{} is scheduled to remove", token); notificationsManager.addInvalidGCMToken(token); } else { logger.warn("Unhandled FirebaseMessaging exception", t); } } else { logger.warn("Unhandled FCM exception", t); } } @Override public void onSuccess(String result) { logger.info("Successfully sent message: " + result); } }, taskExecutor); }); } else { logger.info("GMS: no recipients"); } } @Override public void processSubscribeEvent(SubscribeEvent subscribeEvent) { } @Override public void processLikeEvent(LikeEvent likeEvent) { } @Override public void processPingEvent(PingEvent pingEvent) { } @Override public void processMessageReadEvent(MessageReadEvent messageReadEvent) { } @Override public void processTopEvent(TopEvent topEvent) { } }