package gov.nysenate.openleg.service.notification.dispatch;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Multimap;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import gov.nysenate.openleg.config.Environment;
import gov.nysenate.openleg.model.notification.*;
import gov.nysenate.openleg.service.notification.data.NotificationService;
import gov.nysenate.openleg.service.notification.subscription.NotificationSubscriptionDataService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class NotificationDispatcher {
private static final Logger logger = LoggerFactory.getLogger(NotificationDispatcher.class);
@Autowired
private EventBus eventBus;
@Autowired
private Environment environment;
@Autowired
private NotificationService notificationService;
@Autowired
private NotificationSubscriptionDataService subscriptionDataService;
@Autowired
private List<NotificationSender> notificationSenders;
private ImmutableMap<NotificationTarget, NotificationSender> senderMap;
@PostConstruct
public void init() {
Map<NotificationTarget, NotificationSender> senderProtoMap = new HashMap<>();
notificationSenders.forEach(sender -> senderProtoMap.put(sender.getTargetType(), sender));
senderMap = ImmutableMap.copyOf(senderProtoMap);
eventBus.register(this);
}
/**
* Sends a registered notification to all pertinent subscribers
* @param notification NotificationBody
*/
@Async
public void dispatchNotification(RegisteredNotification notification) {
if (!environment.isNotificationsEnabled()) {
return;
}
try {
Multimap<NotificationTarget, NotificationSubscription> subscriptionMap = ArrayListMultimap.create();
subscriptionDataService.getSubscriptions(notification.getType())
.forEach(subscription -> addSubscription(subscriptionMap, subscription));
subscriptionMap.keySet().forEach(target ->
senderMap.get(target).sendNotification(notification, subscriptionMap.get(target)));
} catch (Throwable ex) {
handleNotificationException(ex);
}
}
@Subscribe
public void handleNotificationEvent(Notification notification) {
try {
RegisteredNotification registeredNotification = notificationService.registerNotification(notification);
dispatchNotification(registeredNotification);
} catch (Throwable ex) {
handleNotificationException(ex);
}
}
/* --- Internal Methods --- */
/**
* Adds a subscription to the specified multimap
* if the map does not already contain a subscription with the same address and same target
* ensuring that a person won't get the same notification multiple times
*/
private void addSubscription(Multimap<NotificationTarget, NotificationSubscription> subMap,
NotificationSubscription subscription) {
for (NotificationSubscription existingSub : subMap.get(subscription.getTarget())) {
if(StringUtils.equals(existingSub.getTargetAddress(), subscription.getTargetAddress())) {
return;
}
}
subMap.put(subscription.getTarget(), subscription);
}
/**
* Handle an exception by logging it
* All notification related exceptions should be caught by this method
* To prevent an infinite feedback loop
*
* @param ex Throwable
*/
private static void handleNotificationException(Throwable ex) {
logger.error("Caught exception while dispatching notification!", ex);
}
}