diff options
author | Yuriy Piskarev <yuriy.piskarev@gmail.com> | 2023-08-24 14:16:17 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-24 14:16:17 +0300 |
commit | ae406c7b49a72de24d81fd74386d9638342c90ee (patch) | |
tree | 6fbcf557375b98e926c78af9c757e62c79d72a1b /src/main/java/org/traccar/notificators/NotificatorFirebase.java | |
parent | 56ff656c908b19feb2fa3dcffa48cc3bcdfe9b3b (diff) | |
parent | 9aeedc90da24848ff97227d6f281eb4d1e1506ef (diff) | |
download | trackermap-server-ae406c7b49a72de24d81fd74386d9638342c90ee.tar.gz trackermap-server-ae406c7b49a72de24d81fd74386d9638342c90ee.tar.bz2 trackermap-server-ae406c7b49a72de24d81fd74386d9638342c90ee.zip |
Merge branch 'traccar:master' into master
Diffstat (limited to 'src/main/java/org/traccar/notificators/NotificatorFirebase.java')
-rw-r--r-- | src/main/java/org/traccar/notificators/NotificatorFirebase.java | 169 |
1 files changed, 106 insertions, 63 deletions
diff --git a/src/main/java/org/traccar/notificators/NotificatorFirebase.java b/src/main/java/org/traccar/notificators/NotificatorFirebase.java index f91ec25a0..be95fb28e 100644 --- a/src/main/java/org/traccar/notificators/NotificatorFirebase.java +++ b/src/main/java/org/traccar/notificators/NotificatorFirebase.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,88 +16,131 @@ */ package org.traccar.notificators; -import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.auth.oauth2.GoogleCredentials; +import com.google.firebase.FirebaseApp; +import com.google.firebase.FirebaseOptions; +import com.google.firebase.messaging.AndroidConfig; +import com.google.firebase.messaging.AndroidNotification; +import com.google.firebase.messaging.ApnsConfig; +import com.google.firebase.messaging.Aps; +import com.google.firebase.messaging.FirebaseMessaging; +import com.google.firebase.messaging.FirebaseMessagingException; +import com.google.firebase.messaging.MessagingErrorCode; +import com.google.firebase.messaging.MulticastMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.traccar.Context; +import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Event; +import org.traccar.model.Notification; import org.traccar.model.Position; import org.traccar.model.User; -import org.traccar.notification.NotificationMessage; +import org.traccar.notification.MessageException; import org.traccar.notification.NotificationFormatter; - -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.InvocationCallback; - -public class NotificatorFirebase extends Notificator { +import org.traccar.session.cache.CacheManager; +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; + +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +@Singleton +public class NotificatorFirebase implements Notificator { private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorFirebase.class); - private final String url; - private final String key; + private final NotificationFormatter notificationFormatter; + private final Storage storage; + private final CacheManager cacheManager; - public static class Notification { - @JsonProperty("title") - private String title; - @JsonProperty("body") - private String body; - @JsonProperty("sound") - private String sound; - } + @Inject + public NotificatorFirebase( + Config config, NotificationFormatter notificationFormatter, + Storage storage, CacheManager cacheManager) throws IOException { - public static class Message { - @JsonProperty("registration_ids") - private String[] tokens; - @JsonProperty("notification") - private Notification notification; - } + this.notificationFormatter = notificationFormatter; + this.storage = storage; + this.cacheManager = cacheManager; - public NotificatorFirebase() { - this( - "https://fcm.googleapis.com/fcm/send", - Context.getConfig().getString(Keys.NOTIFICATOR_FIREBASE_KEY)); - } + InputStream serviceAccount = new ByteArrayInputStream( + config.getString(Keys.NOTIFICATOR_FIREBASE_SERVICE_ACCOUNT).getBytes()); - protected NotificatorFirebase(String url, String key) { - this.url = url; - this.key = key; + FirebaseOptions options = FirebaseOptions.builder() + .setCredentials(GoogleCredentials.fromStream(serviceAccount)) + .build(); + + FirebaseApp.initializeApp(options); } @Override - public void sendSync(long userId, Event event, Position position) { - final User user = Context.getPermissionsManager().getUser(userId); - if (user.getAttributes().containsKey("notificationTokens")) { - - NotificationMessage shortMessage = NotificationFormatter.formatMessage(userId, event, position, "short"); - - Notification notification = new Notification(); - notification.title = shortMessage.getSubject(); - notification.body = shortMessage.getBody(); - notification.sound = "default"; - - Message message = new Message(); - message.tokens = user.getString("notificationTokens").split("[, ]"); - message.notification = notification; - - Context.getClient().target(url).request() - .header("Authorization", "key=" + key) - .async().post(Entity.json(message), new InvocationCallback<Object>() { - @Override - public void completed(Object o) { + public void send(Notification notification, User user, Event event, Position position) throws MessageException { + if (user.hasAttribute("notificationTokens")) { + + var shortMessage = notificationFormatter.formatMessage(user, event, position, "short"); + + List<String> registrationTokens = new ArrayList<>( + Arrays.asList(user.getString("notificationTokens").split("[, ]"))); + + MulticastMessage message = MulticastMessage.builder() + .setNotification(com.google.firebase.messaging.Notification.builder() + .setTitle(shortMessage.getSubject()) + .setBody(shortMessage.getBody()) + .build()) + .setAndroidConfig(AndroidConfig.builder() + .setNotification(AndroidNotification.builder() + .setSound("default") + .build()) + .build()) + .setApnsConfig(ApnsConfig.builder() + .setAps(Aps.builder() + .setSound("default") + .build()) + .build()) + .addAllTokens(registrationTokens) + .putData("eventId", String.valueOf(event.getId())) + .build(); + + try { + var result = FirebaseMessaging.getInstance().sendMulticast(message); + List<String> failedTokens = new LinkedList<>(); + var iterator = result.getResponses().listIterator(); + while (iterator.hasNext()) { + int index = iterator.nextIndex(); + var response = iterator.next(); + if (!response.isSuccessful()) { + MessagingErrorCode error = response.getException().getMessagingErrorCode(); + if (error == MessagingErrorCode.INVALID_ARGUMENT || error == MessagingErrorCode.UNREGISTERED) { + failedTokens.add(registrationTokens.get(index)); + } + LOGGER.warn("Firebase user {} error", user.getId(), response.getException()); + } } - - @Override - public void failed(Throwable throwable) { - LOGGER.warn("Firebase notification error", throwable); + if (!failedTokens.isEmpty()) { + registrationTokens.removeAll(failedTokens); + if (registrationTokens.isEmpty()) { + user.getAttributes().remove("notificationTokens"); + } else { + user.set("notificationTokens", String.join(",", registrationTokens)); + } + storage.updateObject(user, new Request( + new Columns.Include("attributes"), + new Condition.Equals("id", user.getId()))); + cacheManager.updateOrInvalidate(true, user); } - }); + } catch (FirebaseMessagingException | StorageException e) { + LOGGER.warn("Firebase error", e); + } } } - @Override - public void sendAsync(long userId, Event event, Position position) { - sendSync(userId, event, position); - } - } |